How to open new MaterialPageRoute as a child in widget tree in Flutter
Solution 1
I solved my issue using nested Navigator.
Here is the example:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Home(),
));
}
var homeNavigatorKey = GlobalKey<NavigatorState>();
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Navigator(
key: homeNavigatorKey,
onGenerateRoute: (settings) {
/*dirty code for illustration purposes only*/
if (settings.name == '/child') {
return MaterialPageRoute(builder: (context) => Child());
} else {
return MaterialPageRoute(
builder: (context) => Column(
children: <Widget>[
Center(
child: Text("This is home"),
),
RaisedButton(
child: Text("Open child view"),
onPressed: () {
homeNavigatorKey.currentState.pushNamed('/child');
},
)
],
));
}
},
),
);
}
}
class Child extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: RaisedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("Back to home"),
),
));
}
}
The tree looks like this
and allows for passing data from Home to every child.
Feel free to comment or post answers if you have any simpler solution.
Solution 2
Navigator.of(context).push()
will replace your Home
screen with a new screen (while keeping the Home
screen in memory so you can go back to it). They are on the same level in the widget tree, the only way to nest them is to use nested Navigators
I think.
But, given what you want to achieve: why not initialize the ScopedModel one level up and provide at the root of your widget tree? That way you can access your model both in the Home
screen as well as the Child
screen.
Below is how you could do that, I added a simple ScopedModel and am able to access it's text
property inside the Child
screen.
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:tryout/model.dart';
void main() {
runApp(
ScopedModel<TestModel>(
model: TestModel(),
child: MaterialApp(
home: Home(),
),
)
);
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Center(
child: Text("This is home"),
),
RaisedButton(
child: Text("Open child view"),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Child()));
},
)
],
),
);
}
}
class Child extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text("${ScopedModel.of<TestModel>(context).text}"),
));
}
}
And here's the TestModel
class:
import 'package:scoped_model/scoped_model.dart';
class TestModel extends Model {
String _text = "Test to see if this works!";
String get text => _text;
}
![mbartn](https://lh6.googleusercontent.com/-R6g1zgwDSRA/AAAAAAAAAAI/AAAAAAAAACM/or1yEGEzFOU/photo.jpg?sz=256)
mbartn
Updated on December 19, 2022Comments
-
mbartn over 1 year
In the example below when I push new MaterialPageRoute it is created on the same level as Home widget in the Flutter widget tree. I would like to have it as a child of widget Home, so Home would be a parent of Child widget.
Here is a full code:
import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: Home(), )); } class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Column( children: <Widget>[ Center( child: Text("This is home"), ), RaisedButton( child: Text("Open child view"), onPressed: () { Navigator.of(context) .push(MaterialPageRoute(builder: (context) => Child())); }, ) ], ), ); } } class Child extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Container( child: Text("Child view"), )); } }
Here is how the widget tree looks like
I want to achieve that because I would like to initialize the ScopedModel in Home widget and have it available in every new MaterialPageRoute I create.
-
mbartn about 4 yearsThanks for your answer. I know about that possibility and that's how I have it currently implemented but I have a complex application with multiple different models and I want some of them to recreate when specific widget is recreated (Home in this example represents this widget). I was wondering if there is any simpler approach than implementing nested Navigators.
-
JJuice about 4 yearsAaah ok. Maybe you could do it by first disposing the already initialized model, before doing your Navigators.push()?
-
mbartn about 4 yearsAt this point I have a dispose() method in these models but I don't want to clean models by hand because every time a new programmer adds a new variable/functionality to the model he has to remember to dispose it in dispose() method and I don't like it. You think there is any possibility to assign a brand new object instance for already initialized model? I mean something like
ScopedModel.of<TestModel>(context) = TestModel()
? -
JJuice about 4 yearsI am not aware of the possibility to recreate instances, all across your widgettree. Passing down a new instance via the constructor of the Child widget, instead of letting
InheritedWidget
work for you is something you shouldn’t do I think, because it will create multiple instances and states of your model, which is exactly the opposite of what the package is all about. -
JJuice about 4 yearsBut, why do you need a new instance in the first place? What's the problem that recreating the model should solve?
-
mbartn about 4 yearsFor example: Some user data is stored in ExampleModel (let's say his personal notes). When user logs out I have to remove all his session data stored in ExampleModel and pop() Home widget. When the next user logs in he should have clean or brand new ExampleModel when entering Home widget.
-
JJuice about 4 yearsI’ll think about it in the weekend. Found a Reddit topic which is about Provider (but Provider is like scoped model 2.0). I guess you need to make sure to rebuilt you’re widget tree from the point where the scoped model is injected, and up to there your Navigation stack needs to be popped.