This is the 12th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Widget life cycle

Basic concepts of the life cycle

What is the life cycle

When we use an object, we sometimes need to know a state of the object, when it was created and when it was destroyed. The life cycle methods we commonly use are essentially callback functions that are encapsulated in Flutter to set the corresponding callback methods for external use in different states of the Widget.

The role of life cycle

  1. Initialize data
  • Create variables, constants
  • Sending network Requests
  1. Listen for events in the widget
  2. Memory management
  • Destroy data, destroy listeners, timers, etc

Common lifecycle functions for widgets

Widget lifecycle functions can be divided into two types: stateless and stateful.

StatelessWidget (StatelessWidget)

When a stateless Widget is rendered, the constructor and build functions are called in sequence.

StatefulWidget

class MyHomePage extends StatefulWidget { final String? title; MyHomePage({this.title}) {print('Widget constructor is called '); } @override _MyHomePageState createState() {print('createState was called '); return _MyHomePageState(); }} class _MyHomePageState extends State<MyHomePage> {_MyHomePageState() {print(' the State constructor is called '); } @override void initState() { super.initState(); Print ('State init function called '); } @override Widget build(BuildContext context) {print('State build function is called '); return Center(child: Text(widget.title ?? ")); } @override void dispose() { super.dispose(); Print (' Dispose of State called '); }}Copy the code

When a StatefulWidget is rendered, it calls the StatefulWidget constructor, createState, State constructor, initState function, and build function in sequence. The StatefulWidget’s constructor and State’s build function are called when hot overloaded. The build function is called when the setState method is called.

You can see from the source code that setState is actually _element calling markNeedsBuild, but there was a bit of a miscalculation beforehand, where _element is context.

The data sharing widget InheritedWidget

class MyData extends InheritedWidget { final int data; // Constructor const MyData({required this.data, required Widget child}) : super(child: child); // Define a convenient method for widgets in child components to get shared data static MyData? ofContext(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(); } // This callback determines when the current data changes, Override bool updateShouldNotify(covariant InheritedWidget oldWidget) {print(' called UpdateShouldNotify function '); // If true is returned, the didChangeDependencies method in child widgets that depend on shared data (oldWidget as MyData).data! = data; } } class InheritedDemo extends StatefulWidget { const InheritedDemo({Key? key}) : super(key: key); @override _InheritedDemoState createState() => _InheritedDemoState(); } class _InheritedDemoState extends State<InheritedDemo> { int _count = 0; @override Widget build(BuildContext context) { return MyData(data: _count, child: Column( children: [ TextDemo(count: _count), ElevatedButton(onPressed: () { _count++; setState(() {}); }, child: const Icon(Icons.add)), ], )); } } class TextDemo extends StatefulWidget { final int? count; TextDemo({this.count}); @override _TextDemoState createState() => _TextDemoState(); } class _TextDemoState extends State<TextDemo> {@override Widget build(BuildContext context) {print(' build function called '); return Text((MyData.ofContext(context) as MyData).data.toString()); } @override void didChangeDependencies() { super.didChangeDependencies(); Print (' call didChangeDependencies function '); }}Copy the code

Flutter provides an InheritedWidget to resolve this situation because there are too many hierarchies and the child component’s data needs to depend on the parent component’s data. In the example above, we define a class MyData that is responsible for data sharing to inherit the InheritedWidget. The constructor of MyData has two parameters: data represents the data to be shared, and child represents the Widget that depends on the data sharing. And we provide an ofContext method for the outside world to use when getting data. We initialize MyData in the build method of _InheritedDemoState, and use myData.ofContext (context) to retrieve shared data in the child that needs it. If a child component needs to get shared data using an InheritedWidget, its root component must be MyData that inherits from the InheritedWidget class. We call the updateShouldNotify method when we execute _count++, and we use the return value to determine whether to call the didChangeDependencies method of the subcomponent. Otherwise, we don’t call it, and we can do a few things in the didChangeDependencies method as required.

Flutter rendering principle

abstract class Widget extends DiagnosticableTree {
  Element createElement();
}
Copy the code

The key code is attached above, and you can see from the source that all Widget classes implement the createElement function. Here we look at the rendering flow of StatelessWidget, a subclass of Widget, separately from StatefulWidget.

StatelessWidget rendering process

abstract class StatelessWidget extends Widget {
  StatelessElement createElement() => StatelessElement(this);
}
Copy the code

The createElement method in the StatelessWidget creates a StatelessElement object, adds it to the Elment tree, and returns the StatelessElement object. When the StatelessElement object is created, the StatelessWidget itself is passed as an argument to the StatelessElement object.

class StatelessElement extends ComponentElement {
  Widget build() => widget.build(this);
}
Copy the code
abstract class ComponentElement extends Element { void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); assert(_child == null); assert(_lifecycleState == _ElementLifecycle.active); _firstBuild(); assert(_child ! = null); }Copy the code
abstract class Element extends DiagnosticableTree implements BuildContext { Element(Widget widget) : assert(widget ! = null), _widget = widget;Copy the code

StatelessElement inherits from ComponentElement, ComponentElement inherits from Element, External widgets are assigned to the _widget property in the Element constructor, and the mount method is called in ComponentElement, The _firstBuild in the mount method will eventually execute the Build method for ComponentElement, and the build for StatelessElement will execute widget.build(this) and pass itself out.

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

So the context in our external StatelessWidget subclass is the Element object.

StatefulWidget rendering process

abstract class StatefulWidget extends Widget {
  @override
  StatefulElement createElement() => StatefulElement(this);
}
Copy the code

The StatefulWidget also executes createElement, but returns an object of type StatefulElement.

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
        state._element = this;
        state._widget = widget;
}

  @override
  Widget build() => state.build(this);
}
Copy the code

But the StatefulElement goes one step further by executing the createState function and assigning a value to the _state property, and assigning an external widget to the state._widget property. Assign this pointer to state._element. This is why we can get widgets in State. The build method is called state.build(this).