From c048fdb2409d8e7456425aaaac7e027989d76ccb Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Sun, 28 Jun 2026 14:46:01 +0900 Subject: [PATCH 1/2] fix: show error modal instead of snackbar --- .../my_page_modal/delete_user_modal.dart | 17 +++- .../preparation_spare_time_edit_screen.dart | 14 ++- .../onboarding/screens/onboarding_screen.dart | 86 ++++++++++--------- .../components/schedule_multi_page_form.dart | 14 ++- ...eparation_spare_time_edit_screen_test.dart | 3 +- .../schedule_multi_page_form_test.dart | 3 +- 6 files changed, 85 insertions(+), 52 deletions(-) diff --git a/lib/presentation/my_page/my_page_modal/delete_user_modal.dart b/lib/presentation/my_page/my_page_modal/delete_user_modal.dart index 53a339bb..44c2a688 100644 --- a/lib/presentation/my_page/my_page_modal/delete_user_modal.dart +++ b/lib/presentation/my_page/my_page_modal/delete_user_modal.dart @@ -8,6 +8,7 @@ import 'package:on_time_front/domain/use-cases/delete_user_use_case.dart'; import 'package:on_time_front/l10n/app_localizations.dart'; import 'package:on_time_front/presentation/shared/components/custom_alert_dialog.dart'; import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; import 'package:on_time_front/presentation/shared/components/two_button_delete_dialog.dart'; class DeleteUserModal { @@ -259,9 +260,21 @@ class _DeleteFeedbackDialogState extends State<_DeleteFeedbackDialog> { return; } - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(AppLocalizations.of(context)!.error)), + final l10n = AppLocalizations.of(context)!; + await showTwoActionDialog( + context, + config: TwoActionDialogConfig( + title: l10n.error, + primaryAction: DialogActionConfig( + label: l10n.ok, + variant: ModalWideButtonVariant.destructive, + ), + ), ); + if (!mounted) { + return; + } + setState(() { _isDeleting = false; }); diff --git a/lib/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen.dart b/lib/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen.dart index ae1fcd8a..6cea983b 100644 --- a/lib/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen.dart +++ b/lib/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen.dart @@ -7,7 +7,9 @@ import 'package:on_time_front/presentation/my_page/preparation_spare_time_edit/b import 'package:on_time_front/l10n/app_localizations.dart'; import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/bloc/preparation_form_bloc.dart'; import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/components/preparation_form_create_list.dart'; +import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart'; import 'package:on_time_front/presentation/shared/components/time_stepper.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; class PreparationSpareTimeEditScreen extends StatelessWidget { const PreparationSpareTimeEditScreen({super.key}); @@ -51,10 +53,14 @@ class _PreparationSpareTimeEditView extends StatelessWidget { Navigator.of(context).pop(); } else if (state.status == DefaultPreparationSpareTimeStatus.error) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - state.errorMessage ?? AppLocalizations.of(context)!.error, + final l10n = AppLocalizations.of(context)!; + showTwoActionDialog( + context, + config: TwoActionDialogConfig( + title: state.errorMessage ?? l10n.error, + primaryAction: DialogActionConfig( + label: l10n.ok, + variant: ModalWideButtonVariant.destructive, ), ), ); diff --git a/lib/presentation/onboarding/screens/onboarding_screen.dart b/lib/presentation/onboarding/screens/onboarding_screen.dart index 46d773da..2d95b782 100644 --- a/lib/presentation/onboarding/screens/onboarding_screen.dart +++ b/lib/presentation/onboarding/screens/onboarding_screen.dart @@ -12,7 +12,9 @@ import 'package:on_time_front/presentation/onboarding/schedule_spare_time/cubit/ import 'package:on_time_front/presentation/onboarding/schedule_spare_time/screens/schedule_spare_time_form.dart'; import 'package:on_time_front/presentation/onboarding/cubit/onboarding_cubit.dart'; import 'package:on_time_front/presentation/onboarding/preparation_order/cubit/preparation_order_cubit.dart'; +import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart'; import 'package:on_time_front/presentation/shared/components/step_progress.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; import 'package:on_time_front/l10n/app_localizations.dart'; class OnboardingScreen extends StatelessWidget { @@ -22,10 +24,7 @@ class OnboardingScreen extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => getIt.get(), - child: Scaffold( - resizeToAvoidBottomInset: false, - body: _OnboardingForm(), - ), + child: Scaffold(resizeToAvoidBottomInset: false, body: _OnboardingForm()), ); } } @@ -91,47 +90,50 @@ class _OnboardingFormState extends State<_OnboardingForm> ), ), ], - child: Builder(builder: (context) { - return Column( - children: [ - _AppBar( - tabController: _tabController, - onUpdateCurrentPageIndex: _updateCurrentPageIndex, - ), - Expanded( - child: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _pageViewController, - onPageChanged: _handlePageViewChanged, - children: [ - PreparationNameForm(), - PreparationOrderForm(), - PreparationTimeForm(), - ScheduleSpareTimeForm(), - ], + child: Builder( + builder: (context) { + return Column( + children: [ + _AppBar( + tabController: _tabController, + onUpdateCurrentPageIndex: _updateCurrentPageIndex, + ), + Expanded( + child: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _pageViewController, + onPageChanged: _handlePageViewChanged, + children: [ + PreparationNameForm(), + PreparationOrderForm(), + PreparationTimeForm(), + ScheduleSpareTimeForm(), + ], + ), ), - ), - SizedBox( + SizedBox( height: 58, width: double.infinity, child: ElevatedButton( - onPressed: !_isSubmitting && - context.select((OnboardingCubit cubit) => - cubit.state.isValid) + onPressed: + !_isSubmitting && + context.select( + (OnboardingCubit cubit) => cubit.state.isValid, + ) ? () => _onNextPageButtonClicked(context) : null, child: _isSubmitting ? const SizedBox.square( dimension: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), + child: CircularProgressIndicator(strokeWidth: 2), ) : Text(AppLocalizations.of(context)!.next), - )), - ], - ); - }), + ), + ), + ], + ); + }, + ), ), ), ); @@ -162,9 +164,15 @@ class _OnboardingFormState extends State<_OnboardingForm> await context.read().onboardingFormSubmitted(); } catch (_) { if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(AppLocalizations.of(context)!.error), + final l10n = AppLocalizations.of(context)!; + await showTwoActionDialog( + context, + config: TwoActionDialogConfig( + title: l10n.error, + primaryAction: DialogActionConfig( + label: l10n.ok, + variant: ModalWideButtonVariant.destructive, + ), ), ); } finally { @@ -242,9 +250,7 @@ class _AppBar extends StatelessWidget { ), ), ), - SizedBox( - width: iconButtonSize, - ) + SizedBox(width: iconButtonSize), ], ); } diff --git a/lib/presentation/schedule_create/components/schedule_multi_page_form.dart b/lib/presentation/schedule_create/components/schedule_multi_page_form.dart index 2cbf5932..932ac65f 100644 --- a/lib/presentation/schedule_create/components/schedule_multi_page_form.dart +++ b/lib/presentation/schedule_create/components/schedule_multi_page_form.dart @@ -11,7 +11,9 @@ import 'package:on_time_front/presentation/schedule_create/schedule_place_moving import 'package:on_time_front/presentation/schedule_create/schedule_place_moving_time.dart/screens/schedule_place_moving_time_form.dart'; import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/cubit/schedule_form_spare_time_cubit.dart'; import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/screens/schedule_spare_and_preparing_time_form.dart'; +import 'package:on_time_front/presentation/shared/components/modal_wide_button.dart'; import 'package:on_time_front/presentation/shared/components/step_progress.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; import 'package:on_time_front/l10n/app_localizations.dart'; class ScheduleMultiPageForm extends StatefulWidget { @@ -56,10 +58,14 @@ class _ScheduleMultiPageFormState extends State Navigator.of(context).pop(true); } else if (state.submissionStatus == ScheduleFormSubmissionStatus.failure) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - state.submissionError ?? AppLocalizations.of(context)!.error, + final l10n = AppLocalizations.of(context)!; + showTwoActionDialog( + context, + config: TwoActionDialogConfig( + title: state.submissionError ?? l10n.error, + primaryAction: DialogActionConfig( + label: l10n.ok, + variant: ModalWideButtonVariant.destructive, ), ), ); diff --git a/test/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen_test.dart b/test/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen_test.dart index c42c2ad1..296a5c9b 100644 --- a/test/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen_test.dart +++ b/test/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen_test.dart @@ -17,6 +17,7 @@ import 'package:on_time_front/presentation/app/bloc/auth/auth_bloc.dart'; import 'package:on_time_front/presentation/my_page/preparation_spare_time_edit/bloc/default_preparation_spare_time_form_bloc.dart'; import 'package:on_time_front/presentation/my_page/preparation_spare_time_edit/preparation_spare_time_edit_screen.dart'; import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/bloc/preparation_form_bloc.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; import 'package:on_time_front/presentation/shared/theme/theme.dart'; void main() { @@ -85,7 +86,7 @@ void main() { await tester.pumpAndSettle(); expect(find.byType(PreparationSpareTimeEditScreen), findsOneWidget); - expect(find.byType(SnackBar), findsOneWidget); + expect(find.byType(TwoActionDialog), findsOneWidget); expect(find.textContaining('save failed'), findsOneWidget); }); diff --git a/test/presentation/schedule_create/components/schedule_multi_page_form_test.dart b/test/presentation/schedule_create/components/schedule_multi_page_form_test.dart index f55e1fe9..d12a5618 100644 --- a/test/presentation/schedule_create/components/schedule_multi_page_form_test.dart +++ b/test/presentation/schedule_create/components/schedule_multi_page_form_test.dart @@ -33,6 +33,7 @@ import 'package:on_time_front/presentation/schedule_create/components/top_bar.da import 'package:on_time_front/presentation/schedule_create/schedule_date_time/cubit/schedule_date_time_cubit.dart'; import 'package:on_time_front/presentation/schedule_create/screens/schedule_create_screen.dart'; import 'package:on_time_front/presentation/schedule_create/screens/schedule_edit_screen.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; class StubAuthBloc extends Mock implements AuthBloc { StubAuthBloc(this._state); @@ -471,7 +472,7 @@ void main() { await tester.pumpAndSettle(); expect(find.byType(ScheduleMultiPageForm), findsOneWidget); - expect(find.byType(SnackBar), findsOneWidget); + expect(find.byType(TwoActionDialog), findsOneWidget); }); testWidgets('edited name and place are submitted from form steps', ( From 6ef1fc1e30747831482b61ff8188547487f80ae5 Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Sun, 28 Jun 2026 21:34:01 +0900 Subject: [PATCH 2/2] test: update delete error modal expectation --- test/presentation/my_page/delete_user_modal_test.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/presentation/my_page/delete_user_modal_test.dart b/test/presentation/my_page/delete_user_modal_test.dart index 029911eb..f1f1d634 100644 --- a/test/presentation/my_page/delete_user_modal_test.dart +++ b/test/presentation/my_page/delete_user_modal_test.dart @@ -8,6 +8,7 @@ import 'package:on_time_front/domain/repositories/user_repository.dart'; import 'package:on_time_front/domain/use-cases/delete_user_use_case.dart'; import 'package:on_time_front/l10n/app_localizations.dart'; import 'package:on_time_front/presentation/my_page/my_page_modal/delete_user_modal.dart'; +import 'package:on_time_front/presentation/shared/components/two_action_dialog.dart'; import 'package:on_time_front/presentation/shared/theme/theme.dart'; void main() { @@ -144,10 +145,18 @@ void main() { await _openFeedbackDialog(tester); await tester.tap(find.text('의견 보내고 탈퇴하기')); - await tester.pumpAndSettle(); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 200)); + expect(find.byType(TwoActionDialog), findsOneWidget); expect(find.text('오류'), findsOneWidget); expect(find.text('더 좋은 서비스로 다시 만나요'), findsOneWidget); + + await tester.tap(find.text('확인')); + await tester.pumpAndSettle(); + + expect(find.byType(TwoActionDialog), findsNothing); + expect(find.text('더 좋은 서비스로 다시 만나요'), findsOneWidget); expect(find.byType(CircularProgressIndicator), findsNothing); expect(repository.didSignOut, isFalse); expect(didConfirm, isFalse);