Handling route , flutter gorouter

51 Views Asked by At

I am stuck at a problem where I am using GoRouter package for routing and for bottom navigation bar I am using a StatefullShellBranch.

Current behaviour is I have 4 navigation branches, and all are working well. Whenever screen I am when back is pressed it closes the app, whether I am on the home or profile or the chat or any where on the bottom navigation bar .

Expected behaviour
What I want is that if the user is on the bottom navigation bar on the home index I want it the user press back or use gestures it should show a popUp like whether to close the app or if the user is another branch or index on the bottom navigation bar when is press back it should go to the home index or the home branch Can anyone help me ..? With this

I have use the Pop Scope but it is not working..

I have use the Pop Scope and the onExit property of the goRoute but it is not working.

I would like to add that when using the StatefullShellBranch when I use the onExit property and creating the method as onExitPopUp, it works when I press back but when i navigate on the navigation branch it ask the same would you like to close the app I think when I changed the branch in the bottom navigation bar it act like closing the app

This is the route code


this is the bottom nav bar code

enum UserType { influencer, brand }

class MyCustomBottomNavigationBar extends StatefulWidget {
  MyCustomBottomNavigationBar({
    required this.navigationShell,
    required this.bottomNavNavigationHandlerCubit,
    required this.userType,
    super.key,
  });

  final StatefulNavigationShell navigationShell;
  final BottomNameNavigationHandlerCubit bottomNavNavigationHandlerCubit;
  final UserType userType;

  static Widget create({
    required StatefulNavigationShell navigationShell,
    required UserType userType,
  }) {
    return BlocProvider<BottomNameNavigationHandlerCubit>(
      create: (context) => BottomNameNavigationHandlerCubit(),
      child: Consumer<BottomNameNavigationHandlerCubit>(
        builder: (_, BottomNameNavigationHandlerCubit cubit, __) =>
            MyCustomBottomNavigationBar(
          navigationShell: navigationShell,
          bottomNavNavigationHandlerCubit: cubit,
          userType: userType,
        ),
      ),
    );
  }

  @override
  MyCustomBottomNavigationBarState createState() =>
      MyCustomBottomNavigationBarState();
}

class MyCustomBottomNavigationBarState
    extends State<MyCustomBottomNavigationBar> {
  void _goBranch(int index) {
    widget.navigationShell.goBranch(
      index,
      initialLocation: index == widget.navigationShell.currentIndex,
    );
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    List<IconData> icons = [];
    List<String> labels = [];

    // Update icons and labels based on user type
    if (widget.userType == UserType.influencer) {
      icons = [
        Icons.home_rounded,
        Icons.play_arrow_rounded,
        Icons.message_rounded,
        Icons.person_rounded,
      ];
      labels = ['Home', 'Reels', 'Messages', 'Profile'];
    } else if (widget.userType == UserType.brand) {
      icons = [
        Icons.home_rounded,
        Icons.contacts_rounded,
        Icons.message_rounded,
        Icons.person_rounded,
      ];
      labels = ['Home', 'Phonebook', 'Messages', 'Profile'];
    }
    return PopScope(
      canPop: true,
      onPopInvoked: (value){
        debugPrint('onPOPInvoke on botton-------------$value');
        if(!value){
          context.goNamed(RouteStrings.homeScreen);
        }
      },
      child: Scaffold(
        backgroundColor: AppStyle.colors.background,
        extendBodyBehindAppBar: true,
        body: SafeArea(
          child: SizedBox.expand(
            child: widget.navigationShell,
          ),
        ),
        bottomNavigationBar: Container(
          height: size.width * .155,
          child: BlocBuilder<BottomNameNavigationHandlerCubit, int>(
            builder: (context, state) {
              return ListView.builder(
                itemCount: 4,
                scrollDirection: Axis.horizontal,
                padding: EdgeInsets.symmetric(horizontal: size.width * .024),
                itemBuilder: (context, index) => InkWell(
                  onTap: () {
                    debugPrint('tapped index : $index');
                    context.read<BottomNameNavigationHandlerCubit>().updateValue(
                          value: index,
                        );
                    if (widget.userType == UserType.brand) {
                      if (index == 1) {
                        _goBranch(4);
                      } else {
                        _goBranch(index);
                      }
                    } else {
                      _goBranch(index);
                    }
                  },
                  splashColor: Colors.transparent,
                  highlightColor: Colors.transparent,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      SizedBox(height: size.width * .014),
                      Icon(icons[index],
                          size: size.width * .076, color: Colors.grey.shade500),
                      AnimatedContainer(
                        duration: const Duration(milliseconds: 1500),
                        curve: Curves.fastLinearToSlowEaseIn,
                        margin: EdgeInsets.only(
                          top: index == state ? 0 : size.width * .029,
                          right: size.width * .0422,
                          left: size.width * .0422,
                        ),
                        width: size.width * .153,
                        height: index == state ? size.width * .014 : 0,
                        decoration: BoxDecoration(
                          color: Colors.grey.shade500,
                          borderRadius: BorderRadius.vertical(
                            top: Radius.circular(20),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

this is the profile code i.e

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({super.key});

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {

  @override
  void dispose() {
    if(context.mounted){
      context.pushNamed(RouteStrings.homeScreen);
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      // canPop: showExitConfirmation(context),
      canPop: false,
      onPopInvoked: (value){
        debugPrint('onPOPInvoke-------------$value');
        if(!value){
          showExitConfirmation(context);
          context.goNamed(RouteStrings.homeScreen);
        }
      },
      child: const Scaffold(
        backgroundColor: Colors.white,
        body: Center(
          child: Text('Profile Screen'),
        ),
      ),
    );
  }
}

if any things require let me know

1

There are 1 best solutions below

6
pcba-dev On

Your problem is not really related to GoRouter or how you use StatefullShellBranch, but to how Android an iOS handle pop/back gestures.

You shall:

  • Handle back gestures: This can be done by using a BackButtonInterceptor callback. (You do not really need to set android:enableOnBackInvokedCallback="true" as it is indicate on the package documentacion for it to work)
  • Disable pop: Wrap you widgets with PopScope and defined a predictive criteria which will indicate whether they can be popped.
  • Defined a behavior for back buttons: Implement a custom callback which programatically pops or navigates.

In your case solution could be to have a widget class ShelledPage which is then extended by you routes:

abstract class ShelledPage extends StatefulWidget {
  const ShelledPage({super.key, this.isHome = false});

  final bool isHome;

  @override
  ShelledPageState<ShelledPage> createState();
}

abstract class ShelledPageState<T extends ShelledPage> extends State<T> {
  @override
  void initState() {
    super.initState();
    BackButtonInterceptor.add(_backButtonInterceptor);
  }

  @override
  void dispose() {
    BackButtonInterceptor.remove(_backButtonInterceptor);
    super.dispose();
  }

  bool _backButtonInterceptor(final bool stop, final RouteInfo routeInfo) {
    if (widget.isHome) {
      // Do NOT stop the back gesture.
      return false;
    } else {
      // Navigate to the home page and DO stop back gesture.
      context.goNamed('home');
      return true;
    }
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: widget.isHome,
      child: buildWidget(context),
    );
  }

  Widget buildWidget(BuildContext context);

  /// Callback for the back button.
  @protected
  void onBackPressed(BuildContext context) {
    if (widget.isHome) {
      // Close the app.
      SystemNavigator.pop();
    } else {
      context.goNamed('home');
    }
  }
}