banner
jzman

jzman

Coding、思考、自觉。
github

Flutter Series: Widget Lifecycle

PS: Your plan is perfect, but the world is changing too fast.

The previous article here discussed image loading and source code analysis in Flutter. Friends who have done mobile development know the lifecycle of components, and the same applies in Flutter. Understanding and learning the lifecycle of components in Flutter is very important. The related articles are as follows:

This article will mainly introduce the lifecycle of Widgets in Flutter, as follows:

  1. StatelessWidget
  2. StatefulWidget
  3. State Lifecycle States
  4. State Lifecycle Methods

StatelessWidget#

Components derived from StatelessWidget are stateless components. Stateless components are rendered only once during construction and do not support dynamic changes, meaning they cannot be redrawn through other user operations and can only receive parameters passed during construction, as shown below:

/// StatelessWidget
/// Represents a stateless Widget
class StatelessSamplePage extends StatelessWidget {

  // Data passed from outside
  final String data;
  StatelessSamplePage(this.data);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.lightBlue,
      child: Text(data),
    );
  }
}

If the passed parameters are not marked as final, the following warning will occur:

This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: StatelessSamplePage.data

This indicates that the Widget is annotated with @immutable, as shown below:

@immutable
abstract class Widget extends DiagnosticableTree {

At this point, variables can only be marked as final. In Dart, variables marked as final can only be initialized once, which also aligns with the stateless characteristic of StatelessWidget.

StatefulWidget#

Components derived from StatefulWidget are stateful components. Stateful components support multiple builds of the Widget as data changes to render dynamic interfaces. For example, to implement a real-time display of the current time, StatelessWidget cannot accomplish this and can only be implemented using a stateful StatefulWidget, as shown below:

/// StatefulWidget
/// Represents a stateful Widget
class StatefulSamplePage extends StatefulWidget {
  @override
  _StatefulSamplePageState createState() => _StatefulSamplePageState();
}

class _StatefulSamplePageState extends State<StatefulSamplePage> {
  DateFormat _dateFormat;
  String _currentTime;
  Timer _timer;

  @override
  void initState() {
    super.initState();
    _dateFormat = new DateFormat("yyyy-MM-dd HH:mm:ss");
    // Initialize current time
    _currentTime = _dateFormat.format(DateTime.now());
    // Update time
    _startTime();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Scaffold(
        appBar: AppBar(
          title: Text("Stateful Widget sample"),
          centerTitle: true,
        ),
        body: Align(
          alignment: Alignment.topCenter,
          child: Text("Current time: $_currentTime"),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    if(_timer!=null){
      _timer.cancel();
    }
  }

  _startTime() {
    const Duration duration = Duration(seconds: 1);
    _timer = Timer.periodic(
        duration,
        (Timer timer) => {
              // Refresh the interface state
              setState(() {
                _currentTime = _dateFormat.format(DateTime.now());
              })
            });
  }
}

The effect is as follows:

image

In fact, if it is just a static interface, there is no difference between StatelessWidget and StatefulWidget; both can achieve it. The only difference is that StatefulWidget can trigger the Widget's rebuild through the setState method, and the State class serves as the bridge from Stateless to Stateful.

State Lifecycle States#

The Flutter lifecycle actually refers to the lifecycle of various components:

  • StatelessWidget: The lifecycle of a stateless component only includes the build process;
  • StatefulWidget: The lifecycle of a stateful component refers to the lifecycle of State.

The Flutter lifecycle is essentially the lifecycle of a stateless component, which is the lifecycle of State, as shown in the diagram below:

image

The main states of each State's lifecycle are three:

  • created: Refers to the creation state of State, which enters the created state after calling the createState method;
  • dirty: Refers to the state when data changes after calling methods like setState, but the Widget has not yet been rebuilt;
  • clean: Refers to the state after the Widget has been built;
  • defunct: Refers to the state after State.dispose is called, at which point the corresponding Widget has been destroyed and cannot be rebuilt.

State Lifecycle Methods#

The lifecycle of a stateful component is the lifecycle of State, and the specific calling process and build trigger timing are shown in the diagram below:

image

The specific meanings of its lifecycle methods are as follows:

  • createState: Used in StatefulWidget to create State;
  • initState: Initialization operations for State, such as variable initialization;
  • didChangeDependencies: Called after initState, or when using InheritedWidgets, which can be used for Flutter state management;
  • build: Used for Widget construction;
  • deactivate: Called after the Widget containing this State object is removed; if this Widget is removed and not added to another Widget tree structure, the dispose method will continue to be called;
  • dispose: This method releases the resources occupied by the Widget after it is called;
  • reassemble: Called during the development phase when hot reload occurs, after which it will be rebuilt;
  • didUpdateWidget: Called when the parent Widget builds, the didUpdateWidget method of the child Widget will be called.

By adding logs to the corresponding Widget lifecycle methods, we can verify the execution of the above lifecycle methods. The source code for the parent Widget is as follows:

const String TAG = "Flutter";
/// Widget lifecycle
class WidgetLifecycleSamplePage extends StatefulWidget {
  @override
  _WidgetLifecycleSamplePageState createState() {
    Log.info(TAG, "parent createState");
    return _WidgetLifecycleSamplePageState();
  }
}

class _WidgetLifecycleSamplePageState extends State<WidgetLifecycleSamplePage> {

  num _count = 0;

  @override
  void initState() {
    super.initState();
    Log.info(TAG, "parent initState");
  }

  @override
  Widget build(BuildContext context) {
    Log.info(TAG, "parent build");
    return Scaffold(
      appBar: AppBar(
        title: Text("Widget Lifecycle Sample"),
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          Center(
            child: RaisedButton(
                textColor:Colors.white,
                color: Colors.lightBlue,
                child: Text("parent->setState:$_count",style: TextStyle(color: Colors.white),),
                onPressed: (){
                  setState(() {
                    Log.info(TAG, "parent setState");
                    _count++;
                  });
                }),
          ),
          SubWidgetLifecycleSamplePage(),
        ],
      )
    );
  }

  @override
  void didUpdateWidget(WidgetLifecycleSamplePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    Log.info(TAG, "parent didUpdateWidget");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    Log.info(TAG, "parent didChangeDependencies");
  }

  @override
  void deactivate() {
    super.deactivate();
    Log.info(TAG, "parent deactivate");
  }

  @override
  void reassemble() {
    super.reassemble();
    Log.info(TAG, "parent reassemble");
  }

  @override
  void dispose() {
    super.dispose();
    Log.info(TAG, "parent dispose");
  }
}

/// Sub Widget
class SubWidgetLifecycleSamplePage extends StatefulWidget {
  @override
  _SubWidgetLifecycleSamplePageState createState() {
    Log.info(TAG, "sub createState");
    return _SubWidgetLifecycleSamplePageState();
  }
}

The implementation of the child Widget is similar to that of the parent Widget, so it will not be pasted here. You can reply with the keyword 【Lifecycle】 in the public account backend to get the complete source code. The display effect of the above code is as follows:

image

The corresponding lifecycle method calls are as follows:

image

The analysis is as follows:

  1. The Widget initialization process refers to the lifecycle process of entering the page where this Widget is located, first the parent Widget and then the child Widget, and then sequentially calling createState, initState, didChangeDependencies, and build methods;
  2. When the parent Widget calls setState to update data, it triggers the build of the parent Widget, thus calling the didUpdateWidget method of the child Widget;
  3. Other processes such as hot reload and Widget destruction follow the same flow as the State lifecycle method flow chart, and will not be elaborated here.
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.