Flutter Navigator.popUntil failed assertion _debugLocked. What's wrong? - flutter

^ ^ ^^ ^ NO! THIS QUESTION DOES NOT HAVE AN ANSWER THERE! ^ ^ ^ ^ ^ ^
I'm having problems with Navigator.popUntil. I wrote a small demo app to show what's happening. Before I post this as a bug, am I using popUntil wrong??
call to popUntil displays
It looks like something is locking up the Navigator (setting _debugLocked) and not releasing it.
main.dart below : (can just be pasted into the Flutter demo app)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Routing Test Page'),
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => MyHomePage(title: 'Home Page',),
settings: settings,
);
case '/home':
return MaterialPageRoute(
builder: (_) => MyHomePage(title: 'Home Page',),
settings: settings,
);
case '/middlepage':
return MaterialPageRoute(
builder: (_) => MiddlePage(),
settings: settings,
);
case '/bottompage':
return MaterialPageRoute(
builder: (_) => BottomBage(),
settings: settings,
);
default:
return null;
}
},
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Home Page',
),
OutlineButton(
child: Text('push MiddlePage()'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => (MiddlePage())),
),
),
OutlineButton(
child: Text('pushNamed ''/middlepage'''),
onPressed: () => Navigator.pushNamed(context, '/middlepage'),
),
],
),
),
);
}
}
class MiddlePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Middle Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Middle Page',style: TextStyle(fontWeight: FontWeight.bold)
),
OutlineButton(
child: Text('push BottomPage()'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => (BottomBage())),
),
),
OutlineButton(
child: Text('pushNamed ''/bottompage'''),
onPressed: () => Navigator.pushNamed(context, '/bottompage'),
),
OutlineButton(
child: Text('pop'), onPressed: () => Navigator.pop(context)),
OutlineButton(
child: Text('popUntil ''/home'''),
onPressed: () =>
Navigator.popUntil(context, ModalRoute.withName('/home'))),
],
),
),
);
}
}
class BottomBage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Bottom Page', style: TextStyle(fontWeight: FontWeight.bold),
),
OutlineButton(
child: Text('push ''home'''),
onPressed: () =>
Navigator.pushNamed(context, '/home'),
),
OutlineButton(
child: Text('pop'),
onPressed: () =>
Navigator.pop(context)),
OutlineButton(
child: Text('popUntil ''/home'''),
onPressed: () =>
Navigator.popUntil(context, ModalRoute.withName('/home')),
),
OutlineButton(
child: Text('popUntil ''/home'' (with Are You Sure box)) '),
onPressed: () async {
try {
if (await _areYouSureDialog(context)) {
Navigator.popUntil(context, ModalRoute.withName('/home'));
}
} catch (e) {
debugPrint("exception: $e");
}
},
),
],
),
),
);
}
}
Future<bool> _areYouSureDialog(BuildContext context) async {
return await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Pop back?'),
content: const Text('Are you sure?'),
actions: <Widget>[
FlatButton(
child: const Text('YES'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
FlatButton(
child: const Text('NO'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
],
);
},
) ??
false;
}

I recreated your case. Whenever we want to pop navigation to the home screen (root) from anywhere, there are couple of ways to do it as below:
1.Using .isFirst method:
Navigator.of(context).popUntil((route) => route.isFirst);
Using defaultRouteName:
Navigator.popUntil(context, ModalRoute.withName(Navigator.defaultRouteName));
By providing the context first, the route will ensure that the navigation will pop to the default always.
You can try with either approach. I tested at my end and it works well.
Hope this answers your question.

Related

Flutter showDialog not working on a simple test

I am trying flutter and have problems in making a simple showdialog work. I tried a simple test with one button:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter Test',
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
title: Text('Flutter'),
),
body: Center(
child: ListView(
padding: EdgeInsets.all(8),
children: <Widget>[
Container(
child: RaisedButton(
child: Text('My Button'),
onPressed: () => {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: Text('Test'),
content: Text('Dialog content'),
);
},
),
},
color: Colors.cyan,
textColor: Colors.white,
),
),
],
),
),
),
);
}
}
I expect the alert to pop on button tap. What am I missing? I also tried it with the showdialog in a separate custom function call, same result.
You need to use the showDialog method provided by Flutter, as seen on the example here. Check my example below with your button but using the showDialog method:
class DialogIssue extends StatefulWidget {
#override
_DialogIssueState createState() => _DialogIssueState();
}
class _DialogIssueState extends State<DialogIssue> {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('My Button'),
onPressed: () => _confirmDialog(),
color: Colors.cyan,
textColor: Colors.white,
),
);
}
Future<void> _confirmDialog() async {
switch (await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('True or false'),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SimpleDialogOption(
onPressed: () { Navigator.pop(context, true); },
child: const Text('Confirm',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
SimpleDialogOption(
onPressed: () { Navigator.pop(context, false); },
child: const Text('Cancel'),
),
],
),
],
);
}
)){
case true:
print('Confirmed');
break;
case false:
print('Canceled');
break;
default:
print('Canceled');
}
}
}
It can be done in a StatelessWidget, like in this DartPad pad.
Sidenote: I've had to use a Builder because the context in MyApp's build method doesn't have a MaterialApp ancestor.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
showAppDialog(BuildContext context) {
print("Showing app dialog");
showDialog(context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"This is a dialog that works.",
),
icon: const Icon(Icons.delete),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("OK"),
),
],
);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(body: SafeArea(child: Builder(
builder: (context) {
return TextButton(child: Text("Show dialog"), onPressed: () => showAppDialog(context),);
}
))),
);
}
}
PS: You're already using showDialog, why does this answer suggest you to do that 🤔.

What is the use of `rootNavigator` in Navigator.of(context, rootNavigator: true).push();

What's the difference between
Navigator.of(context).pushNamed("/route");
and
Navigator.of(context, rootNavigator: true).pushNamed("/route");
More importantly, what's the use of setting rootNavigator: true on Navigator class, I read docs but they aren't quite clear. Can anyone explain the difference properly?
You can copy paste run full code below
There is a root Navigator above tab navigation
This demo shows open(Navigator.push) a full screen dialog (fullscreenDialog: true) with rootNavigator true/false
picture
rootNavigator = true , fullscreenDialog take all screen and above tab
rootNavigator = false, fullscreenDialog take tab size and inside tab, you can switch between Home and Support tab and see fullscreenDialog is still there
working demo
code snippet
Center(
child: CupertinoButton(
child: const Text(
'Push rootNavigator true',
),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute<bool>(
fullscreenDialog: true,
builder: (BuildContext context) => Tab3Dialog(),
),
);
},
),
),
Center(
child: CupertinoButton(
child: const Text(
'Push rootNavigator false',
),
onPressed: () {
Navigator.of(context, rootNavigator: false).push(
CupertinoPageRoute<bool>(
fullscreenDialog: true,
builder: (BuildContext context) => Tab3Dialog(),
),
);
},
),
),
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PawzHome(),
);
}
}
class PawzHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.conversation_bubble),
title: Text('Support'),
),
],
),
tabBuilder: (BuildContext context, int index) {
switch (index) {
case 0:
return CupertinoTabView(
builder: (BuildContext context) {
return CupertinoDemoTab1();
},
defaultTitle: 'Colors',
);
break;
case 1:
return CupertinoTabView(
builder: (BuildContext context) => CupertinoDemoTab2(),
defaultTitle: 'Support Chat',
);
break;
}
return null;
},
);
}
}
class CupertinoDemoTab1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: CustomScrollView(
slivers: <Widget>[
CupertinoSliverNavigationBar(),
SliverList(
delegate: SliverChildListDelegate([Tab1RowItem()]),
),
],
),
);
}
}
class Tab1RowItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).push(CupertinoPageRoute<void>(
title: "Click me",
builder: (BuildContext context) => Tab1ItemPage(),
));
},
child: Padding(padding: EdgeInsets.all(10.0), child: Text("Click me")),
);
}
}
class Tab1ItemPage extends StatelessWidget {
#override
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Container(
child: Column(
children: <Widget>[
SizedBox(height: 100,),
Center(
child: CupertinoButton(
child: const Text(
'Push rootNavigator true',
),
onPressed: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute<bool>(
fullscreenDialog: true,
builder: (BuildContext context) => Tab3Dialog(),
),
);
},
),
),
Center(
child: CupertinoButton(
child: const Text(
'Push rootNavigator false',
),
onPressed: () {
Navigator.of(context, rootNavigator: false).push(
CupertinoPageRoute<bool>(
fullscreenDialog: true,
builder: (BuildContext context) => Tab3Dialog(),
),
);
},
),
),
],
),
));
}
}
class CupertinoDemoTab2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
child: Container(
child: Center(
child: Text("Tab 2"),
),
));
}
}
class Tab3Dialog extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: CupertinoButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text("Ok"),
),
),
child: Center(
child: CupertinoButton(
color: CupertinoColors.activeBlue,
child: const Text('Sign in'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
If your app has nested navigators this parameter comes in handy when you want to call the root navigator not it's nested navigators ... please consider the image bellow ... in this example we are inside the page 2 and we want the page 3 to be the rootNavigator's child (because for example we want to ignore the bottomNavigationBar that is in MainPage)... in this example if you don't set the rootNavigator = true and you push the page 3 it will be the nestedNavigator's child (So the BottomNavigationBar of the MainPage's will be still visible).

Provider: create new model object per each sequence of pages

I am new to Provider. I am trying to use the provider model across multiple pages.
I have 2 sequence of pages. In the first sequence, there is only one page IncrementerPage, which will just increment the count. In the second sequence, there is an OptionsPage, which will display the count value and has 2 option buttons(Increment & Decrement). By clicking these buttons will navigate to corrensponding pages IncrementerPage & DecrementerPage to increment & decrement the count.
Note: In sequence1 and sequence2, the count should always start from zero.
This is what I done.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChangeNotifierProvider<CounterModel>(
create: (context) => CounterModel(),
child: RaisedButton(
child: Text("Sequence1"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IncrementerPage(),
),
);
},
),
),
ChangeNotifierProvider<CounterModel>(
create: (context) => CounterModel(),
child: RaisedButton(
child: Text("Sequence2"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OptionsPage(),
),
);
},
),
),
],
),
),
);
}
}
class OptionsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Consumer<CounterModel>(
builder: (context, model, child) {
return Text("Count: ${model.count}");
},
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Increment"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IncrementerPage(),
),
);
},
),
RaisedButton(
child: Text("Decrement"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DecrementerPage(),
),
);
},
),
],
),
),
);
}
}
class IncrementerPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Incrementor"),
),
body: Center(
child: Consumer<CounterModel>(
builder: (context, model, child) {
return Text(
'${model.count}',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black54,
),
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
),
);
}
}
class DecrementerPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Decrementer"),
),
body: Center(
child: Consumer<CounterModel>(
builder: (context, model, child) {
return Text(
'${model.count}',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black54,
),
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).decrement();
},
),
);
}
}
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void decrement() {
_count--;
notifyListeners();
}
}
But I am getting this error:
Could not find the correct Provider<CounterModel> above this Consumer<CounterModel> Widget
By moving ChangeNotifierProvider<CounterModel> outside the MaterialApp fixed the error. But both sequence of pages seems to be using the same counter variable (or model like global). So how to pass different model object per each sequence of pages? or is there any better way to do this?.
Probably the following code is causing the exception:
floatingActionButton: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
Provider.of<CounterModel>(context, listen: false).decrement();
},
),
As you can see, the ChangeNotifierProvider for CounterModel is Wrapped around widgets RaisedButton() for sequence 1 and 2 only.
Which are under the body parameter. While the floatingActionButton a colleague parameter for body.
Meaning the Provider.of<CounterModel> can not find the Provider from it's parent widgets.
What you can do is, Move the ChangeNotifierProvider code to cover HomePage and remove every other ChangeNotifierProvider code for CounterModel in the app.

Creating more than one initial route

Probably really straight forward but I am trying to create another button on the home page to follow another route.
Currently there is only one button which takes you down path X (aka the First Screen / Home Page) but I would like another button to take you down path Y. Any suggestions please?
Cheers in advance!
(sorry still getting to grips with the terminology)
void main() {
runApp(MaterialApp(
title: 'Named Routes Demo',
initialRoute: '/',
routes: {
'/': (context) => FirstScreen(),
'/second': (context) => SecondScreen(),
'/second/third': (context) => ThirdScreen(),
},
)
);
}
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: RaisedButton(
child: Text('Search'),
onPressed: () {
Navigator.pushNamed(context, '/second');
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Results Page'),
),
body: Center(
child: RaisedButton(
child: Text('Click this result'),
onPressed: () {
Navigator.pushNamed(context, '/second/third');
},
),
),
);
}
}
class ThirdScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Items Page"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('This is the item!'),
),
),
);
}
}
Instead of using routes, you can use a onGenerateRoute argument
void main() {
runApp(MaterialApp(
title: 'Named Routes Demo',
home: FirstScreen(),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(builder: (context) {
if(settings.name == '/second/third') return ThirdScreen();
if(settings.name == '/second') return SecondScreen();
else return FirstScreen();
})
},
},
)
);
}
You can change this logic to any way that you need to use settings.name
To add another button on the homepage, for this simple code, you can use a Column widget, here's the example
Center(
child: Column(
children: [
RaisedButton(
child: Text('Button one text'),
onPressed: () => Navigator.pushNamed(context, '/second'),
),
RaisedButton(
child: Text('Button two text'),
onPressed: () => Navigator.pushNamed(context, '/second/third'),
),
],
),
),

How to do nested navigation in Flutter

Does anyone have any recommendations for figuring out nested navigation in Flutter?
What I want is to keep a persistent BottomNavigationBar even when redirecting to new screens. Similar to YouTube, where the bottom bar is always there, even when you browse deeper into the menus.
I'm unable to figure it out from the docs.
The only tutorial I have been able to find so far that goes in-depth into exactly my requirement is https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf (source code). However, It's super confusing.
Right now I'm using
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return Container()
However, its just pushing the new widget over the entire stack, covoring the BottomNavigationBar.
Any tips would be greatly appreciated!
Here is a simple example that even supports popping to the first screen with a tab bar.
import 'package:flutter/material.dart';
import '../library/screen.dart';
import '../playlists/screen.dart';
import '../search/screen.dart';
import '../settings/screen.dart';
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
int _currentIndex = 0;
final _libraryScreen = GlobalKey<NavigatorState>();
final _playlistScreen = GlobalKey<NavigatorState>();
final _searchScreen = GlobalKey<NavigatorState>();
final _settingsScreen = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Navigator(
key: _libraryScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => LibraryScreen(),
),
),
Navigator(
key: _playlistScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PlaylistsScreen(),
),
),
Navigator(
key: _searchScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SearchScreen(),
),
),
Navigator(
key: _settingsScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SettingsScreen(),
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (val) => _onTap(val, context),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.library_books),
title: Text('Library'),
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('Playlists'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
);
}
void _onTap(int val, BuildContext context) {
if (_currentIndex == val) {
switch (val) {
case 0:
_libraryScreen.currentState.popUntil((route) => route.isFirst);
break;
case 1:
_playlistScreen.currentState.popUntil((route) => route.isFirst);
break;
case 2:
_searchScreen.currentState.popUntil((route) => route.isFirst);
break;
case 3:
_settingsScreen.currentState.popUntil((route) => route.isFirst);
break;
default:
}
} else {
if (mounted) {
setState(() {
_currentIndex = val;
});
}
}
}
}
Here is the example code for persistent BottomNavigationBar as a starter:
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
final navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Navigator(
key: navigatorKey,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PageOne(),
),
),
),
BottomNavigationBar(navigatorKey)
],
),
);
}
}
class BottomNavigationBar extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
BottomNavigationBar(this.navigatorKey) : assert(navigatorKey != null);
Future<void> push(Route route) {
return navigatorKey.currentState.push(route);
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
child: ButtonBar(
alignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text("PageOne"),
onPressed: () {
push(MaterialPageRoute(builder: (context) => PageOne()));
},
),
RaisedButton(
child: Text("PageTwo"),
onPressed: () {
push(MaterialPageRoute(builder: (context) => PageTwo()));
},
)
],
),
);
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page One"),
RaisedButton(
onPressed: (){
Navigator.of(context).pop();
},
child: Text("Pop"),
),
],
),
);
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page Two"),
RaisedButton(
onPressed: (){
Navigator.of(context).pop();
},
child: Text("Pop"),
),
],
),
);
}
}
Here is how it the screen record