From 55de4cb2ea96fe4ced59af19578f044419720dc1 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Tue, 6 Jun 2023 04:36:48 +0300 Subject: [PATCH] core: replace auto_route with go_router (#406) * ft: migrate to go_router * fix(tests): go_router tests --- lib/presentation/auth/auth_screen.dart | 7 +- .../auth/unauthenticated_screen.dart | 12 +- .../auth/widgets/profile_photo.dart | 6 +- lib/presentation/auth/widgets/verified.dart | 10 +- lib/presentation/core/app_widget.dart | 8 +- .../crowdaction_comments_page.dart | 2 +- .../parts/comment_appbar_delegate.dart | 2 +- .../widgets/flag_comment.dart | 8 +- .../crowdaction_details_screen.dart | 8 +- .../widgets/confirm_participation.dart | 8 +- .../widgets/crowdaction_details_banner.dart | 4 +- .../widgets/withdraw_participation.dart | 4 +- .../crowdaction_participants_screen.dart | 4 +- .../components_demo_screen.dart | 6 +- lib/presentation/demo/demo_screen.dart | 16 +- lib/presentation/home/home_screen.dart | 30 +-- .../home/widgets/current_upcoming_layout.dart | 6 +- .../home/widgets/password_modal.dart | 16 +- .../onboarding/onboarding_screen.dart | 7 +- lib/presentation/profile/profile_screen.dart | 12 +- .../profile/widget/commitments_tab.dart | 9 +- .../profile/widget/signup_cta.dart | 6 +- lib/presentation/routes/app_pages.dart | 32 +++ lib/presentation/routes/app_routes.dart | 190 ++++++++++++------ .../settings/settings_layout.dart | 6 +- .../settings/settings_screen.dart | 18 +- .../shared_widgets/crowdaction_card.dart | 10 +- .../custom_app_bars/custom_appbar.dart | 6 +- .../micro_crowdaction_card.dart | 11 +- lib/presentation/utils/launch_url.dart | 6 +- pubspec.lock | 24 +-- pubspec.yaml | 3 +- .../presentation/auth/profile_photo_test.dart | 20 +- .../auth/profile_photo_test.ext.dart | 8 +- test/presentation/auth/verified_test.dart | 23 +-- test/presentation/auth/verified_test.ext.dart | 4 +- .../confirm_participation_test.dart | 23 +-- .../confirm_participation_test.ext.dart | 4 +- .../withdraw_participation_test.dart | 11 +- test/presentation/home/home_screen_test.dart | 5 +- .../home/widgets/password_modal_test.dart | 27 ++- .../profile/profile_screen_test.dart | 5 +- .../presentation/profile/signup_cta_test.dart | 15 +- test/presentation/router.mocks.dart | 60 +++--- .../settings/settings_layout_test.dart | 16 +- .../settings/settings_layout_test.ext.dart | 4 +- .../settings/settings_screen_test.dart | 87 ++++---- .../settings/settings_screen_test.ext.dart | 4 +- .../shared_widgets/crowdaction_card_test.dart | 21 +- .../crowdaction_card_test.ext.dart | 4 +- .../micro_crowdaction_card_test.dart | 28 +-- 51 files changed, 464 insertions(+), 402 deletions(-) create mode 100644 lib/presentation/routes/app_pages.dart diff --git a/lib/presentation/auth/auth_screen.dart b/lib/presentation/auth/auth_screen.dart index f066cb25..eab48512 100644 --- a/lib/presentation/auth/auth_screen.dart +++ b/lib/presentation/auth/auth_screen.dart @@ -1,11 +1,11 @@ -import 'package:auto_route/auto_route.dart'; import 'package:dots_indicator/dots_indicator.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../application/auth/auth_bloc.dart'; import '../../application/user/profile/profile_bloc.dart'; -import '../routes/app_routes.gr.dart'; +import '../routes/app_routes.dart'; import '../shared_widgets/custom_app_bars/custom_appbar.dart'; import '../themes/constants.dart'; import '../utils/context.ext.dart'; @@ -115,8 +115,7 @@ class AuthPageState extends State { ); } - void _authDone(BuildContext context) => - context.router.replaceAll([const VerifiedRoute()]); + void _authDone(BuildContext context) => context.go(AppPage.verified.path); void _toPage(int page) => _pageController.animateToPage( page, diff --git a/lib/presentation/auth/unauthenticated_screen.dart b/lib/presentation/auth/unauthenticated_screen.dart index f5e91a29..25a6bdd9 100644 --- a/lib/presentation/auth/unauthenticated_screen.dart +++ b/lib/presentation/auth/unauthenticated_screen.dart @@ -1,11 +1,11 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; -import '../routes/app_routes.gr.dart'; -import '../shared_widgets/pill_button.dart'; -import '../themes/constants.dart'; import '../../domain/core/i_settings_repository.dart'; import '../../infrastructure/core/injection.dart'; +import '../routes/app_routes.dart'; +import '../shared_widgets/pill_button.dart'; +import '../themes/constants.dart'; class UnauthenticatedPage extends StatelessWidget { @override @@ -51,7 +51,7 @@ class UnauthenticatedPage extends StatelessWidget { text: 'Log In', isEnabled: true, isLoading: false, - onTap: () => context.router.push(const AuthRoute()), + onTap: () => context.push(AppPage.auth.path), lightBackground: true, ), const SizedBox(height: 80), @@ -66,7 +66,7 @@ class UnauthenticatedPage extends StatelessWidget { // Push onboarding screen if first time launching application final settingsRepository = getIt(); if (!(await settingsRepository.getWasUserOnboarded())) { - context.router.push(const OnboardingRoute()); + context.push(AppPage.onBoarding.path); } } } diff --git a/lib/presentation/auth/widgets/profile_photo.dart b/lib/presentation/auth/widgets/profile_photo.dart index 0d4ff7ec..cf572c62 100644 --- a/lib/presentation/auth/widgets/profile_photo.dart +++ b/lib/presentation/auth/widgets/profile_photo.dart @@ -1,8 +1,8 @@ import 'dart:io'; -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../../application/auth/auth_bloc.dart'; import '../../../application/user/avatar/avatar_bloc.dart'; @@ -42,9 +42,7 @@ class SelectProfilePhotoState extends State { uploading: () { /// TODO: Loading indication }, - uploadSuccess: () { - context.router.pop(); - }, + uploadSuccess: context.pop, uploadFailed: () { /// TODO: Show error snackbar | Implement failures }, diff --git a/lib/presentation/auth/widgets/verified.dart b/lib/presentation/auth/widgets/verified.dart index 1729d16a..971bde74 100644 --- a/lib/presentation/auth/widgets/verified.dart +++ b/lib/presentation/auth/widgets/verified.dart @@ -1,12 +1,12 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:rive/rive.dart'; import 'package:shimmer/shimmer.dart'; import '../../../application/user/profile/profile_bloc.dart'; import '../../../infrastructure/core/injection.dart'; -import '../../routes/app_routes.gr.dart'; +import '../../routes/app_routes.dart'; import '../../shared_widgets/pill_button.dart'; import '../../shared_widgets/shimmers/title_shimmer_line.dart'; import '../../themes/constants.dart'; @@ -80,13 +80,11 @@ class VerifiedPage extends StatelessWidget { ), const SizedBox(height: 40), PillButton( - onTap: () => - context.router.replaceAll([const HomeRoute()]), + onTap: () => context.go(AppPage.home.path), text: 'Go to CrowdActions', ), TextButton( - onPressed: () => - context.router.replaceAll([const HomeRoute()]), + onPressed: () => context.go(AppPage.home.path), child: const Text( 'Show me all CrowdActions', style: TextStyle( diff --git a/lib/presentation/core/app_widget.dart b/lib/presentation/core/app_widget.dart index c4222876..283e09dd 100644 --- a/lib/presentation/core/app_widget.dart +++ b/lib/presentation/core/app_widget.dart @@ -5,7 +5,8 @@ import '../../application/auth/auth_bloc.dart'; import '../../application/user/profile/profile_bloc.dart'; import '../../application/user/profile_tab/profile_tab_bloc.dart'; import '../../infrastructure/core/injection.dart'; -import '../routes/app_routes.gr.dart'; + +import '../routes/app_routes.dart'; import '../themes/themes.dart'; class AppWidget extends StatelessWidget { @@ -35,7 +36,7 @@ class AppWidget extends StatelessWidget { .add(FetchProfileTabInfo()); }, unauthenticated: () { - _appRouter.replaceAll([const UnauthenticatedRoute()]); + _appRouter.router.go(AppPage.unauthenticated.path); }, orElse: () {}, ); @@ -44,8 +45,7 @@ class AppWidget extends StatelessWidget { color: Colors.white, title: 'CollAction', theme: lightTheme(), - routerDelegate: _appRouter.delegate(), - routeInformationParser: _appRouter.defaultRouteParser(), + routerConfig: _appRouter.router, ), ), ); diff --git a/lib/presentation/crowdaction/crowdaction_comments/crowdaction_comments_page.dart b/lib/presentation/crowdaction/crowdaction_comments/crowdaction_comments_page.dart index 6765f865..c2d1df6d 100644 --- a/lib/presentation/crowdaction/crowdaction_comments/crowdaction_comments_page.dart +++ b/lib/presentation/crowdaction/crowdaction_comments/crowdaction_comments_page.dart @@ -1,5 +1,5 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../core/core.dart'; import '../../../domain/crowdaction/crowdaction_comment.dart'; diff --git a/lib/presentation/crowdaction/crowdaction_comments/parts/comment_appbar_delegate.dart b/lib/presentation/crowdaction/crowdaction_comments/parts/comment_appbar_delegate.dart index 5a58d537..d2434064 100644 --- a/lib/presentation/crowdaction/crowdaction_comments/parts/comment_appbar_delegate.dart +++ b/lib/presentation/crowdaction/crowdaction_comments/parts/comment_appbar_delegate.dart @@ -24,7 +24,7 @@ class CommentAppBarDelegate extends SliverPersistentHeaderDelegate { child: IconButton( icon: const Icon(CollactionIcons.left), iconSize: 24, - onPressed: () => context.router.pop(), + onPressed: context.pop, color: Colors.white, ), ) diff --git a/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart b/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart index 18a46210..9e72d61a 100644 --- a/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart +++ b/lib/presentation/crowdaction/crowdaction_comments/widgets/flag_comment.dart @@ -1,5 +1,5 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../shared_widgets/pill_button.dart'; import '../../../shared_widgets/selectable_chip.dart'; @@ -145,7 +145,7 @@ class FlagDialogState extends State { width: double.infinity, height: 52, child: TextButton( - onPressed: () => context.router.pop(), + onPressed: context.pop, child: const Text("Cancel"), ), ), @@ -217,9 +217,7 @@ class FlagSuccess extends StatelessWidget { ), PillButton( text: "Got it", - onTap: () { - context.router.pop(); - }, + onTap: context.pop, margin: EdgeInsets.zero, ), const SizedBox(height: 20), diff --git a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart index cea76a9d..4ad3dfb9 100644 --- a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart @@ -1,6 +1,6 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../../../domain/crowdaction/crowdaction.dart'; import '../../../../infrastructure/core/injection.dart'; @@ -8,7 +8,8 @@ import '../../../application/auth/auth_bloc.dart'; import '../../../application/crowdaction/crowdaction_details/crowdaction_details_bloc.dart'; import '../../../application/participation/participation_bloc.dart'; import '../../../application/user/profile_tab/profile_tab_bloc.dart'; -import '../../routes/app_routes.gr.dart'; + +import '../../routes/app_routes.dart'; import '../../shared_widgets/commitments/commitment_card_list.dart'; import '../../shared_widgets/pill_button.dart'; import '../../themes/constants.dart'; @@ -348,7 +349,6 @@ class CrowdActionDetailsPageState extends State { } void _createAccount(BuildContext context) { - context.router.pop(); - context.router.push(const AuthRoute()); + context.go(AppPage.auth.path); } } diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart index d6cbf014..2e7cbb09 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart @@ -1,6 +1,6 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../../../../domain/crowdaction/crowdaction.dart'; import '../../../../application/participation/participation_bloc.dart'; @@ -91,9 +91,7 @@ class ParticipationSuccess extends StatelessWidget { ), PillButton( text: "Got it", - onTap: () { - context.router.pop(); - }, + onTap: context.pop, margin: EdgeInsets.zero, ), const SizedBox(height: 20), @@ -223,7 +221,7 @@ class ParticipationDialog extends StatelessWidget { width: double.infinity, height: 52, child: TextButton( - onPressed: () => context.router.pop(), + onPressed: context.pop, child: const Text("Cancel"), ), ), diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart index b7d20feb..35b5fbd4 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/crowdaction_details_banner.dart @@ -1,6 +1,6 @@ -import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:shimmer/shimmer.dart'; import '../../../../core/core.dart'; @@ -39,7 +39,7 @@ class CrowdActionDetailsBanner extends StatelessWidget { elevation: 4, child: InkWell( borderRadius: BorderRadius.circular(20), - onTap: () => context.router.pop(), + onTap: context.pop, child: const Icon( CollactionIcons.left, color: kPrimaryColor400, diff --git a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart index 513b3f92..28398144 100644 --- a/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart +++ b/lib/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart @@ -1,6 +1,6 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../../../../domain/crowdaction/crowdaction.dart'; import '../../../../application/participation/participation_bloc.dart'; @@ -164,7 +164,7 @@ class WithdrawParticipation extends StatelessWidget { width: double.infinity, height: 52, child: TextButton( - onPressed: () => context.router.pop(), + onPressed: context.pop, child: const Text("Cancel"), ), ), diff --git a/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart b/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart index 5ced5712..c0ab59d2 100644 --- a/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_participants/crowdaction_participants_screen.dart @@ -1,6 +1,6 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import '../../../application/crowdaction/crowdaction_participants/crowdaction_participants_bloc.dart'; @@ -85,7 +85,7 @@ class _CrowdActionParticipantsPageState Icons.chevron_left, color: kPrimaryColor200, ), - onPressed: () => context.router.pop(), + onPressed: context.pop, ), title: const Text( "Participants", diff --git a/lib/presentation/demo/components_demo/components_demo_screen.dart b/lib/presentation/demo/components_demo/components_demo_screen.dart index c89d83b6..72c42d88 100644 --- a/lib/presentation/demo/components_demo/components_demo_screen.dart +++ b/lib/presentation/demo/components_demo/components_demo_screen.dart @@ -1,5 +1,5 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../domain/crowdaction/crowdaction.dart'; import '../../shared_widgets/accent_chip.dart'; @@ -326,9 +326,7 @@ class ComponentsDemoPageState extends State { width: double.infinity, height: 52, child: TextButton( - onPressed: () { - context.router.pop(); - }, + onPressed: context.pop, child: const Text("Cancel"), ), ), diff --git a/lib/presentation/demo/demo_screen.dart b/lib/presentation/demo/demo_screen.dart index 26412a7f..8b0905fd 100644 --- a/lib/presentation/demo/demo_screen.dart +++ b/lib/presentation/demo/demo_screen.dart @@ -1,14 +1,15 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../domain/core/i_settings_repository.dart'; import '../../infrastructure/core/injection.dart'; -import '../routes/app_routes.gr.dart'; +import '../routes/app_routes.dart'; import '../shared_widgets/rectangle_button.dart'; import 'components_demo/current_user_status_text.dart'; class DemoPage extends StatelessWidget { DemoPage({super.key}); + final settingsRepository = getIt(); final _pageScrollController = ScrollController(); @@ -30,23 +31,22 @@ class DemoPage extends StatelessWidget { const SizedBox(height: 30.0), RectangleButton( text: "Reusable Components", - onTap: () => context.router.push(const ComponentsDemoRoute()), + onTap: () => context.push(AppPage.componentsDemo.path), ), const SizedBox(height: 10.0), RectangleButton( text: "Crowdaction Comments", - onTap: () => - context.router.push(const CrowdActionCommentsRoute()), + onTap: () => context.push(AppPage.commentsDemo.path), ), const SizedBox(height: 10.0), RectangleButton( text: "Contact Form", - onTap: () => context.router.push(const ContactFormRoute()), + onTap: () => context.push(AppPage.contactForm.path), ), const SizedBox(height: 10.0), RectangleButton( text: "Onboarding", - onTap: () => context.router.push(const OnboardingRoute()), + onTap: () => context.push(AppPage.onBoarding.path), ), const SizedBox( height: 15.0, @@ -57,7 +57,7 @@ class DemoPage extends StatelessWidget { ), RectangleButton( text: "Register", - onTap: () => context.router.push(const AuthRoute()), + onTap: () => context.push(AppPage.auth.path), ), ], ), diff --git a/lib/presentation/home/home_screen.dart b/lib/presentation/home/home_screen.dart index cfa80edb..4de1ce2d 100644 --- a/lib/presentation/home/home_screen.dart +++ b/lib/presentation/home/home_screen.dart @@ -1,27 +1,24 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../presentation/themes/constants.dart'; import '../core/collaction_icons.dart'; -import '../routes/app_routes.gr.dart'; class HomePage extends StatelessWidget { + final StatefulNavigationShell navigationShell; + + const HomePage({super.key, required this.navigationShell}); + @override Widget build(BuildContext context) { - return AutoTabsScaffold( - routes: const [ - CrowdactionRouter(), - UserProfileRouter(), - if (!kReleaseMode) ...[ - DemoScreenRouter(), - ], - ], - bottomNavigationBuilder: (_, tabsRouter) => bottomNavbar(tabsRouter), + return Scaffold( + body: navigationShell, + bottomNavigationBar: bottomNavbar(), ); } - Widget bottomNavbar(TabsRouter tabsRouter) { + Widget bottomNavbar() { return BottomNavigationBar( backgroundColor: Colors.white, showSelectedLabels: false, @@ -48,8 +45,13 @@ class HomePage extends StatelessWidget { ), ], ], - currentIndex: tabsRouter.activeIndex, - onTap: tabsRouter.setActiveIndex, + currentIndex: navigationShell.currentIndex, + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, ); } } diff --git a/lib/presentation/home/widgets/current_upcoming_layout.dart b/lib/presentation/home/widgets/current_upcoming_layout.dart index 593bddc1..0f5d2413 100644 --- a/lib/presentation/home/widgets/current_upcoming_layout.dart +++ b/lib/presentation/home/widgets/current_upcoming_layout.dart @@ -1,9 +1,9 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import '../../../application/crowdaction/spotlight/spotlight_bloc.dart'; -import '../../routes/app_routes.gr.dart'; +import '../../routes/app_routes.dart'; import '../../shared_widgets/content_placeholder.dart'; import '../../shared_widgets/micro_crowdaction_card.dart'; import '../../shared_widgets/micro_crowdaction_card_loading.dart'; @@ -55,7 +55,7 @@ class _CurrentAndUpcomingLayoutState extends State { ), TextButton( onPressed: () => - context.router.push(CrowdActionBrowseRoute()), + context.push(AppPage.crowdActionsList.path), child: const Text( 'View all', textAlign: TextAlign.center, diff --git a/lib/presentation/home/widgets/password_modal.dart b/lib/presentation/home/widgets/password_modal.dart index e2288136..5bb922c5 100644 --- a/lib/presentation/home/widgets/password_modal.dart +++ b/lib/presentation/home/widgets/password_modal.dart @@ -1,12 +1,13 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../domain/core/i_settings_repository.dart'; import '../../../domain/crowdaction/crowdaction.dart'; import '../../../infrastructure/core/injection.dart'; import '../../../presentation/core/collaction_icons.dart'; -import '../../../presentation/routes/app_routes.gr.dart'; + import '../../../presentation/themes/constants.dart'; +import '../../routes/app_routes.dart'; class PasswordModal extends StatefulWidget { final CrowdAction crowdAction; @@ -27,8 +28,8 @@ class PasswordModal extends StatefulWidget { final accessList = await settingsRepository.getCrowdActionAccessList(); if (accessList.contains(crowdAction.id)) { - context.router.push( - CrowdActionDetailsRoute(crowdAction: crowdAction), + context.push( + AppPage.crowdActionDetailsRoute(crowdAction.id), ); } else { showModalBottomSheet( @@ -167,11 +168,8 @@ class _PasswordModalState extends State { addCrowdActionAccess(); - context.router.popAndPush( - CrowdActionDetailsRoute( - crowdAction: widget.crowdAction, - ), - ); + context.pop(); + context.push(AppPage.crowdActionDetailsRoute(widget.crowdAction.id)); } else { setState(() => _validated = false); } diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart index 12c13b4a..2fbda06c 100644 --- a/lib/presentation/onboarding/onboarding_screen.dart +++ b/lib/presentation/onboarding/onboarding_screen.dart @@ -1,11 +1,12 @@ -import 'package:auto_route/auto_route.dart'; import 'package:dots_indicator/dots_indicator.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../domain/core/i_settings_repository.dart'; import '../../infrastructure/core/injection.dart'; import '../../presentation/shared_widgets/no_ripple_behavior.dart'; import '../core/collaction_icons.dart'; +import '../routes/app_routes.dart'; import '../themes/constants.dart'; import 'widgets/onboarding_step.dart'; @@ -100,7 +101,7 @@ class OnboardingPageState extends State { child: TextButton( onPressed: () => settingsRepository .setWasUserOnboarded(wasOnboarded: true) - .then((_) => context.router.pop()), + .then(context.pop), child: const Text( "Skip", style: TextStyle( @@ -129,6 +130,6 @@ class OnboardingPageState extends State { void getStarted() { settingsRepository .setWasUserOnboarded(wasOnboarded: true) - .then((_) => context.router.popUntilRoot()); + .then((_) => context.go(AppPage.home.path)); } } diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index cf54a308..4b355d21 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -1,13 +1,14 @@ import 'dart:io'; -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; import 'package:share_plus/share_plus.dart'; import '../../application/user/profile/profile_bloc.dart'; import '../core/collaction_icons.dart'; -import '../routes/app_routes.gr.dart'; + +import '../routes/app_routes.dart'; import '../shared_widgets/photo_selector.dart'; import '../shared_widgets/pill_button.dart'; import '../themes/constants.dart'; @@ -77,7 +78,7 @@ class _UserProfilePageState extends State { ), const SizedBox(height: 10), ElevatedButton( - onPressed: () => context.router.push(const SettingsRoute()), + onPressed: () => context.push(AppPage.settings.path), style: ElevatedButton.styleFrom( foregroundColor: kPrimaryColor0, backgroundColor: Colors.white, @@ -318,9 +319,8 @@ class _UserProfilePageState extends State { const SizedBox(height: 40), PillButton( text: 'Sign in', - onTap: () => context.router - .push(const AuthRoute()) - .then((_) { + onTap: () => + context.push(AppPage.auth.path).then((_) { // Refresh profile context .read() diff --git a/lib/presentation/profile/widget/commitments_tab.dart b/lib/presentation/profile/widget/commitments_tab.dart index f8ed87e1..d6047d9d 100644 --- a/lib/presentation/profile/widget/commitments_tab.dart +++ b/lib/presentation/profile/widget/commitments_tab.dart @@ -1,9 +1,10 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../domain/crowdaction/crowdaction.dart'; import '../../../domain/user/user.dart'; -import '../../routes/app_routes.gr.dart'; + +import '../../routes/app_routes.dart'; import '../../shared_widgets/commitments/commitment_card.dart'; import '../../themes/constants.dart'; import 'signup_cta.dart'; @@ -47,8 +48,8 @@ class CommitmentsTab extends StatelessWidget { .where((crowdAction) => !crowdAction.isClosed) .map( (crowdAction) => GestureDetector( - onTap: () => context.router.push( - CrowdActionDetailsRoute(crowdAction: crowdAction), + onTap: () => context.push( + AppPage.crowdActionDetailsRoute(crowdAction.id), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/profile/widget/signup_cta.dart b/lib/presentation/profile/widget/signup_cta.dart index b924fd40..df3a990d 100644 --- a/lib/presentation/profile/widget/signup_cta.dart +++ b/lib/presentation/profile/widget/signup_cta.dart @@ -1,8 +1,8 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../domain/user/user.dart'; -import '../../routes/app_routes.gr.dart'; +import '../../routes/app_routes.dart'; import '../../shared_widgets/pill_button.dart'; import '../../themes/constants.dart'; @@ -50,7 +50,7 @@ class SignUpCTA extends StatelessWidget { PillButton( text: 'Sign in', onTap: () { - context.router.push(const AuthRoute()); + context.push(AppPage.auth.path); }, ), ], diff --git a/lib/presentation/routes/app_pages.dart b/lib/presentation/routes/app_pages.dart new file mode 100644 index 00000000..5eb3ad33 --- /dev/null +++ b/lib/presentation/routes/app_pages.dart @@ -0,0 +1,32 @@ +part of 'app_routes.dart'; + +enum AppPage { + mainPage('/'), + home('/home'), + crowdActionDetails('/details'), + crowdActionParticipants('/participants'), + crowdActionsList('/view-all'), + userProfile('/user'), + profileDetails('/user/details'), + + // Demo + demoPage('/demo'), + componentsDemo('/demo/components'), + commentsDemo('/demo/comments'), + onBoarding('/onboarding'), + auth('/auth'), + verified('/verified'), + settings('/settings'), + settingsLayout('/settings-layout'), + licenses('/licenses'), + contactForm('/contact-form'), + unauthenticated('/unauthenticated'), + webView('/web-view'); + + final String path; + + const AppPage(this.path); + + static String crowdActionDetailsRoute(String id) => + '${AppPage.crowdActionDetails.path}/$id'; +} diff --git a/lib/presentation/routes/app_routes.dart b/lib/presentation/routes/app_routes.dart index 4bfe0835..8f9acfff 100644 --- a/lib/presentation/routes/app_routes.dart +++ b/lib/presentation/routes/app_routes.dart @@ -1,9 +1,9 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:auto_route/empty_router_widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../../presentation/profile/profile_screen.dart'; -import '../auth/unauthenticated_screen.dart'; import '../auth/auth_screen.dart'; +import '../auth/unauthenticated_screen.dart'; import '../auth/widgets/verified.dart'; import '../contact_form/contact_form_screen.dart'; import '../crowdaction/crowdaction_browse/crowdaction_browse_screen.dart'; @@ -20,62 +20,132 @@ import '../settings/settings_layout.dart'; import '../settings/settings_screen.dart'; import '../shared_widgets/web_view_page.dart'; -@MaterialAutoRouter( - replaceInRouteName: 'Page,Route', - routes: [ - AutoRoute( - page: HomePage, - initial: true, - children: [ - AutoRoute( - path: 'browse-crowdactions', - name: 'CrowdactionRouter', - page: EmptyRouterPage, - children: [ - AutoRoute(path: '', page: CrowdActionHomeScreen), - AutoRoute(path: 'details', page: CrowdActionDetailsPage), - AutoRoute(path: 'participants', page: CrowdActionParticipantsPage), - AutoRoute(path: 'view-all', page: CrowdActionBrowsePage), - ], - ), - AutoRoute( - path: 'user', - name: 'UserProfileRouter', - page: EmptyRouterPage, - children: [ - AutoRoute( - path: '', - page: UserProfilePage, +part 'app_pages.dart'; + +class AppRouter { + final GlobalKey _rootNavigatorKey = + GlobalKey(debugLabel: 'root'); + + GoRouter get router => GoRouter( + navigatorKey: _rootNavigatorKey, + routes: [ + StatefulShellRoute.indexedStack( + builder: ( + BuildContext context, + GoRouterState state, + StatefulNavigationShell navigationShell, + ) { + return HomePage(navigationShell: navigationShell); + }, + branches: [ + StatefulShellBranch(routes: [ + GoRoute( + path: AppPage.home.path, + pageBuilder: (_, __) => + const NoTransitionPage(child: CrowdActionHomeScreen()), + ), + ]), + StatefulShellBranch(routes: [ + GoRoute( + path: AppPage.userProfile.path, + pageBuilder: (_, __) => + NoTransitionPage(child: UserProfilePage()), + ), + ]), + StatefulShellBranch(routes: [ + GoRoute( + path: AppPage.demoPage.path, + pageBuilder: (_, __) => NoTransitionPage(child: DemoPage()), + routes: [ + GoRoute( + path: 'components', + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const ComponentsDemoPage(), + ), + GoRoute( + path: 'comments', + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const CrowdActionCommentsPage(), + ), + ]) + ]) + ], + ), + GoRoute( + path: AppPage.crowdActionsList.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (BuildContext context, GoRouterState state) { + return CrowdActionBrowsePage(); + }, + ), + GoRoute( + path: '${AppPage.crowdActionDetails.path}/:id', + parentNavigatorKey: _rootNavigatorKey, + builder: (BuildContext context, GoRouterState state) { + return CrowdActionDetailsPage( + crowdActionId: state.pathParameters['id'], + ); + }, + ), + GoRoute( + path: AppPage.crowdActionParticipants.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, state) => CrowdActionParticipantsPage( + crowdActionId: state.extra! as String, ), - AutoRoute( - path: 'details', - page: CrowdActionDetailsPage, + ), + GoRoute( + path: AppPage.onBoarding.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const OnboardingPage(), + ), + GoRoute( + path: AppPage.verified.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const VerifiedPage(), + ), + GoRoute( + path: AppPage.auth.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const AuthPage(), + ), + GoRoute( + path: AppPage.settings.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const SettingsPage(), + ), + GoRoute( + path: AppPage.licenses.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const LicensesPage(), + ), + GoRoute( + path: AppPage.settingsLayout.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const SettingsLayout(), + ), + GoRoute( + path: AppPage.contactForm.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => const ContactFormPage(), + ), + GoRoute( + path: AppPage.unauthenticated.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, __) => UnauthenticatedPage(), + ), + GoRoute( + path: AppPage.webView.path, + parentNavigatorKey: _rootNavigatorKey, + builder: (_, state) => WebViewPage( + url: state.extra?.toString() ?? "", + title: state.pathParameters['title']?.toString() ?? "", ), - ], - ), - AutoRoute( - path: 'demo', - name: 'DemoScreenRouter', - page: EmptyRouterPage, - children: [ - AutoRoute(path: '', page: DemoPage), - AutoRoute(path: 'components-demo', page: ComponentsDemoPage), - AutoRoute(path: 'onboarding', page: OnboardingPage), - AutoRoute(path: 'verified', page: VerifiedPage), - AutoRoute(path: 'comments', page: CrowdActionCommentsPage), - ], - ), - ], - ), - AutoRoute(path: 'onboarding', page: OnboardingPage), - AutoRoute(path: 'auth', page: AuthPage), - AutoRoute(path: 'verified', page: VerifiedPage), - AutoRoute(path: 'settings-page', page: SettingsPage), - AutoRoute(path: 'licenses-page', page: LicensesPage), - AutoRoute(path: 'settings-layout', page: SettingsLayout), - AutoRoute(path: 'contact-form', page: ContactFormPage), - AutoRoute(path: 'webview', page: WebViewPage), - AutoRoute(path: 'unauthenticated', page: UnauthenticatedPage) - ], -) -class $AppRouter {} + ) + ], + debugLogDiagnostics: true, + initialLocation: AppPage.home.path, + ); + + AppRouter(); +} diff --git a/lib/presentation/settings/settings_layout.dart b/lib/presentation/settings/settings_layout.dart index 56dbdbc1..ecd69cbd 100644 --- a/lib/presentation/settings/settings_layout.dart +++ b/lib/presentation/settings/settings_layout.dart @@ -1,5 +1,5 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../themes/constants.dart'; @@ -14,7 +14,7 @@ class SettingsLayout extends StatelessWidget { backgroundColor: kSecondaryColor, leading: RawMaterialButton( elevation: 5, - onPressed: () => context.router.pop(), + onPressed: context.pop, child: const CircleAvatar( backgroundColor: kSecondaryColor, child: Icon( @@ -30,7 +30,7 @@ class SettingsLayout extends StatelessWidget { backgroundColor: MaterialStateProperty.all(kSecondaryColor), elevation: MaterialStateProperty.all(2), ), - onPressed: () => context.router.pop(), + onPressed: context.pop, child: const CircleAvatar( backgroundColor: kSecondaryColor, child: Icon( diff --git a/lib/presentation/settings/settings_screen.dart b/lib/presentation/settings/settings_screen.dart index 1689e386..2999d8b6 100644 --- a/lib/presentation/settings/settings_screen.dart +++ b/lib/presentation/settings/settings_screen.dart @@ -1,14 +1,15 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import '../routes/app_routes.dart'; import '/application/settings/build_information/build_information_bloc.dart'; import '../../../presentation/utils/launch_url.dart'; import '../../application/auth/auth_bloc.dart'; import '../../application/user/profile/profile_bloc.dart'; import '../../infrastructure/core/injection.dart'; import '../core/collaction_icons.dart'; -import '../routes/app_routes.gr.dart'; + import '../shared_widgets/custom_app_bars/custom_appbar.dart'; import '../themes/constants.dart'; import 'widgets/build_information_tile.dart'; @@ -40,24 +41,21 @@ class SettingsPage extends StatelessWidget { title: 'Contact us', icon: CollactionIcons.message, trailingIcon: CollactionIcons.arrow_right, - onTap: () => - context.router.push(const ContactFormRoute()), + onTap: () => context.push(AppPage.contactForm.path), ), const SizedBox(height: 15), SettingsListTile( title: 'Onboarding', icon: CollactionIcons.rocket, trailingIcon: CollactionIcons.arrow_right, - onTap: () => - context.router.push(const OnboardingRoute()), + onTap: () => context.push(AppPage.onBoarding.path), ), const SizedBox(height: 15), SettingsListTile( title: 'Open source libraries', icon: CollactionIcons.opensource, trailingIcon: CollactionIcons.arrow_right, - onTap: () => - context.router.push(const LicensesRoute()), + onTap: () => context.push(AppPage.licenses.path), ), const SizedBox(height: 15), SettingsListTile( @@ -94,12 +92,12 @@ class SettingsPage extends StatelessWidget { title: 'Log out', icon: CollactionIcons.logout, iconColor: kErrorColor, - onTap: () async { + onTap: () { BlocProvider.of(context) .add(const AuthEvent.signedOut()); BlocProvider.of(context) .add(GetUserProfile()); - await context.router.pop(); + context.pop(); }, ), ], diff --git a/lib/presentation/shared_widgets/crowdaction_card.dart b/lib/presentation/shared_widgets/crowdaction_card.dart index 39e28ed7..19daa28b 100644 --- a/lib/presentation/shared_widgets/crowdaction_card.dart +++ b/lib/presentation/shared_widgets/crowdaction_card.dart @@ -1,14 +1,16 @@ -import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; + // ignore: depend_on_referenced_packages import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../domain/crowdaction/crowdaction.dart'; import '../core/collaction_icons.dart'; import '../crowdaction/crowdaction_details/widgets/participants.dart'; import '../home/widgets/password_modal.dart'; -import '../routes/app_routes.gr.dart'; + +import '../routes/app_routes.dart'; import '../themes/constants.dart'; import 'country_icon.dart'; import 'custom_fab.dart'; @@ -46,8 +48,8 @@ class _CrowdActionCardState extends State if (widget.crowdAction.hasPassword) { PasswordModal.show(context, widget.crowdAction); } else { - context.router.push( - CrowdActionDetailsRoute(crowdAction: widget.crowdAction), + context.push( + AppPage.crowdActionDetailsRoute(widget.crowdAction.id), ); } }, diff --git a/lib/presentation/shared_widgets/custom_app_bars/custom_appbar.dart b/lib/presentation/shared_widgets/custom_app_bars/custom_appbar.dart index be10ad26..4c2a84b5 100644 --- a/lib/presentation/shared_widgets/custom_app_bars/custom_appbar.dart +++ b/lib/presentation/shared_widgets/custom_app_bars/custom_appbar.dart @@ -1,5 +1,5 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../core/collaction_icons.dart'; import '../../themes/constants.dart'; @@ -29,7 +29,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { color: Colors.white, child: InkWell( borderRadius: BorderRadius.circular(20), - onTap: () => context.router.pop(), + onTap: context.pop, child: const Icon( CollactionIcons.left, color: kPrimaryColor400, @@ -43,7 +43,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { Padding( padding: const EdgeInsets.all(8.0), child: ElevatedButton( - onPressed: () => context.router.pop(), + onPressed: context.pop, style: ElevatedButton.styleFrom( foregroundColor: kPrimaryColor0, backgroundColor: Colors.white, diff --git a/lib/presentation/shared_widgets/micro_crowdaction_card.dart b/lib/presentation/shared_widgets/micro_crowdaction_card.dart index ab695dda..555b9931 100644 --- a/lib/presentation/shared_widgets/micro_crowdaction_card.dart +++ b/lib/presentation/shared_widgets/micro_crowdaction_card.dart @@ -1,10 +1,11 @@ -import 'package:auto_route/auto_route.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import '../../domain/crowdaction/crowdaction.dart'; import '../home/widgets/password_modal.dart'; -import '../routes/app_routes.gr.dart'; + +import '../routes/app_routes.dart'; import '../themes/constants.dart'; import 'accent_chip.dart'; import 'country_icon.dart'; @@ -30,11 +31,7 @@ class MicroCrowdActionCard extends StatelessWidget { if (crowdAction.hasPassword) { PasswordModal.show(context, crowdAction); } else { - context.router.push( - CrowdActionDetailsRoute( - crowdActionId: crowdAction.id, - ), - ); + context.push(AppPage.crowdActionDetailsRoute(crowdAction.id)); } }, child: SizedBox( diff --git a/lib/presentation/utils/launch_url.dart b/lib/presentation/utils/launch_url.dart index 33f63168..04ebcb20 100644 --- a/lib/presentation/utils/launch_url.dart +++ b/lib/presentation/utils/launch_url.dart @@ -1,8 +1,8 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../routes/app_routes.gr.dart'; +import '../routes/app_routes.dart'; /// Launches a URL, either in a webview or by using /// the default browser of the phone, if URL is reachable. @@ -21,7 +21,7 @@ Future launchUrl( if (await canLaunchUrl(Uri.parse(url))) { if (useWebView) { if (context != null) { - context.router.push(WebViewRoute(url: url)); + context.push(AppPage.webView.path, extra: url); return; } launchUrl(url, useWebView: true); diff --git a/pubspec.lock b/pubspec.lock index 8d0387fe..bde1cbc8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,22 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" - auto_route: - dependency: "direct main" - description: - name: auto_route - sha256: "12047baeca0e01df93165ef33275b32119d72699ab9a49dc64c20e78f586f96d" - url: "https://pub.dev" - source: hosted - version: "5.0.4" - auto_route_generator: - dependency: "direct dev" - description: - name: auto_route_generator - sha256: c66eaa20dbba3211cac656037f88ba836a633dda953d9f4f9f9f5809b57e4278 - url: "https://pub.dev" - source: hosted - version: "5.0.2" bloc: dependency: "direct main" description: @@ -592,6 +576,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "00d1b67d6e9fa443331da229084dd3eb04407f5a2dff22940bd7bba6af5722c3" + url: "https://pub.dev" + source: hosted + version: "7.1.1" graphs: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 49eec0f9..46addd13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,6 @@ environment: sdk: ">=2.18.6 <3.0.0" dependencies: - auto_route: ^5.0.2 bloc: ^8.1.0 cached_network_image: ^3.2.2 country_codes: ^2.2.0 @@ -31,6 +30,7 @@ dependencies: flutter_markdown: ^0.6.14 freezed_annotation: ^2.2.0 get_it: ^7.2.0 + go_router: ^7.1.1 http: ^0.13.5 image: ^3.2.2 image_cropper: ^3.0.0 @@ -52,7 +52,6 @@ dependencies: webview_flutter: ^4.0.2 dev_dependencies: - auto_route_generator: ^5.0.2 bloc_test: ^9.1.0 build_runner: ^2.3.0 firebase_auth_mocks: ^0.10.3 diff --git a/test/presentation/auth/profile_photo_test.dart b/test/presentation/auth/profile_photo_test.dart index 112bd6e5..3d967475 100644 --- a/test/presentation/auth/profile_photo_test.dart +++ b/test/presentation/auth/profile_photo_test.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:auto_route/auto_route.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:collaction_app/application/auth/auth_bloc.dart'; import 'package:collaction_app/application/user/avatar/avatar_bloc.dart'; @@ -12,6 +11,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:mocktail/mocktail.dart'; @@ -23,7 +23,7 @@ import '../shared_widgets/photo_selector_test.dart'; part 'profile_photo_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late AvatarBloc avatarBloc; late AuthBloc authBloc; @@ -34,7 +34,7 @@ void main() { MethodChannel('plugins.hunghd.vn/image_cropper'); setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); // Avatar Bloc avatarBloc = MockAvatarBloc(); @@ -72,7 +72,7 @@ void main() { group('SelectProfilePhoto tests:', () { testWidgets('can render', (WidgetTester tester) async { - await tester.pumpSelectProfilePhoto(authBloc, stackRouter, onSkip: () {}); + await tester.pumpSelectProfilePhoto(authBloc, goRouter, onSkip: () {}); await tester.pumpAndSettle(); expect(find.byType(SelectProfilePhoto), findsOneWidget); @@ -91,7 +91,7 @@ void main() { bool onSkipCalled = false; await tester.pumpSelectProfilePhoto( authBloc, - stackRouter, + goRouter, onSkip: () => onSkipCalled = true, ); await tester.tap(find.text('Maybe later')); @@ -102,7 +102,7 @@ void main() { testWidgets('open PhotoSelection, select photo, adds event to AuthBloc', (WidgetTester tester) async { - await tester.pumpSelectProfilePhoto(authBloc, stackRouter, onSkip: () {}); + await tester.pumpSelectProfilePhoto(authBloc, goRouter, onSkip: () {}); expect( tester.firstWidget(find.byType(PillButton)).isEnabled, @@ -129,21 +129,21 @@ void main() { verify(() => authBloc.add(any())).called(1); }); - testWidgets('stackRouter pops on uploadSuccess event', + testWidgets('goRouter pops on uploadSuccess event', (WidgetTester tester) async { whenListen( avatarBloc, Stream.fromIterable([AvatarState.uploadSuccess()]), ); - await tester.pumpSelectProfilePhoto(authBloc, stackRouter, onSkip: () {}); - verify(() => stackRouter.pop()).called(1); + await tester.pumpSelectProfilePhoto(authBloc, goRouter, onSkip: () {}); + verify(() => goRouter.pop()).called(1); }); testWidgets('PillButton isLoading', (WidgetTester tester) async { when(() => authBloc.state).thenAnswer( (_) => AuthState.awaitingPhotoUpdate(), ); - await tester.pumpSelectProfilePhoto(authBloc, stackRouter, onSkip: () {}); + await tester.pumpSelectProfilePhoto(authBloc, goRouter, onSkip: () {}); expect( tester.firstWidget(find.byType(PillButton)).isLoading, true, diff --git a/test/presentation/auth/profile_photo_test.ext.dart b/test/presentation/auth/profile_photo_test.ext.dart index 03a3fb9c..ed25342e 100644 --- a/test/presentation/auth/profile_photo_test.ext.dart +++ b/test/presentation/auth/profile_photo_test.ext.dart @@ -2,8 +2,10 @@ part of 'profile_photo_test.dart'; extension WidgetX on WidgetTester { Future pumpSelectProfilePhoto( - AuthBloc authBloc, StackRouter stackRouter, - {required Function() onSkip}) async { + AuthBloc authBloc, + GoRouter router, { + required Function() onSkip, + }) async { await pumpWidget( BlocProvider( create: (context) => authBloc, @@ -13,7 +15,7 @@ extension WidgetX on WidgetTester { onSkip: onSkip, ), ), - ).withRouterScope(stackRouter), + ).withRouterScope(router), ), ); } diff --git a/test/presentation/auth/verified_test.dart b/test/presentation/auth/verified_test.dart index 0ea2d3ed..20bd8bc9 100644 --- a/test/presentation/auth/verified_test.dart +++ b/test/presentation/auth/verified_test.dart @@ -1,11 +1,11 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/application/user/profile/profile_bloc.dart'; import 'package:collaction_app/presentation/auth/widgets/verified.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/shared_widgets/pill_button.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../application/user/profile/profile_bloc.mocks.dart'; @@ -15,15 +15,12 @@ import '../router.mocks.dart'; part 'verified_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late ProfileBloc profileBloc; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); - when(() => stackRouter.replaceAll([const HomeRoute()])).thenAnswer( - (_) async {}, - ); + goRouter = RouteHelpers.setUpRouterStubs(); // Profile Bloc profileBloc = MockProfileBloc(); @@ -37,7 +34,7 @@ void main() { group('VerifiedPage tests:', () { testWidgets('can render', (WidgetTester tester) async { - await tester.pumpVerifiedPage(stackRouter); + await tester.pumpVerifiedPage(goRouter); await tester.pump(); expect(find.byType(VerifiedPage), findsOneWidget); @@ -47,26 +44,26 @@ void main() { when(() => profileBloc.state).thenAnswer( (_) => ProfileState(userProfile: testUserProfile), ); - await tester.pumpVerifiedPage(stackRouter); + await tester.pumpVerifiedPage(goRouter); await tester.pump(); expect(find.text(testUserProfile.profile.fullName), findsOneWidget); }); testWidgets('PillButton redirects to home', (WidgetTester tester) async { - await tester.pumpVerifiedPage(stackRouter); + await tester.pumpVerifiedPage(goRouter); await tester.tap(find.byType(PillButton)); await tester.pump(); - verify(() => stackRouter.replaceAll([const HomeRoute()])).called(1); + verify(() => goRouter.go(AppPage.home.path)).called(1); }); testWidgets('TextButton redirects to home', (WidgetTester tester) async { - await tester.pumpVerifiedPage(stackRouter); + await tester.pumpVerifiedPage(goRouter); await tester.tap(find.byType(TextButton)); await tester.pump(); - verify(() => stackRouter.replaceAll([const HomeRoute()])).called(1); + verify(() => goRouter.go(AppPage.home.path)).called(1); }); }); } diff --git a/test/presentation/auth/verified_test.ext.dart b/test/presentation/auth/verified_test.ext.dart index 4838d665..3bdb0016 100644 --- a/test/presentation/auth/verified_test.ext.dart +++ b/test/presentation/auth/verified_test.ext.dart @@ -1,13 +1,13 @@ part of 'verified_test.dart'; extension WidgetX on WidgetTester { - Future pumpVerifiedPage(StackRouter stackRouter) async { + Future pumpVerifiedPage(GoRouter goRouter) async { await pumpWidget( MaterialApp( home: Scaffold( body: VerifiedPage(), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); } } diff --git a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart index fc1e4820..4d2bd087 100644 --- a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart +++ b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.dart @@ -1,4 +1,3 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/application/participation/participation_bloc.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; import 'package:collaction_app/presentation/crowdaction/crowdaction_details/widgets/confirm_participation.dart'; @@ -8,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../../application/participation/participation_bloc.mock.dart'; @@ -17,12 +17,11 @@ import '../../router.mocks.dart'; part 'confirm_participation_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late ParticipationBloc participationBloc; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); - when(() => stackRouter.pop()).thenAnswer((_) async => true); + goRouter = RouteHelpers.setUpRouterStubs(); // Participation Bloc participationBloc = MockParticipationBloc(); @@ -42,7 +41,7 @@ void main() { 'and press "Cancel" TextButton to pop context', (WidgetTester tester) async { await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, [], @@ -54,13 +53,13 @@ void main() { await tester.tap(find.byType(TextButton)); await tester.pumpAndSettle(); - verify(() => stackRouter.pop()).called(1); + verify(() => goRouter.pop()).called(1); }); testWidgets('shows 0 commitments, with Confirm button disabled', (WidgetTester tester) async { await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, [], @@ -85,7 +84,7 @@ void main() { testWidgets('shows 1 commitment, with Confirm button enabled', (WidgetTester tester) async { await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, [tCommitment], @@ -108,7 +107,7 @@ void main() { testWidgets('shows 3 commitments', (WidgetTester tester) async { await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, List.filled(3, tCommitment), @@ -131,7 +130,7 @@ void main() { final selectedCommitments = List.filled(3, tCommitment); await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, selectedCommitments, @@ -157,7 +156,7 @@ void main() { ); await tester.pumpConfirmParticipation( - stackRouter, + goRouter, participationBloc, tCrowdaction, [], @@ -169,7 +168,7 @@ void main() { await tester.tap(find.byType(PillButton)); await tester.pumpAndSettle(); - verify(() => stackRouter.pop()).called(1); + verify(() => goRouter.pop()).called(1); }); }); } diff --git a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart index 45ea656f..44f7404e 100644 --- a/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart +++ b/test/presentation/crowdaction/crowdaction_details/confirm_participation_test.ext.dart @@ -2,7 +2,7 @@ part of 'confirm_participation_test.dart'; extension WidgetX on WidgetTester { Future pumpConfirmParticipation( - StackRouter stackRouter, + GoRouter goRouter, ParticipationBloc participationBloc, CrowdAction crowdAction, List selectedCommitments) async { @@ -15,7 +15,7 @@ extension WidgetX on WidgetTester { crowdAction: crowdAction, selectedCommitments: selectedCommitments), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ), ); } diff --git a/test/presentation/crowdaction/crowdaction_details/withdraw_participation_test.dart b/test/presentation/crowdaction/crowdaction_details/withdraw_participation_test.dart index 33d8cd8e..ec109f40 100644 --- a/test/presentation/crowdaction/crowdaction_details/withdraw_participation_test.dart +++ b/test/presentation/crowdaction/crowdaction_details/withdraw_participation_test.dart @@ -1,9 +1,9 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/application/participation/participation_bloc.dart'; import 'package:collaction_app/presentation/crowdaction/crowdaction_details/widgets/withdraw_participation.dart'; import 'package:collaction_app/presentation/shared_widgets/pill_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../../application/participation/participation_bloc.mock.dart'; @@ -13,14 +13,13 @@ import '../../../utils/participation.fixtures.dart'; import '../../router.mocks.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late ParticipationBloc participationBloc; bool cancelPressed = false; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); - when(() => stackRouter.pop()).thenAnswer((_) async => true); + goRouter = RouteHelpers.setUpRouterStubs(); participationBloc = MockParticipationBloc(); when(() => participationBloc.state).thenAnswer( @@ -95,7 +94,7 @@ void main() { isParticipating: true, ), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); await tester.tap(find.byType(Text)); await tester.pumpAndSettle(); @@ -104,7 +103,7 @@ void main() { await tester.tap(find.byType(TextButton)); await tester.pumpAndSettle(); - verify(() => stackRouter.pop()).called(1); + verify(() => goRouter.pop()).called(1); }); testWidgets('post-cancel modal renders and closes on PillButton tap', diff --git a/test/presentation/home/home_screen_test.dart b/test/presentation/home/home_screen_test.dart index 6f8b6cdf..e74802d4 100644 --- a/test/presentation/home/home_screen_test.dart +++ b/test/presentation/home/home_screen_test.dart @@ -2,7 +2,7 @@ import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc. import 'package:collaction_app/domain/core/i_settings_repository.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart'; import 'package:collaction_app/presentation/home/home_screen.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -51,8 +51,7 @@ void main() { widget: MaterialApp.router( color: Colors.white, title: 'CollAction', - routerDelegate: appRouter.delegate(), - routeInformationParser: appRouter.defaultRouteParser(), + routerConfig: appRouter.router, ), ); await tester.pumpAndSettle(); diff --git a/test/presentation/home/widgets/password_modal_test.dart b/test/presentation/home/widgets/password_modal_test.dart index 5c65446a..a8ea15dc 100644 --- a/test/presentation/home/widgets/password_modal_test.dart +++ b/test/presentation/home/widgets/password_modal_test.dart @@ -1,11 +1,11 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/domain/core/i_settings_repository.dart'; import 'package:collaction_app/presentation/home/widgets/password_modal.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/themes/constants.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../../domain/core/i_settings_repository.mocks.dart'; @@ -14,11 +14,11 @@ import '../../../test_utilities.dart'; import '../../router.mocks.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late ISettingsRepository iSettingsRepository; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); iSettingsRepository = SettingsRepositoryMock(); when( @@ -106,7 +106,7 @@ void main() { // await buildAndPump( // tester: tester, // widget: PasswordModal(crowdAction: tCrowdaction) - // .withRouterScope(stackRouter), + // .withRouterScope(goRouter), // ); // await tester.pumpAndSettle(); @@ -120,7 +120,7 @@ void main() { // ).called(1); // final capturedRoutes = - // verify(() => stackRouter.popAndPush(captureAny())).captured; + // verify(() => goRouter.popAndPush(captureAny())).captured; // expect(capturedRoutes.length, 1); // expect(capturedRoutes.first, isA()); @@ -162,7 +162,7 @@ void main() { home: Scaffold( body: Container(), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); PasswordModal.show( @@ -172,14 +172,11 @@ void main() { await tester.pumpAndSettle(); - final capturedRoutes = - verify(() => stackRouter.push(captureAny())).captured; - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); - - final route = capturedRoutes.first as CrowdActionDetailsRoute; - expect(route.args?.crowdAction, tCrowdaction); - expect(route.args?.crowdActionId, null); + verify( + () => goRouter.push( + AppPage.crowdActionDetailsRoute(tCrowdaction.id), + ), + ).called(1); }, ); }); diff --git a/test/presentation/profile/profile_screen_test.dart b/test/presentation/profile/profile_screen_test.dart index 3de6e058..9cfadd7a 100644 --- a/test/presentation/profile/profile_screen_test.dart +++ b/test/presentation/profile/profile_screen_test.dart @@ -10,7 +10,7 @@ import 'package:collaction_app/domain/user/user.dart'; import 'package:collaction_app/presentation/core/collaction_icons.dart'; import 'package:collaction_app/presentation/home/home_screen.dart'; import 'package:collaction_app/presentation/profile/profile_screen.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -93,8 +93,7 @@ void main() { child: MaterialApp.router( color: Colors.white, title: 'CollAction', - routerDelegate: appRouter.delegate(), - routeInformationParser: appRouter.defaultRouteParser(), + routerConfig: appRouter.router, ), ), ); diff --git a/test/presentation/profile/signup_cta_test.dart b/test/presentation/profile/signup_cta_test.dart index f04bf8d6..f8789e4e 100644 --- a/test/presentation/profile/signup_cta_test.dart +++ b/test/presentation/profile/signup_cta_test.dart @@ -1,8 +1,8 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/presentation/profile/widget/signup_cta.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/shared_widgets/pill_button.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../test_helper.dart'; @@ -10,10 +10,10 @@ import '../../utils/user.fixtures.dart'; import '../router.mocks.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); }); group('SignUpCTA tests:', () { @@ -21,7 +21,7 @@ void main() { 'can render no user, PillButton pushes to stack router', (WidgetTester tester) async { await buildAndPump( - tester: tester, widget: SignUpCTA().withRouterScope(stackRouter)); + tester: tester, widget: SignUpCTA().withRouterScope(goRouter)); await tester.pumpAndSettle(); expect( @@ -36,10 +36,7 @@ void main() { await tester.tap(find.byType(PillButton)); await tester.pumpAndSettle(); - final capturedRoutes = - verify(() => stackRouter.push(captureAny())).captured; - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); + verify(() => goRouter.push(AppPage.auth.path)).called(1); }, ); diff --git a/test/presentation/router.mocks.dart b/test/presentation/router.mocks.dart index cfaf8ceb..4dbb6b69 100644 --- a/test/presentation/router.mocks.dart +++ b/test/presentation/router.mocks.dart @@ -1,17 +1,32 @@ -import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; -class MockStackRouter extends Mock implements StackRouter {} +class MockGoRouter extends Mock implements GoRouter {} -class MockNavigationResolver extends Mock implements NavigationResolver {} +class MockGoRouterProvider extends StatelessWidget { + const MockGoRouterProvider({ + required this.goRouter, + required this.child, + Key? key, + }) : super(key: key); -// ignore: avoid_implementing_value_types -class FakePageRouteInfo extends Fake implements PageRouteInfo {} + /// The mock navigator used to mock navigation calls. + final GoRouter goRouter; -extension StackRouterX on Widget { - /// Returns [this] wrapped in a [StackRouterScope] - /// if [stackRouter] is not null. + /// The child [Widget] to render. + final Widget child; + + @override + Widget build(BuildContext context) => InheritedGoRouter( + goRouter: goRouter, + child: child, + ); +} + +extension RouterX on Widget { + /// Returns [this] wrapped in a [MockGoRouterProvider] + /// if [router] is not null. /// Otherwise returns [this]. /// /// Use this when verifying navigation is required in a test case. @@ -21,13 +36,12 @@ extension StackRouterX on Widget { /// home: Scaffold( /// body: WidgetToPump(), /// ), - /// ).withRouterScope(stackRouter); + /// ).withRouterScope(goRouter); /// ``` - Widget withRouterScope(StackRouter? stackRouter) { - return stackRouter != null - ? StackRouterScope( - controller: stackRouter, - stateHash: 0, + Widget withRouterScope(GoRouter? router) { + return router != null + ? MockGoRouterProvider( + goRouter: router, child: this, ) : this; @@ -37,15 +51,15 @@ extension StackRouterX on Widget { /// Route Helpers class RouteHelpers { /// Sets up default routing stubs - static StackRouter setUpRouterStubs() { - registerFallbackValue(FakePageRouteInfo()); - - final StackRouter stackRouter = MockStackRouter(); - when(() => stackRouter.push(any())).thenAnswer((_) async => null); - when(() => stackRouter.replace(any())).thenAnswer((_) async => null); - when(() => stackRouter.replaceNamed(any())).thenAnswer((_) async => null); - when(() => stackRouter.pop()).thenAnswer((_) async => true); + static GoRouter setUpRouterStubs() { + final GoRouter goRouter = MockGoRouter(); + when(() => goRouter.push(any())).thenAnswer((_) async => null); + when(() => goRouter.push(any(), extra: any(named: 'extra'))) + .thenAnswer((_) async => null); + when(() => goRouter.replace(any())).thenAnswer((_) async {}); + when(() => goRouter.replaceNamed(any())).thenAnswer((_) async {}); + when(() => goRouter.pop()).thenAnswer((_) async => true); - return stackRouter; + return goRouter; } } diff --git a/test/presentation/settings/settings_layout_test.dart b/test/presentation/settings/settings_layout_test.dart index bdf4dfe0..cb16bc08 100644 --- a/test/presentation/settings/settings_layout_test.dart +++ b/test/presentation/settings/settings_layout_test.dart @@ -1,7 +1,7 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/presentation/settings/settings_layout.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../router.mocks.dart'; @@ -9,36 +9,36 @@ import '../router.mocks.dart'; part 'settings_layout_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); }); group('SettingsLayout tests:', () { testWidgets('can render', (WidgetTester tester) async { - await tester.pumpSettingsLayout(stackRouter); + await tester.pumpSettingsLayout(goRouter); await tester.pumpAndSettle(); expect(find.byType(SettingsLayout), findsOneWidget); }); testWidgets('back ElevatedButton works', (WidgetTester tester) async { - await tester.pumpSettingsLayout(stackRouter); + await tester.pumpSettingsLayout(goRouter); await tester.pumpAndSettle(); await tester.tap(find.byType(ElevatedButton)); - final capturedPops = verify(() => stackRouter.pop()).callCount; + final capturedPops = verify(() => goRouter.pop()).callCount; expect(capturedPops, 1); }); testWidgets('back RawMaterialButton works', (WidgetTester tester) async { - await tester.pumpSettingsLayout(stackRouter); + await tester.pumpSettingsLayout(goRouter); await tester.pumpAndSettle(); await tester.tap(find.byType(RawMaterialButton)); - final capturedPops = verify(() => stackRouter.pop()); + final capturedPops = verify(() => goRouter.pop()); expect(capturedPops.callCount, 1); }); }); diff --git a/test/presentation/settings/settings_layout_test.ext.dart b/test/presentation/settings/settings_layout_test.ext.dart index 3998c210..a3870062 100644 --- a/test/presentation/settings/settings_layout_test.ext.dart +++ b/test/presentation/settings/settings_layout_test.ext.dart @@ -2,14 +2,14 @@ part of 'settings_layout_test.dart'; extension WidgetX on WidgetTester { Future pumpSettingsLayout([ - StackRouter? stackRouter, + GoRouter? goRouter, ]) async { await pumpWidget( MaterialApp( home: Scaffold( body: SettingsLayout(), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); } } diff --git a/test/presentation/settings/settings_screen_test.dart b/test/presentation/settings/settings_screen_test.dart index 6e87405a..07809e24 100644 --- a/test/presentation/settings/settings_screen_test.dart +++ b/test/presentation/settings/settings_screen_test.dart @@ -1,12 +1,11 @@ import 'dart:io'; -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/application/auth/auth_bloc.dart'; import 'package:collaction_app/application/settings/build_information/build_information_bloc.dart'; import 'package:collaction_app/application/user/profile/profile_bloc.dart'; import 'package:collaction_app/domain/settings/build_information.dart'; import 'package:collaction_app/infrastructure/core/injection.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/settings/settings_screen.dart'; import 'package:collaction_app/presentation/settings/widgets/build_information_tile.dart'; import 'package:flutter/material.dart'; @@ -14,6 +13,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../application/auth/auth_bloc.mocks.dart'; @@ -25,7 +25,7 @@ import '../router.mocks.dart'; part 'settings_screen_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late AuthBloc authBloc; late ProfileBloc profileBloc; late BuildInformationBloc buildInformationBloc; @@ -56,7 +56,7 @@ void main() { }); setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); // Auth Bloc authBloc = MockAuthBloc(); @@ -90,7 +90,7 @@ void main() { group('SettingsPage tests:', () { testWidgets('can render', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); expect(find.byType(SettingsPage), findsOneWidget); @@ -98,49 +98,40 @@ void main() { testWidgets('onTap, "Contact Us" button routes to ContactForm', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.tap(find.text('Contact us')); - final capturedRoutes = verify( - () => stackRouter.push(captureAny()), - ).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); + verify( + () => goRouter.push(AppPage.contactForm.path), + ).called(1); }); testWidgets('onTap, "Onboarding" button routes to Onboarding', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.tap(find.text('Onboarding')); - final capturedRoutes = verify( - () => stackRouter.push(captureAny()), - ).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); + verify( + () => goRouter.push(AppPage.onBoarding.path), + ).called(1); }); testWidgets('onTap, "Open source libraries" button routes to Licenses', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.tap(find.text('Open source libraries')); - final capturedRoutes = verify( - () => stackRouter.push(captureAny()), - ).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); + verify( + () => goRouter.push(AppPage.licenses.path), + ).called(1); }); testWidgets('onTap, "Terms of use" button opens terms webpage', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.runAsync(() async { @@ -148,20 +139,17 @@ void main() { await tester.pumpAndSettle(); }); - final capturedRoutes = verify( - () => stackRouter.push(captureAny()), - ).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); - - final route = capturedRoutes.first as WebViewRoute; - expect(route.args?.url, 'https://www.collaction.org/terms'); + verify( + () => goRouter.push( + AppPage.webView.path, + extra: 'https://www.collaction.org/terms', + ), + ).called(1); }); testWidgets('onTap, "Privacy policy" button opens privacy webpage', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.runAsync(() async { @@ -176,20 +164,17 @@ void main() { await tester.pumpAndSettle(); }); - final capturedRoutes = verify( - () => stackRouter.push(captureAny()), - ).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); - - final route = capturedRoutes.first as WebViewRoute; - expect(route.args?.url, 'https://www.collaction.org/privacy'); + verify( + () => goRouter.push( + AppPage.webView.path, + extra: 'https://www.collaction.org/privacy', + ), + ).called(1); }); testWidgets('"Log out" button not available when not logged in', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); expect( @@ -206,7 +191,7 @@ void main() { when(() => profileBloc.state) .thenAnswer((_) => ProfileState(userProfile: testUserProfile)); - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); await tester.scrollUntilVisible( @@ -218,7 +203,7 @@ void main() { ); await tester.tap(find.text('Log out')); - final capturedPops = verify(() => stackRouter.pop()); + final capturedPops = verify(() => goRouter.pop()); expect(capturedPops.callCount, 1); verify(() => authBloc.add(AuthEvent.signedOut())); @@ -229,7 +214,7 @@ void main() { testWidgets('BuildInformationTile renders when fetched', (WidgetTester tester) async { - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); expect( @@ -247,7 +232,7 @@ void main() { (_) => BuildInformationState.loading(), ); - await tester.pumpSettingsPage(stackRouter); + await tester.pumpSettingsPage(goRouter); await tester.pumpAndSettle(); expect( diff --git a/test/presentation/settings/settings_screen_test.ext.dart b/test/presentation/settings/settings_screen_test.ext.dart index ff0d65bf..e22d0785 100644 --- a/test/presentation/settings/settings_screen_test.ext.dart +++ b/test/presentation/settings/settings_screen_test.ext.dart @@ -2,7 +2,7 @@ part of 'settings_screen_test.dart'; extension WidgetX on WidgetTester { Future pumpSettingsPage([ - StackRouter? stackRouter, + GoRouter? goRouter, ]) async { await pumpWidget( MaterialApp( @@ -19,7 +19,7 @@ extension WidgetX on WidgetTester { child: SettingsPage(), ), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); } } diff --git a/test/presentation/shared_widgets/crowdaction_card_test.dart b/test/presentation/shared_widgets/crowdaction_card_test.dart index dfef0712..ca7f52de 100644 --- a/test/presentation/shared_widgets/crowdaction_card_test.dart +++ b/test/presentation/shared_widgets/crowdaction_card_test.dart @@ -1,13 +1,13 @@ -import 'package:auto_route/auto_route.dart'; import 'package:collaction_app/application/crowdaction/crowdaction_details/crowdaction_details_bloc.dart'; import 'package:collaction_app/application/participation/top_participants/top_participants_bloc.dart'; import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/shared_widgets/crowdaction_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../application/crowdaction/crowdaction_details/crowdaction_details_bloc.mocks.dart'; @@ -18,13 +18,13 @@ import '../router.mocks.dart'; part 'crowdaction_card_test.ext.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late TopParticipantsBloc topParticipantsBloc; late CrowdActionDetailsBloc crowdActionDetailsBloc; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); // Participants Bloc topParticipantsBloc = MockTopParticipantsBloc(); @@ -52,18 +52,11 @@ void main() { 'should go to [CrowdActionDetailsPage] ' 'when the CrowdAction has no password ' 'and [CrowActionCard] is tapped', (tester) async { - await tester.pumpCrowdactionCard(tCrowdactionNoPassword, stackRouter); + await tester.pumpCrowdactionCard(tCrowdactionNoPassword, goRouter); await tester.tap(find.byType(CrowdActionCard)); - final capturedRoutes = - verify(() => stackRouter.push(captureAny())).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); - - final route = capturedRoutes.first as CrowdActionDetailsRoute; - expect(route.args?.crowdAction, tCrowdactionNoPassword); - expect(route.args?.crowdActionId, null); + verify(() => goRouter.push( + AppPage.crowdActionDetailsRoute(tCrowdactionNoPassword.id))).called(1); }); } diff --git a/test/presentation/shared_widgets/crowdaction_card_test.ext.dart b/test/presentation/shared_widgets/crowdaction_card_test.ext.dart index 2ad4dae4..d9861d51 100644 --- a/test/presentation/shared_widgets/crowdaction_card_test.ext.dart +++ b/test/presentation/shared_widgets/crowdaction_card_test.ext.dart @@ -3,7 +3,7 @@ part of 'crowdaction_card_test.dart'; extension WidgetX on WidgetTester { Future pumpCrowdactionCard( CrowdAction crowdAction, [ - StackRouter? stackRouter, + GoRouter? goRouter, ]) async { await pumpWidget( MaterialApp( @@ -12,7 +12,7 @@ extension WidgetX on WidgetTester { crowdAction: crowdAction, ), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); } } diff --git a/test/presentation/shared_widgets/micro_crowdaction_card_test.dart b/test/presentation/shared_widgets/micro_crowdaction_card_test.dart index 982ed1c4..2e09615c 100644 --- a/test/presentation/shared_widgets/micro_crowdaction_card_test.dart +++ b/test/presentation/shared_widgets/micro_crowdaction_card_test.dart @@ -1,11 +1,13 @@ -import 'package:auto_route/auto_route.dart'; +import 'dart:io'; + import 'package:collaction_app/application/crowdaction/crowdaction_details/crowdaction_details_bloc.dart'; -import 'package:collaction_app/presentation/routes/app_routes.gr.dart'; +import 'package:collaction_app/presentation/routes/app_routes.dart'; import 'package:collaction_app/presentation/shared_widgets/micro_crowdaction_card.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; import 'package:mocktail/mocktail.dart'; import '../../application/crowdaction/crowdaction_details/crowdaction_details_bloc.mocks.dart'; @@ -13,11 +15,11 @@ import '../../test_utilities.dart'; import '../router.mocks.dart'; void main() { - late StackRouter stackRouter; + late GoRouter goRouter; late CrowdActionDetailsBloc crowdActionDetailsBloc; setUpAll(() { - stackRouter = RouteHelpers.setUpRouterStubs(); + goRouter = RouteHelpers.setUpRouterStubs(); // Crowdaction Details Bloc crowdActionDetailsBloc = MockCrowdActionDetailsBloc(); @@ -27,6 +29,8 @@ void main() { // dot env dotenv.testLoad(fileInput: tDotEnv); + + HttpOverrides.global = null; }); tearDownAll(() { @@ -44,20 +48,16 @@ void main() { tCrowdactionNoPassword, ), ), - ).withRouterScope(stackRouter), + ).withRouterScope(goRouter), ); await tester.tap(find.byType(MicroCrowdActionCard)); await tester.pumpAndSettle(); - final capturedRoutes = - verify(() => stackRouter.push(captureAny())).captured; - - expect(capturedRoutes.length, 1); - expect(capturedRoutes.first, isA()); - - final route = capturedRoutes.first as CrowdActionDetailsRoute; - expect(route.args?.crowdAction, null); - expect(route.args?.crowdActionId, tCrowdactionNoPassword.id); + verify( + () => goRouter.push( + AppPage.crowdActionDetailsRoute(tCrowdactionNoPassword.id), + ), + ).called(1); }); }