Flutter Web Navigation with persistent drawer and appbar

5,659

Because Fluro redirects without persistence, you may have to have each page have a Scaffold with an AppBar and Drawer, most efficient way would to have a custom scaffold you could re-use.

import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';

void main() {
  FluroRouter.setupRouter();
  runApp(
    MaterialApp(
      title: 'Your website',
      onGenerateRoute: FluroRouter.router.generator
    ),
  );
}

class CustomScaffold extends StatelessWidget {
  final Widget body;
  CustomScaffold({this.body});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //your appbar here
      appBar: AppBar(),
      //your drawer here
      drawer: Drawer(),
      body: body,
    );
  }
}

class FluroRouter {
  static Router router = Router();
  static Handler _routeOneHandler = Handler(
      handlerFunc: (BuildContext context, Map<String, dynamic> params) => PageOne());
  static Handler _routeTwoHandler = Handler(
      handlerFunc: (BuildContext context, Map<String, dynamic> params) => PageTwo());
  static void setupRouter() {
    router.define(
      '/',
      handler: _routeOneHandler,
    );
    router.define(
      '/two',
      handler: _routeTwoHandler,
    );
  }
}

class PageOne extends StatelessWidget {
  Widget build(BuildContext context) {
    return CustomScaffold(
      body: Container(child: Text('Page 1')),
    );
  }
}

class PageTwo extends StatelessWidget {
  Widget build(BuildContext context) {
    return CustomScaffold(
      body: Container(child: Text('Page 2')),
    );
  }
}
Share:
5,659
Aerofluxx
Author by

Aerofluxx

Updated on December 20, 2022

Comments

  • Aerofluxx
    Aerofluxx over 1 year

    I'm having still no success with my Flutter Web App, doing an URL based routing and keeping things like the appbar and the drawer persistent.

    The goal I'm trying to achieve is, that I can only modify the content of my App. So i.e. if the user enters the url $baseurl/#/ it should navigate to the content of page one. $baseurl/#/two to the content of page two, and so on...

    My main file is something like this. As you can see it's done with Fluro Router and I tried so simplify everything to see my main goal.

    import 'package:flutter/material.dart';
    import 'package:newnavigationtest/routes.dart';
    
    void main() {
      FluroRouter.setupRouter();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      final navigatorKey = GlobalKey<NavigatorState>();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: MainLayout(),
        );
      }
    }
    
    class MainLayout extends StatelessWidget {
      final navigatorKey = GlobalKey<NavigatorState>();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Row(
            children: <Widget>[
              Column(
                children: [
                  Expanded(
                    child: Container(
                      color: Colors.green,
                      width: 100,
                      child: Text("Static sidebar"),
                    ),
                  ),
                ],
              ),
              Expanded(
                child: Navigator(
                  key: navigatorKey,
                  initialRoute: '/',
                  onGenerateRoute: FluroRouter.router.generator,
                ),
              )
            ],
          ),
        );
      }
    }
    

    The FluroRouter file looks like this

    import 'package:fluro/fluro.dart';
    import 'package:flutter/material.dart';
    
    class FluroRouter {
      static Router router = Router();
      static Handler _routeOneHandler = Handler(
          handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
              Container(child: Center(child: Text("Later content for page 1"))));
      static Handler _routeTwoHandler = Handler(
          handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
              Container(child: Center(child: Text("Later content for page 2"))));
      static void setupRouter() {
        router.define(
          '/',
          handler: _routeOneHandler,
        );
        router.define(
          '/two',
          handler: _routeTwoHandler,
        );
      }
    }
    

    Clearly, if the user enters a URL nothing happens at the moment. I think this is because the main navigator in the MaterialApp is consuming the input from the user and not passing it through to my Navigator in the Scaffold. But I'm absolutely not sure.

    Can anybody point me the right way to achieve the wanted behavior?

    Thanks!

    Update 1:

    I also tried something like using the builder to build the child and keep the rest persistent. (This leads me to the behavior I'm looking for, except for the OverLay Error)

    import 'package:flutter/material.dart';
    import 'package:newnavigationtest/routes.dart';
    
    void main() {
      FluroRouter.setupRouter();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      final navigatorKey = GlobalKey<NavigatorState>();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          key: navigatorKey,
          initialRoute: '/',
          onGenerateRoute: FluroRouter.router.generator,
          builder: (context, child) {
            return MainLayout(child: child);
          },
        );
      }
    }
    
    class MainLayout extends StatelessWidget {
      final Widget child;
    
      MainLayout({@required this.child});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Row(
            children: <Widget>[
              Column(
                children: [
                  Expanded(
                    child: Container(
                      color: Colors.green,
                      width: 100,
                      child:
                          Tooltip(message: "Hello", child: Text("Static sidebar")),
                    ),
                  ),
                ],
              ),
              Expanded(
                child: child,
              )
            ],
          ),
        );
      }
    }
    

    But this leads to a missing Overlay at the tooltip.

    ════════ Exception caught by widgets library ═══════════════════════════════════
    The following assertion was thrown building Tooltip("Hello", dirty, state: _TooltipState#93c0f(ticker inactive)):
    No Overlay widget found.
    
    Tooltip widgets require an Overlay widget ancestor for correct operation.