refactor: enhance navigation robustness by introducing popSafe and safePushNamedAndRemoveUntil methods and updating their usage.

This commit is contained in:
Achintha Isuru
2026-02-28 17:23:53 -05:00
parent c26128f1f2
commit 53b612851c
24 changed files with 481 additions and 334 deletions

View File

@@ -135,7 +135,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
leading: const Icon(Icons.photo_library),
title: Text(t.common.gallery),
onTap: () {
Modular.to.pop();
Modular.to.popSafe();
_onGallery(context);
},
),
@@ -143,7 +143,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
leading: const Icon(Icons.camera_alt),
title: Text(t.common.camera),
onTap: () {
Modular.to.pop();
Modular.to.popSafe();
_onCamera(context);
},
),
@@ -215,10 +215,16 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
String _getStatusText(bool hasUploadedPhoto) {
return switch (widget.item.verificationStatus) {
AttireVerificationStatus.approved => t.staff_profile_attire.capture.approved,
AttireVerificationStatus.rejected => t.staff_profile_attire.capture.rejected,
AttireVerificationStatus.pending => t.staff_profile_attire.capture.pending_verification,
_ => hasUploadedPhoto ? t.staff_profile_attire.capture.pending_verification : t.staff_profile_attire.capture.not_uploaded,
AttireVerificationStatus.approved =>
t.staff_profile_attire.capture.approved,
AttireVerificationStatus.rejected =>
t.staff_profile_attire.capture.rejected,
AttireVerificationStatus.pending =>
t.staff_profile_attire.capture.pending_verification,
_ =>
hasUploadedPhoto
? t.staff_profile_attire.capture.pending_verification
: t.staff_profile_attire.capture.not_uploaded,
};
}
@@ -281,7 +287,9 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
child: Column(
children: <Widget>[
_FileTypesBanner(
message: t.staff_profile_attire.upload_file_types_banner,
message: t
.staff_profile_attire
.upload_file_types_banner,
),
const SizedBox(height: UiConstants.space4),
ImagePreviewSection(
@@ -350,17 +358,10 @@ class _FileTypesBanner extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(
UiIcons.info,
size: 20,
color: UiColors.primary,
),
Icon(UiIcons.info, size: 20, color: UiColors.primary),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Text(
message,
style: UiTypography.body2r.textSecondary,
),
child: Text(message, style: UiTypography.body2r.textSecondary),
),
],
),

View File

@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:krow_core/core.dart';
import 'attire_upload_buttons.dart';
@@ -98,7 +99,7 @@ class FooterSection extends StatelessWidget {
text: 'Submit Image',
onPressed: () {
if (updatedItem != null) {
Modular.to.pop(updatedItem);
Modular.to.popSafe(updatedItem);
}
},
),

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import '../blocs/emergency_contact_bloc.dart';
import '../widgets/emergency_contact_add_button.dart';
import '../widgets/emergency_contact_form_item.dart';
@@ -25,7 +26,7 @@ class EmergencyContactScreen extends StatelessWidget {
elevation: 0,
leading: IconButton(
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
onPressed: () => Modular.to.pop(),
onPressed: () => Modular.to.popSafe(),
),
title: Text(
'Emergency Contact',

View File

@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
/// Language selection page for staff profile.
///
@@ -30,7 +31,7 @@ class LanguageSelectionPage extends StatelessWidget {
);
Modular.to
.pop(); // Close the language selection page after showing the snackbar
.popSafe(); // Close the language selection page after showing the snackbar
}
@override

View File

@@ -3,12 +3,12 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import '../blocs/personal_info_bloc.dart';
import '../blocs/personal_info_state.dart';
import '../widgets/personal_info_content.dart';
/// The Personal Info page for staff onboarding.
///
/// This page allows staff members to view and edit their personal information
@@ -22,7 +22,9 @@ class PersonalInfoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final TranslationsStaffOnboardingPersonalInfoEn i18n = Translations.of(context).staff.onboarding.personal_info;
final TranslationsStaffOnboardingPersonalInfoEn i18n = Translations.of(
context,
).staff.onboarding.personal_info;
return BlocProvider<PersonalInfoBloc>(
create: (BuildContext context) => Modular.get<PersonalInfoBloc>(),
child: BlocListener<PersonalInfoBloc, PersonalInfoState>(
@@ -33,7 +35,7 @@ class PersonalInfoPage extends StatelessWidget {
message: i18n.save_success,
type: UiSnackbarType.success,
);
Modular.to.pop();
Modular.to.popSafe();
} else if (state.status == PersonalInfoStatus.error) {
UiSnackbar.show(
context,
@@ -54,19 +56,13 @@ class PersonalInfoPage extends StatelessWidget {
UiIcons.chevronLeft,
color: UiColors.textSecondary,
),
onPressed: () => Modular.to.pop(),
onPressed: () => Modular.to.popSafe(),
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
),
title: Text(
i18n.title,
style: UiTypography.title1m.textPrimary,
),
title: Text(i18n.title, style: UiTypography.title1m.textPrimary),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(
color: UiColors.border,
height: 1.0,
),
child: Container(color: UiColors.border, height: 1.0),
),
),
body: SafeArea(
@@ -74,9 +70,7 @@ class PersonalInfoPage extends StatelessWidget {
builder: (BuildContext context, PersonalInfoState state) {
if (state.status == PersonalInfoStatus.loading ||
state.status == PersonalInfoStatus.initial) {
return const Center(
child: CircularProgressIndicator(),
);
return const Center(child: CircularProgressIndicator());
}
if (state.staff == null) {

View File

@@ -11,7 +11,6 @@ import 'package:krow_core/core.dart';
/// The Preferred Locations row navigates to a dedicated Uber-style page.
/// Uses only design system tokens for colors, typography, and spacing.
class PersonalInfoForm extends StatelessWidget {
/// Creates a [PersonalInfoForm].
const PersonalInfoForm({
super.key,
@@ -22,6 +21,7 @@ class PersonalInfoForm extends StatelessWidget {
required this.currentLocations,
this.enabled = true,
});
/// The staff member's full name (read-only).
final String fullName;
@@ -42,7 +42,8 @@ class PersonalInfoForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
final TranslationsStaffOnboardingPersonalInfoEn i18n = t.staff.onboarding.personal_info;
final TranslationsStaffOnboardingPersonalInfoEn i18n =
t.staff.onboarding.personal_info;
final String locationSummary = currentLocations.isEmpty
? i18n.locations_summary_none
: currentLocations.join(', ');
@@ -84,17 +85,13 @@ class PersonalInfoForm extends StatelessWidget {
hint: i18n.locations_hint,
icon: UiIcons.mapPin,
enabled: enabled,
onTap: enabled
? () => Modular.to.pushNamed(StaffPaths.preferredLocations)
: null,
onTap: enabled ? () => Modular.to.toPreferredLocations() : null,
),
const SizedBox(height: UiConstants.space4),
const _FieldLabel(text: 'Language'),
const SizedBox(height: UiConstants.space2),
_LanguageSelector(
enabled: enabled,
),
_LanguageSelector(enabled: enabled),
],
);
}
@@ -132,7 +129,9 @@ class _TappableRow extends StatelessWidget {
color: enabled ? UiColors.bgPopup : UiColors.bgSecondary,
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all(
color: enabled ? UiColors.border : UiColors.border.withValues(alpha: 0.5),
color: enabled
? UiColors.border
: UiColors.border.withValues(alpha: 0.5),
),
),
child: Row(
@@ -164,9 +163,7 @@ class _TappableRow extends StatelessWidget {
/// A language selector widget that displays the current language and navigates to language selection page.
class _LanguageSelector extends StatelessWidget {
const _LanguageSelector({
this.enabled = true,
});
const _LanguageSelector({this.enabled = true});
final bool enabled;
@@ -176,9 +173,7 @@ class _LanguageSelector extends StatelessWidget {
final String languageName = currentLocale == 'es' ? 'Español' : 'English';
return GestureDetector(
onTap: enabled
? () => Modular.to.pushNamed(StaffPaths.languageSelection)
: null,
onTap: enabled ? () => Modular.to.toLanguageSelection() : null,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
@@ -188,18 +183,21 @@ class _LanguageSelector extends StatelessWidget {
color: enabled ? UiColors.bgPopup : UiColors.bgSecondary,
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all(
color: enabled ? UiColors.border : UiColors.border.withValues(alpha: 0.5),
color: enabled
? UiColors.border
: UiColors.border.withValues(alpha: 0.5),
),
),
child: Row(
children: <Widget>[
const Icon(UiIcons.settings, size: 18, color: UiColors.iconSecondary),
const Icon(
UiIcons.settings,
size: 18,
color: UiColors.iconSecondary,
),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Text(
languageName,
style: UiTypography.body2r.textPrimary,
),
child: Text(languageName, style: UiTypography.body2r.textPrimary),
),
if (enabled)
const Icon(
@@ -220,10 +218,7 @@ class _FieldLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
text,
style: UiTypography.titleUppercase3m.textSecondary,
);
return Text(text, style: UiTypography.titleUppercase3m.textSecondary);
}
}
@@ -244,10 +239,7 @@ class _ReadOnlyField extends StatelessWidget {
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
border: Border.all(color: UiColors.border),
),
child: Text(
value,
style: UiTypography.body2r.textInactive,
),
child: Text(value, style: UiTypography.body2r.textInactive),
);
}
}