Let’s start with a quick question

void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container( child: FlatButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => SecondPage())); }, child: Text(' jump '),),),); } } class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), ); }}Copy the code

Click the jump button to find that there is no jump, and an error is reported

Navigator operation requested with a context that does not include a Navigator.
Copy the code

Widget

Everything is Widget in Flutter demonstrates the importance of widgets

  • WidgetWhat is it?
  • WidgetCan n you show it?
  • WidgetMutable??
  • WidgetIs it reusable?
  • WidgetHow is it stored after loading?

  • WidgetIt’s an immutable profile. It’s not really him
  • WidgetIt can be reused
  • WidgetImmutable by itself, but it can passStateAchieve variable effect

BuildContext

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {}
}
Copy the code

The StatelessWidget has a build method passed in to the context. When is the build method triggered and what is the context passed in?

StatelessWidget

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}
Copy the code

StatelessElement

class StatelessElement extends ComponentElement { StatelessElement(StatelessWidget widget) : super(widget); @override StatelessWidget get widget => super.widget as StatelessWidget; @override Widget build() => widget.build(this); @override void update(StatelessWidget newWidget) { super.update(newWidget); assert(widget == newWidget); _dirty = true; rebuild(); }}Copy the code

Now we know that the context is the StatelessElement object, and the build method in StatelessElement triggers the build method in the StatelessWidget, The build method of StatelessElement is implicitly called by the Flutter Framework when building the UI tree

Its inheritance relationship is

class StatelessElement extends ComponentElement

abstract class ComponentElement extends Element

abstract class Element extends DiagnosticableTree implements BuildContext
Copy the code

useBuildContextDirect access is discouragedElementobject

MaterialApp & Navigator

We see that the Navigator is provided by the MaterialApp

Flutter renders three trees (Widget, Element, RenderObject)

The widgets we create will eventually be stored as a Tree after they are loaded, like the Widget Tree in the example above

In Flutter, the Widget Tree is only the blueprint of the interface, providing the description of the display of the interface. Each node in the Widget Tree has one Element corresponding to it

We can’t find the Navigator through navigator. of(context)

static NavigatorState of( BuildContext context, { bool rootNavigator = false, bool nullOk = false, }) { final NavigatorState navigator = rootNavigator ? context.findRootAncestorStateOfType<NavigatorState>() : context.findAncestorStateOfType<NavigatorState>(); 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

We see that this method actually gets the NavigatorState object

  • findRootAncestorStateOfTypefindrootState
  • findAncestorStateOfTypeFind the one closest to youstate

You can basically tell that the context is broken, and you can’t find the NavigatorState from the context that was passed in

After knowing the reason, many ways can solve our problem

2. Encapsulate as child widgets

class BtnWidget extends StatelessWidget{ @override Widget build(BuildContext context) { return FlatButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => SecondPage())); }, child: Text(' jump ')); }}Copy the code

3. Provide him with a Navigator on the top node

void main() => runApp(MaterialApp(home: MyApp(),));
Copy the code

Page made up of statefulWidgets

void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() { return MyAppState(); } } class MyAppState extends State<MyApp>{ @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: FlatButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => SecondPage())); }, child: Text(' jump '),),),); }}Copy the code

stateIs made up ofStatefulElementThe triggerStatefulWidgetTo create the

RenderObject trees involve a lot of page rendering

  • First create aWidget Tree
  • throughWidgetthecreateElementTo create aElement TreeandWidget Treeassociate
  • throughcreateRenderObjectMethod to createrenderObjectThe tree

The real responsibility for rendering the page is the RenderObject, and Element holds the Widget, State, and RenderObject