Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Web Navigation with persistent drawer and appbar

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.
like image 556
Aerofluxx Avatar asked May 17 '20 10:05

Aerofluxx


People also ask

How do you open the drawer from another widget in Flutter?

It can be opened via swipe gesture or by clicking on the menu icon in the app bar. Typically, the navigation drawer opens up from the left side of the screen, but you can also configure it to open from the right side (for the LTR text settings).

How do you show the drawers in all pages in Flutter?

To share a drawer between all pages we could add a builder in our MaterialApp instance. This will instantiate a Scaffold under Navigator but above all routes. Inside your page, you can instantiate another Scaffold without restriction as you'd usually do.

How do you use drawer without AppBar Flutter?

You have your own custom Menu button to open/close drawer. You don't want to use AppBar as well. In that case you can use GlobalKey<ScaffoldState>() object to open Drawer.


Video Answer


1 Answers

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')),
    );
  }
}
like image 53
Jackson Lee Avatar answered Nov 11 '22 06:11

Jackson Lee