Filip Brablec
Filip Brablec

Reputation: 11

Flutter - go_route, shellroute with , popscope problem with bottomnavigation

I have a problem for which I haven't seen a solution anywhere yet.

I'm using go_route for routing, where I also use shellroute to generate a bottomnavigatorbar in some given paths, but I want it to work like for example on Instagram, so that when I have the functionality of a given page where the bottomnavigatorbar is generated, I want it to remember the given paths where it was before, so that it doesn't follow exactly the structure of go_route, so that when a user goes for example to /, then to /search and then to /profile, so when he clicks the back button, it first moves him to /search and then to /, but once he's in the / path, for example, he clicks on /search, again on /, so the back button from the / directory moves him to /search, just where he was before, I solve this in the bottomnavigation back_button_interceptor, where I check the back button and the popscope function, when I finally shut down the application, but the problem occurs when I'm in the / directory

This is a piece of my go_route `

const String rootRoute = '/';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey();

final GoRouter router = GoRouter(
    debugLogDiagnostics: true,
    errorBuilder: (context, state) => ErrorScreen(state.error),
    initialLocation: rootRoute,
    navigatorKey: navigatorKey,

    routes: <RouteBase>[
       ShellRoute(
        //navigatorKey: _rootNavigatorKey,
        builder: (BuildContext context, GoRouterState state, Widget child) {
          return MultiBlocProvider(
            providers: [
              BlocProvider.value(value: _authBloc),
              BlocProvider.value(value: _authorizedTokenCubit),
              BlocProvider.value(value: _signInUserCubit),
            ],
            child: BlocBuilder<AuthorizedTokenCubit, AuthorizedTokenState>(
              builder: (context, authState) {
                if (authState is AuthorizedTokenSuccess) {
                 return BlocBuilder<SignInUserCubit, SignInUserState>(
                    builder: (context, SignInState) {

                      if (state.uri.path == '/' || state.uri.path == '/search' || (SignInState is SignInUserSuccess && state.uri.pathSegments.length == 1)) {
                        return ScaffoldWithNavBar(child: child);
                      } else {
                        return child; // No ScaffoldWithNavBar for WelcomePage
                      }
                    }
                  );
                  return child;
                } else {
                  return child; // No ScaffoldWithNavBar for WelcomePage
                }
              },
            ),
          );
        },
        routes: <RouteBase>[
        GoRoute(
          path: '/',
          builder: (BuildContext context, GoRouterState state) {
            return MultiBlocProvider(
                providers: [
                  BlocProvider.value(value: _authBloc),
                  BlocProvider.value(value: _authorizedTokenCubit),
                ],
                child: BlocBuilder<AuthorizedTokenCubit, AuthorizedTokenState>(
                    builder: (context, state) {
                  if (state is AuthorizedTokenSuccess) {
                    return HomePage();
                  } else {
                    return WelcomePage();
                  }
                }));
          },
          routes: <RouteBase>[
            GoRoute(
                path: 'signin',
                builder: (BuildContext context, GoRouterState state) {
                  return MultiBlocProvider(providers: [
                    BlocProvider.value(value: _authBloc),
                  ], child: SignInPage());
                },
                routes: [
                  GoRoute(
                    path: 'forgottenpassword',
                    builder: (BuildContext context, GoRouterState state) {
                      return MultiBlocProvider(
                          providers: [
                            BlocProvider.value(value: _authBloc),
                            BlocProvider.value(value: _authorizedTokenCubit),
                          ],
                          child: BlocBuilder<AuthorizedTokenCubit,
                              AuthorizedTokenState>(builder: (context, state) {
                            return ForgottenPasswordPage();
                          }));
                    },
                  ),
                ]),
            
            GoRoute(
              path: 'forgottenpasswordlink',
              builder: (BuildContext context, GoRouterState state) {
                return MultiBlocProvider(providers: [
                  BlocProvider.value(value: _authBloc),
                  BlocProvider.value(value: _injectDataCubit),

                ], child: ForgottenPasswordLinkPage());
              },
            ),
             GoRoute(
              path: 'search',
              builder: (BuildContext context, GoRouterState state) {
                // return MultiBlocProvider(providers: [
                //   BlocProvider.value(value: _searchCubit),
                // ], child: Search());
                return Search();
              },
            ),
           );

Where I implement ScaffoldWithNavBar which is bottomnavigation, this works fine for me, it creates where it should, the problem is though, in battomnavigation.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:back_button_interceptor/back_button_interceptor.dart';

import '../../router/models/routes.dart';

class ScaffoldWithNavBar extends StatefulWidget {
  /// Constructs an [ScaffoldWithNavBar].
  final Widget child;

  const ScaffoldWithNavBar({
    required this.child,
    super.key,
  });

  /// The widget to display in the body of the Scaffold.
  /// In this sample, it is a Navigator.

  @override
  _ScaffoldWithNavBarState createState() => _ScaffoldWithNavBarState();
}
List<String> navigationHistory = ['/'];

class _ScaffoldWithNavBarState extends State<ScaffoldWithNavBar> {
  int _selectedIndex = 0;
  
  @override
  void initState() {
    super.initState();
    // print('hoj');
    //  print(widget.child);

    BackButtonInterceptor.add(myInterceptor);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _calculateSelectedIndex(context).then((index) {
      setState(() {
        _selectedIndex = index;
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[   
          PopScope(
            canPop: false,
            child: TextButton(
              onPressed: () async {
                // Your onPressed code here
              },
              child: Text('data'),
            ),
          ),
          Expanded(child: widget.child)
        ],
      ),
      
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.search),
            label: 'Find',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person),
            label: 'My profile',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.blue, // Set the color for the selected item
        //unselectedItemColor: Colors.grey, // Set the color for unselected items
        onTap: (int idx) => _onItemTapped(idx, context),
      ),
    );
  }

bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
    print("BACK BUTTON!"); // Do some stuff.
     // Get and remove the last item from navigationHistory
    
    if (navigationHistory.isNotEmpty) {
      String lastPath = navigationHistory.last;
      print(lastPath);
      GoRouter.of(context).go(lastPath);
      navigationHistory.removeLast();
    }else{
     // SystemNavigator.pop();
    }
    print(navigationHistory);


    return true;
  }

  Future<int> _calculateSelectedIndex(BuildContext context) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? username = prefs.getString('username');
    final String location = GoRouterState.of(context).uri.path;
    if (location == '/') {
      return 0;
    }
    if (location == '/search') {
      return 1;
    }
    if(username != null){
      if (location.contains(username)) {
        return 2;
      }else{
        return 0;
      }
    }
    return 0;
  }

  void _onItemTapped(int index, BuildContext context) async{
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? username = prefs.getString('username');
    final String currentPath = GoRouterState.of(context).uri.path;
    print(currentPath);
    setState(() {
      _selectedIndex = index;

      // Ensure '/' is always the first entry in navigationHistory
      if (!navigationHistory.contains('/')) {
        navigationHistory.insert(0, '/');
      }

      // Remove currentPath if it already exists in navigationHistory to ensure uniqueness
      if(currentPath != '/'){
        navigationHistory.remove(currentPath);
      }

      // Insert currentPath at the beginning of navigationHistory
      if (currentPath != '/') {
        if (username != null)
        if(!((index == 1 && currentPath == RoutePaths.search) || (index == 2 && currentPath == '/' + username))){
          navigationHistory.insert(1, currentPath); // Insert after '/' if it's not already there
        }
      }

      // Limit navigationHistory to 3 unique paths
      if (navigationHistory.length > 3) {
        navigationHistory.removeLast();
      }
    });
    print(navigationHistory);

    switch (index) {
      case 0:
        GoRouter.of(context).go(RoutePaths.home);
        break;
      case 1:
        GoRouter.of(context).go(RoutePaths.search);
        //context.pushReplacement(RoutePaths.search);
        //GoRouter.of(context).go(RoutePaths.search);
        break;
      case 2:
        if (username != null) {
         context.push('/' + username);
        } else {
          GoRouter.of(context).go(RoutePaths.home);
        }
        break;
    }
  }
}

Here you can see my bottomnavigation code but the strange thing is that the whole function works as it should, when I comment out where I am inserting the page I am on, then it works as it should and in the current context the application doesn't close, but as soon as I include widget.child in there it doesn't work Expanded(child: widget.child)

I've tried many options but no matter what I've searched and tried different options, nothing works as it should, I use go_router: ^12.1.3

Upvotes: 1

Views: 184

Answers (0)

Related Questions