preface
The following error occurred when the flutter route was used:
Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context){
return new MaterialApp(
title: 'Test Flutter',
home: Scaffold(
body: Center(
child: FlatButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewRouter()));
},
child: Text('jump')),),),); } } class NewRouter extends StatelessWidget { @override Widget build (BuildContext context){return Scaffold(
appBar: AppBar(
title: Text("hahahha"),
),
body: Center(
child: Text("new router hahah"),),); }}Copy the code
The solution
Just pull out the Home part as a new Widget.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context){
return new MaterialApp(
title: 'Test Flutter',
home: new MyHomeWidget(),
);
}
}
class MyHomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context){
return new Scaffold(
appBar: AppBar(title: Text('new Flutter'),),
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('my first flutter app'),
FlatButton(
color: Colors.green,
child: Text('Route hop'),
textColor: Colors.white,
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context){
returnNewRouter(); })); },)],),); } } class NewRouter extends StatelessWidget { @override Widget build (BuildContext context){return Scaffold(
appBar: AppBar(
title: Text("hahahha"),
),
body: Center(
child: Text("new router hahah"),),); }}Copy the code
Why does my Navigator operation not find the Navigator in the current context? Why does it make a new widget?
Then let’s do a concrete analysis
Navigator
We often open a bunch of pages in an app, and when we return, it goes back to the last open page, and then it goes back layer by layer, and yes it’s a stack. In Flutter, the page stack is managed and maintained by the Navigator.
// Press a new page to screen
Navigator.of(context).push
// Remove the top page of the route
Navigator.of(context).pop
Copy the code
Usually when we build an application we don’t manually create a Navigator that can also navigate the page, and that’s why.
Yes, this Navigator is exactly what MaterialApp provides for us. However, if home, Routes, onGenerateRoute and onUnknownRoute are all null and Builder is not null, the MaterialApp will not create any Navigator.
BuildContext
Every time we write part of the interface code, we do it in the build function. The build function needs to pass in a BuildContext by default. Let’s see what it is
abstract class BuildContext {
/// The current configuration of the [Element] that is this [BuildContext].
Widget get widget;
/// The [BuildOwner] for this context. The [BuildOwner] is in charge of
/// managing the rendering pipeline for this context.BuildOwner get owner; .Copy the code
We can see that BuildContext is an abstract class, but what is passed in each build function. Let’s take a look at what happens when you build a view.
BuildContext
Every time we write part of the interface code, we do it in the build function. The build function needs to pass in a BuildContext by default. Let’s see what it is.
abstract class BuildContext {
/// The current configuration of the [Element] that is this [BuildContext].
Widget get widget;
/// The [BuildOwner] for this context. The [BuildOwner] is in charge of
/// managing the rendering pipeline forthis context. BuildOwner get owner; .Copy the code
We can see that BuildContext is an abstract class, but what is passed in each build function. Let’s take a look at what happens when you build a view.
How does Flutter build views
In Flutter, Everything is Widget, we write the UI interface by nesting widgets with constructors. In fact, a Widget is not really something to display on the screen, it’s just configuration information, it’s always immutable, and it can be reused in multiple places. So what’s the view tree that’s actually displayed on the screen? Element Tree!
So let’s take a look at what happens when you build your view. Take the Stateless Widget as an example.
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this); .Copy the code
When the widget is loaded into the view tree, createElement is first invoked and the current widget is passed to Element.
So let’s see what this StatelessElement is
class StatelessElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatelessElement(StatelessWidget widget) : super(widget);
@override
StatelessWidget get widget => super.widget;
@override
Widget build() => widget.build(this);
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true; rebuild(); }}Copy the code
As you can see, StatelessElement preserves the widget’s reference by passing it into its constructor and calls the Build method.
The build method actually calls the Widget’s build method and passes in this, the StatelessElement object. We know that the build method needs to pass in a BuildContext. Why do we pass in StatelessElement? So we keep looking.
class StatelessElement extends ComponentElement
...
abstract class ComponentElement extends Element
...
abstract class Element extends DiagnosticableTree implements BuildContext
Copy the code
It’s actually the Element class that implements BuildContext and is inherited by ComponentElement -> StatelessElement.
So let’s go back to the official explanation for BuildContext:
BuildContextobjects are actually Elementobjects. The BuildContextinterface is used to discourage direct manipulation of Elementobjects.
The BuildContext object is actually an Element object, and the BuildContext interface is used to prevent direct operations on Element objects.
Cool! Now we finally know where this BuildContext comes from. Let’s review again what the Flutter construction view does.
View tree loading process
StatelessWidget
- It first calls the createElement method of the StatelessWidget and generates a StatelesseElement object from the widget.
- Mount the StatelesseElement object to the Element tree.
- The StatelesseElement object calls the Widget’s build method and passes the Element itself as BuildContext.
StatefulWidget
- The createElement method of the StatefulWidget is also called first, and the StatefulElement object is generated from the widget and the widget reference is retained.
- Mount the StatefulElement to the Element tree.
- CreateState based on the widget’s createState method.
- The StatefulElement object calls state’s build method and passes Element itself as BuildContext.
So the context we use in the build function is the Element object created by the current widget.
Method of (context)
This code is often used in Flutter
// Open a new page
Navigator.of(context).push
// Open the Scaffold Drawer
Scaffold.of(context).openDrawer
// Get the display1 style text theme
Theme.of(context).textTheme.display1
Copy the code
So what is this of context? Let’s take the example of a Navigator opening a new page.
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false, {})// Key code -----------------------------------------v
final NavigatorState navigator = rootNavigator
? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())
: context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
// Key code ----------------------------------------^
assert(() {
if (navigator == null && !nullOk) {
throw FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'The context used to push or pop routes from the Navigator must be that of a '
'widget that is a descendant of a Navigator widget.'
);
}
return true; } ());return navigator;
}
Copy the code
As you can see, the key code section through the context. RootAncestorStateOfType traverse upward Element tree, and find a matching NavigatorState recently. In other words, “of” is really an encapsulation of the data that the context gets across components.
Our Navigator’s push is done by finding the NavigatorState.
Not only that, BuildContext has many ways to get objects across components
AncestorInheritedElementForWidgetOfExactType (Type targetType) - > InheritedElement ancestorRenderObjectOfType (TypeMatcher The matcher) - > RenderObject ancestorStateOfType (TypeMatcher matcher) - > State ancestorWidgetOfExactType (Type targetType) - > Widget findRenderObject() → RenderObject inheritFromElement(InheritedElement ancestor, {ObjectThe aspect}) - > InheritedWidget inheritFromWidgetOfExactType (Type targetType, {ObjectAspect}) → InheritedWidget rootAncestorStateOfType(TypeMatcher matcher) → State Visitorelements (bool The visitor - > Element (Element))voidVisitChildElements (ElementVisitor visitor) - >void
Copy the code
Note that the initState phase in State does not get data across components; you can use these methods only after didChangeDependencies.