Search code examples
flutterdartsidebarnavigator

Flutter persistent sidebar


In my application I want to have a sidebar that allows me to have access to specific functions everywhere in my application.


What I want :

  • That the sidebar remains visible when I push my pages
  • That I can pushNamed route or open a modal with one of the sidebar functions
  • That I can not display the sidebar on certain pages

What I do :

enter image description here

In red, the persistent sidebar and in yellow my app content.

enter image description here

If I click on my profil button in the HomeView, the ProfilView is displayed and my sidebar remains visible so it's ok

enter image description here

My AppView :

class AppView extends StatelessWidget {
  const AppView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: AppConfig.kAppName,
      debugShowCheckedModeBanner: false,
      theme: AppTheme().data,
      builder: (context, child) => SidebarTemplate(child: child), // => I create a template 
      onGenerateRoute: RouterClass.generate,
      initialRoute: RouterName.kHome,
    );
  }

My SidebarTemplate : (Display the sidebar and load the page with my router)

class SidebarTemplate extends StatelessWidget {
  final Widget? child;
  const SidebarTemplate({Key? key, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body : Row(
          children: [
            SidebarAtom(), // => My sidebar Widget
            Expanded(
              child: ClipRect(
                  child: child! // => My view
              ),
            )
          ],
        )
      ),
    );
  }
}

My RouterClass :

abstract class RouterClass{

  static Route<dynamic> generate(RouteSettings settings){
    final args = settings.arguments;

    switch(settings.name){

      case RouterName.kHome:
        return MaterialPageRoute(
            builder: (context) => HomeView()
        );

      case RouterName.kProfil:
        return MaterialPageRoute(
            builder: (context) => ProfilView(title: "Profil",)
        );

      default:
        return MaterialPageRoute(
            builder: (context) => Error404View(title: "Erreur")
        );
    }
  }
}

How to do :

  • To pushNamed or open a modal with a button from my sidebar because I have an error
The following assertion was thrown while handling a gesture:
I/flutter (28519): Navigator operation requested with a context that does not include a Navigator.
I/flutter (28519): The context used to push or pop routes from the Navigator must be that of a widget that is a
I/flutter (28519): descendant of a Navigator widget.
  • To hide the sidebar when I want like SplashScreen for example

Any guidance on the best way to accomplish this would be appreciated.


Solution

  • You can use a NavigatorObserver to listen to the changes in the route.

    class MyNavObserver with NavigatorObserver {
      final StreamController<int> streamController;
    
      MyNavObserver({required this.streamController});
    
      @override
      void didPop(Route route, Route? previousRoute) {
        if (previousRoute != null) {
          if (previousRoute.settings.name == null) {
            streamController.add(3);
          } else {
            streamController
                .add(int.parse(previousRoute.settings.name!.split('/').last));
          }
        }
      }
    
      @override
      void didPush(Route route, Route? previousRoute) {
        if (route.settings.name == null) {
          streamController.add(3);
        } else {
          streamController.add(int.parse(route.settings.name!.split('/').last));
        }
      }
    }
    

    and using StreamController you can make changes to your SidebarTemplate by putting it inside StreamBuilder. This will take care of all the requirements you have mentioned in the question.

    Check out the live example here.