feat: Refactor staff profile page and logout button for improved state management and navigation

This commit is contained in:
Achintha Isuru
2026-02-19 13:39:03 -05:00
parent 55344fad90
commit 7b9507b87f
4 changed files with 161 additions and 153 deletions

View File

@@ -38,24 +38,12 @@ class StaffProfilePage extends StatelessWidget {
} }
} }
void _onSignOut(ProfileCubit cubit, ProfileState state) {
if (state.status != ProfileStatus.loading) {
cubit.signOut();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ProfileCubit cubit = Modular.get<ProfileCubit>();
// Load profile data on first build
if (cubit.state.status == ProfileStatus.initial) {
cubit.loadProfile();
}
return Scaffold( return Scaffold(
body: BlocConsumer<ProfileCubit, ProfileState>( body: BlocProvider<ProfileCubit>(
bloc: cubit, create: (_) => Modular.get<ProfileCubit>()..loadProfile(),
child: BlocConsumer<ProfileCubit, ProfileState>(
listener: (BuildContext context, ProfileState state) { listener: (BuildContext context, ProfileState state) {
if (state.status == ProfileStatus.signedOut) { if (state.status == ProfileStatus.signedOut) {
Modular.to.toGetStartedPage(); Modular.to.toGetStartedPage();
@@ -104,7 +92,6 @@ class StaffProfilePage extends StatelessWidget {
fullName: profile.name, fullName: profile.name,
level: _mapStatusToLevel(profile.status), level: _mapStatusToLevel(profile.status),
photoUrl: profile.avatar, photoUrl: profile.avatar,
onSignOutTap: () => _onSignOut(cubit, state),
), ),
Transform.translate( Transform.translate(
offset: const Offset(0, -UiConstants.space6), offset: const Offset(0, -UiConstants.space6),
@@ -113,7 +100,9 @@ class StaffProfilePage extends StatelessWidget {
horizontal: UiConstants.space5, horizontal: UiConstants.space5,
), ),
child: Column( child: Column(
spacing: UiConstants.space6,
children: <Widget>[ children: <Widget>[
// Reliability Stats and Score
ReliabilityStatsCard( ReliabilityStatsCard(
totalShifts: profile.totalShifts, totalShifts: profile.totalShifts,
averageRating: profile.averageRating, averageRating: profile.averageRating,
@@ -121,25 +110,31 @@ class StaffProfilePage extends StatelessWidget {
noShowCount: profile.noShowCount, noShowCount: profile.noShowCount,
cancellationCount: profile.cancellationCount, cancellationCount: profile.cancellationCount,
), ),
const SizedBox(height: UiConstants.space6),
// Reliability Score Bar
ReliabilityScoreBar( ReliabilityScoreBar(
reliabilityScore: profile.reliabilityScore, reliabilityScore: profile.reliabilityScore,
), ),
const SizedBox(height: UiConstants.space6),
// Ordered sections
const OnboardingSection(), const OnboardingSection(),
const SizedBox(height: UiConstants.space6),
// Compliance section
const ComplianceSection(), const ComplianceSection(),
const SizedBox(height: UiConstants.space6),
// Finance section
const FinanceSection(), const FinanceSection(),
const SizedBox(height: UiConstants.space6),
// Support section
const SupportSection(), const SupportSection(),
const SizedBox(height: UiConstants.space6),
// Settings section
const SettingsSection(), const SettingsSection(),
// Logout button at the bottom
const LogoutButton(),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
LogoutButton(
onTap: () => _onSignOut(cubit, state),
),
const SizedBox(height: UiConstants.space12),
], ],
), ),
), ),
@@ -149,6 +144,7 @@ class StaffProfilePage extends StatelessWidget {
); );
}, },
), ),
),
); );
} }
} }

View File

@@ -1,20 +1,40 @@
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../blocs/profile_cubit.dart';
import '../blocs/profile_state.dart';
/// The sign-out button widget. /// The sign-out button widget.
/// ///
/// Uses design system tokens for all colors, typography, spacing, and icons. /// Uses design system tokens for all colors, typography, spacing, and icons.
/// Handles logout logic when tapped and navigates to onboarding on success.
class LogoutButton extends StatelessWidget { class LogoutButton extends StatelessWidget {
final VoidCallback onTap; const LogoutButton({super.key});
const LogoutButton({super.key, required this.onTap}); /// Handles the sign-out action.
///
/// Checks if the profile is not currently loading, then triggers the
/// sign-out process via the ProfileCubit.
void _handleSignOut(BuildContext context, ProfileState state) {
if (state.status != ProfileStatus.loading) {
context.read<ProfileCubit>().signOut();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final i18n = t.staff.profile.header; final TranslationsStaffProfileHeaderEn i18n = t.staff.profile.header;
return Container( return BlocListener<ProfileCubit, ProfileState>(
listener: (BuildContext context, ProfileState state) {
if (state.status == ProfileStatus.signedOut) {
// Navigate to get started page after successful sign-out
// This will be handled by the profile page listener
}
},
child: Container(
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: UiColors.bgPopup, color: UiColors.bgPopup,
@@ -24,13 +44,18 @@ class LogoutButton extends StatelessWidget {
child: Material( child: Material(
color: UiColors.transparent, color: UiColors.transparent,
child: InkWell( child: InkWell(
onTap: onTap, onTap: () {
_handleSignOut(
context,
context.read<ProfileCubit>().state,
);
},
borderRadius: UiConstants.radiusLg, borderRadius: UiConstants.radiusLg,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4), padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: <Widget>[
const Icon( const Icon(
UiIcons.logOut, UiIcons.logOut,
color: UiColors.destructive, color: UiColors.destructive,
@@ -46,6 +71,7 @@ class LogoutButton extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }

View File

@@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
/// The header section of the staff profile page, containing avatar, name, level, /// The header section of the staff profile page, containing avatar, name, and level.
/// and a sign-out button.
/// ///
/// Uses design system tokens for all colors, typography, and spacing. /// Uses design system tokens for all colors, typography, and spacing.
class ProfileHeader extends StatelessWidget { class ProfileHeader extends StatelessWidget {
@@ -16,21 +15,17 @@ class ProfileHeader extends StatelessWidget {
/// Optional photo URL for the avatar /// Optional photo URL for the avatar
final String? photoUrl; final String? photoUrl;
/// Callback when sign out is tapped
final VoidCallback onSignOutTap;
/// Creates a [ProfileHeader]. /// Creates a [ProfileHeader].
const ProfileHeader({ const ProfileHeader({
super.key, super.key,
required this.fullName, required this.fullName,
required this.level, required this.level,
this.photoUrl, this.photoUrl,
required this.onSignOutTap,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final i18n = t.staff.profile.header; final TranslationsStaffProfileHeaderEn i18n = t.staff.profile.header;
return Container( return Container(
width: double.infinity, width: double.infinity,
@@ -49,31 +44,22 @@ class ProfileHeader extends StatelessWidget {
child: SafeArea( child: SafeArea(
bottom: false, bottom: false,
child: Column( child: Column(
children: [ children: <Widget>[
// Top Bar // Top Bar
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: <Widget>[
Text( Text(
i18n.title, i18n.title,
style: UiTypography.headline4m.textSecondary, style: UiTypography.headline4m.textSecondary,
), ),
GestureDetector(
onTap: onSignOutTap,
child: Text(
i18n.sign_out,
style: UiTypography.body2m.copyWith(
color: UiColors.primaryForeground.withValues(alpha: 0.8),
),
),
),
], ],
), ),
const SizedBox(height: UiConstants.space8), const SizedBox(height: UiConstants.space8),
// Avatar Section // Avatar Section
Stack( Stack(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
children: [ children: <Widget>[
Container( Container(
width: 112, width: 112,
height: 112, height: 112,
@@ -83,13 +69,13 @@ class ProfileHeader extends StatelessWidget {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: <Color>[
UiColors.accent, UiColors.accent,
UiColors.accent.withValues(alpha: 0.5), UiColors.accent.withValues(alpha: 0.5),
UiColors.primaryForeground, UiColors.primaryForeground,
], ],
), ),
boxShadow: [ boxShadow: <BoxShadow>[
BoxShadow( BoxShadow(
color: UiColors.foreground.withValues(alpha: 0.2), color: UiColors.foreground.withValues(alpha: 0.2),
blurRadius: 10, blurRadius: 10,
@@ -119,7 +105,7 @@ class ProfileHeader extends StatelessWidget {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: <Color>[
UiColors.accent, UiColors.accent,
UiColors.accent.withValues(alpha: 0.7), UiColors.accent.withValues(alpha: 0.7),
], ],
@@ -144,7 +130,7 @@ class ProfileHeader extends StatelessWidget {
color: UiColors.primaryForeground, color: UiColors.primaryForeground,
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(color: UiColors.primary, width: 2), border: Border.all(color: UiColors.primary, width: 2),
boxShadow: [ boxShadow: <BoxShadow>[
BoxShadow( BoxShadow(
color: UiColors.foreground.withValues(alpha: 0.1), color: UiColors.foreground.withValues(alpha: 0.1),
blurRadius: 4, blurRadius: 4,

View File

@@ -68,7 +68,7 @@ graph TD
- `data/`: Repository Implementations. - `data/`: Repository Implementations.
- `presentation/`: - `presentation/`:
- Pages, BLoCs, Widgets. - Pages, BLoCs, Widgets.
- For performance make the pages as `StatelessWidget` and move the state management to the BLoC or `StatefulWidget` to an external separate widget file. - For performance make the pages as `StatelessWidget` and move the state management to the BLoC (always use a BlocProvider when providing the BLoC to the widget tree) or `StatefulWidget` to an external separate widget file.
- **Responsibilities**: - **Responsibilities**:
- **Presentation**: UI Pages, Modular Routes. - **Presentation**: UI Pages, Modular Routes.
- **State Management**: BLoCs / Cubits. - **State Management**: BLoCs / Cubits.