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:
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')),
);
}
}
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(...);
}