Home > Net >  Flutter Toggle overlay widget from every screen
Flutter Toggle overlay widget from every screen

Time:06-15

I have a problem. In my main.dart I have a IndexedStack with a bottomNavigationBar. The IndexedStack exists of 4 screens, and one of the screens contains a Navigator inside that screen. Now in the bottomNavigationBar, I have a custom bar like this: enter image description here

I want the Navigator to switch screens when I click on that bar, but because that custom bottom bar is not part of the Navigator, I can't call:

Navigator.pushNamed(context, 'location_details');

Because that results in the following error:

FlutterError (Could not find a generator for route RouteSettings("location_details", null) in the _WidgetsAppState.
Make sure your root app widget has provided a way to generate
this route.
Generators for routes are searched for in the following order:
 1. For the "/" route, the "home" property, if non-null, is used.
 2. Otherwise, the "routes" table is used, if it has an entry for the route.
 3. Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes".
 4. Finally if all else fails onUnknownRoute is called.
Unfortunately, onUnknownRoute was not set.)

Here is the main.dart code:

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    final screens = [
      LocationStateManager(),
      MySessionsPage(),
      MapPage(),
      SettingsPage()
    ];

    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title,
              style: TextStyle(
                  color: PRIMARY,
                  fontSize: 32,
                  fontFamily: 'JotiOne',
                  fontWeight: FontWeight.bold)),
          centerTitle: true,
          backgroundColor: DARK_BACKGROUND_PRIMARY,
        ),
        body: SafeArea(
          child: IndexedStack(index: currentIndex, children: screens),
        ),
        bottomNavigationBar: new Theme(
          data:
              Theme.of(context).copyWith(canvasColor: DARK_BACKGROUND_PRIMARY),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              (isVisible)
                  ? BottomBarTraveling()
                  : SizedBox(
                      height: 0,
                    ),
              BottomNavigationBar(
                type: BottomNavigationBarType.fixed,
                iconSize: 30,
                currentIndex: currentIndex,
                onTap: (index) => setState(() => currentIndex = index),
                showSelectedLabels: false,
                showUnselectedLabels: false,
                selectedItemColor: NAVBAR_SELECTED,
                unselectedItemColor: NAVBAR_UNSELECTED,
                items: [
                  BottomNavigationBarItem(icon: Icon(Icons.home), label: ''),
                  BottomNavigationBarItem(icon: Icon(Icons.list), label: ''),
                  BottomNavigationBarItem(icon: Icon(Icons.map), label: ''),
                  BottomNavigationBarItem(icon: Icon(Icons.settings), label: '')
                ],
              )
            ],
          ),
        ));
  }
}

And here is the LocationStateManager code which contains the Navigator:

class LocationStateManager extends StatefulWidget {

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

class _LocationStateManagerState extends State<LocationStateManager> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: null,
        body: Stack(children: <Widget>[
          Navigator(
            onGenerateRoute: (settings) {
              switch (settings.name) {
                case 'location_list':
                  return MaterialPageRoute(
                    builder: (context) => LocationListPage(),
                  );
                case 'location_details':
                  return MaterialPageRoute(
                    builder: (context) =>
                        KiteupLocationPage(),
                  );
                case 'kiteup_status_page':
                  return MaterialPageRoute(
                    builder: (context) => KiteupStatusPage(),
                  );
                default:
                  return MaterialPageRoute(
                    builder: (context) => LocationListPage(),
                  );
              }
            },
          ),
        ]));
  }
}

How can I change the Navigator route to location_details, by clicking on the BottomBarTraveling() bar, that is placed a level above the Navigator.

PS: Inside the BottomBarTraveling() is the gesturedetector which has the

onTap: () { Navigator.pushNamed(context, 'location_details'); }

CodePudding user response:

Navigator is default navigator of flutter, in this case you want to deal with custom navigator (or neste navigator), you must decrate your ow navigator and use it insteads of Navigator. The point is create navigator = GlobalKey<NavigatorState>() and passing to key props of Navigator.

Update: Of course any function of navigator like pop/push/pushName on your custom navigator need using navigator instead of Navigator.

Example: A simple app with button out of Navigator and 2 screen with differences color inside Navigator, push button and Navigator was changed.

import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: MyHomePage()));

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static final navigator = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Expanded(
            child: Navigator(
              key: navigator,
              initialRoute: '/',
              onGenerateRoute: (settings) {
                switch (settings.name) {
                  case '/':
                    return MaterialPageRoute(builder: (_) => const Sc1());
                  case 'sc2':
                  default:
                    return MaterialPageRoute(builder: (_) => const Sc2());
                }
              },
            ),
          ),
          ElevatedButton(
            child: const Text('To Sc2'),
            onPressed: () {
              navigator.currentState?.pushNamed('sc2');
            },
          ),
        ],
      ),
    );
  }
}

// screen 1 with green color
class Sc1 extends StatelessWidget {
  const Sc1({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.green,
      child: const Center(child: Text('Screen 1')),
    );
  }
}

// screen 2 with blue color
class Sc2 extends StatelessWidget {
  const Sc2({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      child: const Center(child: Text('Screen 2')),
    );
  }
}

enter image description here

Addtional: In case you want to calling navigator outside of MyHomePage, you can declare an static like this and calling it from child nodes


class MyHomePage extends StatefulWidget {
  static MyHomePageState of(BuildContext context) =>
      context.findAncestorStateOfType<MyHomePageState>()!;
  ...
}
call() {
  MyHomePage.of(context).navigator.push(...);
}
  • Related