An overview of the

The life cycle is the entire cycle from widget component loading to unloading, and being familiar with the life cycle allows you to do what you need to do at the right time.

In Flutter development, everything is widget, but we do not directly inherit the Widget class to implement a new component. We usually inherit the Widget class indirectly by inheriting the StatelessWidget or StatefulWidget. Statelesswidgets and StatefulWidgets are directly derived from the Widget class. These two important abstract classes in Flutter introduce two types of Widget models. This article focuses on the life cycle of these two widgets.

StatelessWidget

StatelessWidget is a StatelessWidget that does not change once it is created, so setState cannot be provided to modify the state of the component. Its internal properties should be declared final to prevent unexpected changes. So the StatelessWidget has only one life cycle. The build is used to create the Widget, but since the build is called every time the screen is refreshed, do not write the business logic in the build. You can write business logic to the constructor of your StatelessWidget. Its life cycle is shown below:

StatefulWidget

StatefulWidget is a StatefulWidget that re-renders the UI when state changes and provides setState to modify the state of the component. Its life cycle is mainly in the state part, as shown in the following figure:

The following functions are explained:

  • InitState: called when the Widget is first inserted into the Widget tree; The Flutter framework calls this callback only once for each State object. Therefore, the Flutter callback usually performs one-time operations, such as State initialization and event notification of subscribing to subtrees. Cannot call in the callback BuildContext. DependOnInheritedWidgetOfExactType (the method used to get away from the current Widget in the Widget tree a recent parent InheritFromWidget), the reason is that after the initialization is complete, The InheritFromWidget in the Widget tree may also change, so the right thing to do is call it in the Build () method or didChangeDependencies() method.

  • DidChangeDependencies () : called when the State object’s dependencies change; For example, when the InheritedWidget it relies on changes, the Framework calls this method to notify the component of the change. Typically, the Flutter framework notifies the widget to invoke this callback when the system language Locale or application theme changes.

Note: When the didChangeDependencies method is called and the component state becomes dirty, call the Build method immediately.

  • Build () : used to build widgets, which is called in the following scenario:

    1. After calling initState().
    2. After calling didUpdateWidget().
    3. After calling setState().
    4. After calling didChangeDependencies().
    5. After the State object is removed from one location in the tree (with a deactivate call) it is reinserted into another location in the tree.

Note: This method should contain only the code that builds the component, and should not include any additional functionality, especially for time-consuming tasks.

  • Reassemble () : This callback is specifically for development debugging and is called on hot reload, never in Release mode.

  • DidUpdateWidget () : The child’s didUpdateWidget may be called when the parent widget rebuilds the child widget.

When the parent widget is rebuilt, the Flutter framework calls Widget.canUpdate to check whether the key and runtimeType of the old and new nodes in the widget tree are the same. DidUpdateWidget () will not be called if the key and runtimeType of the new and old nodes are not equal. That is, when the parent component recreates a new component with the same runtimeType and widget.key, the Framework updates the component properties of the State object to reference the new component, and then calls this method with the previous component as an argument.

Note: After the Framework calls this method, it sets the component to the dirty state and then calls the Build method, so there is no need to call the setState method in this method.

  • Deactivate () : This callback is called when the State object is removed from the tree. The Flutter framework can re-insert a State object into the tree in some scenarios, such as when the subtree containing the State object moves from one position in the tree to another (this can be done using a GlobalKey). If it is not re-inserted into the tree after removal, the Dispose () method is immediately called.

  • Dispose () : called when the State object is permanently removed from the tree; Resources are usually released in this callback.

Examples show

Show a demo of random number, click the random button, update the random number, print the call flow of the demo’s life cycle function, the code is as follows:

import 'dart:math'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( body: RandomPage(), ), ); } } class RandomPage extends StatefulWidget { @override State<StatefulWidget> createState() { // TODO: implement createState return _RandomPageState(); } } class _RandomPageState extends State<RandomPage> { final _randomBuild = Random(); int _randomValue = 0; @override void initState() { // TODO: implement initState super.initState(); print('initState'); } @override void didChangeDependencies() { // TODO: implement didChangeDependencies super.didChangeDependencies(); print('didChangeDependencies'); } @override Widget build(BuildContext context) { print('build'); // TODO: implement build return Container( child: Padding( padding: EdgeInsets.only(top: 100), child: Center( child: Column(children: [Text(' random number: $_randomValue'), SizedBox(height: 200), TextButton(child: Text(' toggle random number '), onPressed: () { setState(() { _randomValue = _randomBuild.nextInt(10000); }); }, ) ], ), ), ), ); } @override void reassemble() { // TODO: implement reassemble super.reassemble(); print('reassemble'); } @override void didUpdateWidget(covariant RandomPage oldWidget) { // TODO: implement didUpdateWidget super.didUpdateWidget(oldWidget); print('didUpdateWidget'); } @override void deactivate() { // TODO: implement deactivate super.deactivate(); print('deactivate'); } @override void dispose() { // TODO: implement dispose super.dispose(); print('dispose'); }}Copy the code

Click Run to start for the first time and print the log as follows:

flutter: initState
flutter: didChangeDependencies
flutter: build
Copy the code

Click switch random number button to print log as follows:

flutter: build
Copy the code

Clicking the Toggle random number button calls setState(), which calls Build to rebuild the widget.

Click the Hot Reload button and print the log as follows:

flutter: reassemble
flutter: didUpdateWidget
flutter: build
Copy the code

App Life cycle

Through WidgetsBindingObserver didChangeAppLifecycleState life cycle state of the App can be gained. The life cycle is in the AppLifecycleState class. Common states include the following:

  • Resumed: Is visible and responds to user input
  • Inactive: Inactive and unable to process user responses
  • Paused: invisible and unable to respond to user input, but still active in the background

Examples are as follows:

class _RandomPageState extends State<RandomPage> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance? .addObserver(this); } @override void dispose() { WidgetsBinding.instance? .removeObserver(this); super.dispose(); } void didChangeAppLifecycleState(AppLifecycleState state) async { if (state == AppLifecycleState.resumed) { getData(); } } @override Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } void getData() { } }Copy the code

Remember to register and remove listeners.

Some life cycle related considerations

  • Page rendered callback

AddPostFrameCallback is a callback to the end of StatefulWidge’s rendering and is only called once, after which the StatefulWidget refreshes the UI. Can’t call in initState BuildContext dependOnInheritedWidgetOfExactType, can use addPostFrameCallback circumvent this limitation:

@override 
void initState() { 
    super.initState(); 
    WidgetsBinding.instance.addPostFrameCallback((_) => {}); 
}
Copy the code
  • mounted

Mounted is a property in the State object that specifies whether the current component is in the tree. (After creating State, the Framework associates State with BuildContext before calling initState.) When the Framework calls dispose, Mounted is set to false, indicating that the component is no longer in the tree.

CreateState specifies that the component is already in the component tree. Mounted is set to true by the Framework.

if(mounted){ 
    setState(() { ... });
}
Copy the code

Mounted is strongly recommended when calling setState.

Why add this judgment? If the component is not in the tree or has been removed from the tree, setState will throw an exception. Mounted determines whether the component is in the tree.

  • Dirty or clean

Dirty Indicates that the component is currently dirty. The build function will be executed at the next frame, and the component is dirty after the setState method is called or the didUpdateWidget method is executed.

Clean corresponds to dirty. Clean indicates that the component is in the clean state. In the clean state, the component does not execute the build function.