preface

This month, I will continue my study of Flutter CoderWhy with a note ~ routine or a few questions? 🤔

There are two styles of Flutter

There are two styles of Flutter Design App: Material Design style MaterialApp and iOS style CupertinoApp

Scaffold

Scaffold scaffolding: Used to define the infrastructure of the page, such as navigation up, content area, navigation down, sidebar

  • There are related attributes: AppBar, drawer, body
import 'package:flutter/material.dart';

main() {
  / / runApp function
  runApp(MaterialApp(
    / / debugShowCheckedModeBanner: false, / / control interface whether the upper right corner of the display ` debug ` hints
    home: Scaffold(
      appBar: AppBar(
        title: Text("The first Flutter program"),
      ),
      body: Center(
        child: Text(
          "Hello World",
          textDirection: TextDirection.ltr,
          style: TextStyle(fontSize: 30, color: Colors.orange),
        ),
      ),
    ),
  ));
}
Copy the code

Widget

Everything in Flutter is Widget. There are two types of Widgets: StatelessWidget && StatefulWidget

StatelessWidget

Widgets that do not have state changes usually do some presentation work

Why are StatelessWidgets immutable?

  • StatelessWidget inherits from Widget
  • Widget is @immutable and cannot be changed. So the StatelessWidget is immutable
    • @immutable: Annotations that indicate classes or subclasses must be immutable portals
    • So member variables defined by widgets must use final definitions
    • Off-topic (annotation application) : Flutter annotation processing and code generation
  • Inheriting StatelessWidget subclasses must be implementedWidget build(BuildContext context)Abstract methods

StatelessWidget & Widget source code

@immutable
abstract class Widget extends DiagnosticableTree {
	// ...
}

abstract class StatelessWidget extends Widget {
  /// Initializes [key] for subclasses.
  const StatelessWidget({ Key key }) : super(key: key);

  /// Creates a [StatelessElement] to manage this widget's location in the tree.
  ///
  /// It is uncommon for subclasses to override this method.
  @override
  StatelessElement createElement() => StatelessElement(this);

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

The life cycle

Call the constructor first, then call build

class ZQLifeCycleStatelessWidget extends StatelessWidget {

  final String message;

  ZQLifeCycleStatelessWidget(this.message) {
    print("Constructor called");
  }

  @override
  Widget build(BuildContext context) {
    print("Call build method");
    return Text(this.message); }}Copy the code

StatefulWidget

A Widget that has a state change, usually an interactive state change, or a page refresh presentation based on data

Why is StatefullWidget mutable?

  • 📢 The parent class of both StatelessWidget and StatefulWidget is a Widget and therefore defines a member variable as final and immutable
  • However, subclasses that inherit StatefulWidget must be implementedState createState();Extraction method.
    • So what you can change is State which is also different from StatelessWidget

The source code of StatefulWidget

abstract class StatefulWidget extends Widget {
  /// Initializes [key] for subclasses.
  const StatefulWidget({ Key key }) : super(key: key);

  /// Creates a [StatefulElement] to manage this widget's location in the tree.
  ///
  /// It is uncommon for subclasses to override this method.
  @override
  StatefulElement createElement() => StatefulElement(this);

  /// Creates the mutable state for this widget at a given location in the tree.
  ///
  /// Subclasses should override this method to return a newly created
  /// instance of their associated [State] subclass:
  ///
  /// ```dart
  /// @override
  /// _MyState createState() => _MyState();
  /// ` ` `
  ///
  /// The framework can call this method multiple times over the lifetime of
  /// a [StatefulWidget]. For example, if the widget is inserted into the tree
  /// in multiple locations, the framework will create a separate [State] object
  /// for each location. Similarly, if the widget is removed from the tree and
  /// later inserted into the tree again, the framework will call [createState]
  /// again to create a fresh [State] object, simplifying the lifecycle of
  /// [State] objects.
  @protected
  @factory
  State createState();
}
Copy the code

State the source of

abstract class State<T extends StatefulWidget> with Diagnosticable {
	// ...
  /// an argument.
  T get widget => _widget;
  T _widget;

  _StateLifecycle _debugLifecycleState = _StateLifecycle.created;

  BuildContext get context => _element;
  StatefulElement _element;

  bool getmounted => _element ! =null;

  @protected
  @mustCallSuper
  void initState() {
    assert(_debugLifecycleState == _StateLifecycle.created);
  }

  @mustCallSuper
  @protected
  void didUpdateWidget(covariant T oldWidget) { }

  @mustCallSuper
  void reassemble() { }

  @protected
  void setState(VoidCallback fn) {
    assert(fn ! =null);
    assert(() {
      if (_debugLifecycleState == _StateLifecycle.defunct) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() called after dispose(): $this'),
          ErrorDescription(
              'This error happens if you call setState() on a State object for a widget that '
                  'no longer appears in the widget tree (e.g., whose parent widget no longer '
                  'includes the widget in its build). This error can occur when code calls '
                  'setState() from a timer or an animation callback.'
          ),
          ErrorHint(
              'The preferred solution is '
                  'to cancel the timer or stop listening to the animation in the dispose() '
                  'callback. Another solution is to check the "mounted" property of this '
                  'object before calling setState() to ensure the object is still in the '
                  'tree.'
          ),
          ErrorHint(
              'This error might indicate a memory leak if setState() is being called '
                  'because another object is retaining a reference to this State object '
                  'after it has been removed from the tree. To avoid memory leaks, '
                  'consider breaking the reference to this object during dispose().'),]); }if(_debugLifecycleState == _StateLifecycle.created && ! mounted) {throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() called in constructor: $this'),
          ErrorHint(
              'This happens when you call setState() on a State object for a widget that '
                  "hasn't been inserted into the widget tree yet. It is not necessary to call "
                  'setState() in the constructor, since the state is already assumed to be dirty '
                  'when it is initially created.'),]); }return true; } ());final dynamic result = fn() as dynamic;
    assert(() {
      if (result is Future) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() callback argument returned a Future.'),
          ErrorDescription(
              'The setState() method on $this was called with a closure or method that '
                  'returned a Future. Maybe it is marked as "async".'
          ),
          ErrorHint(
              'Instead of performing asynchronous work inside a call to setState(), first '
                  'execute the work (without updating the widget state), and then synchronously '
                  'update the state inside a call to setState().'),]); }// We ignore other types of return values so that you can do things like:
      // setState(() => x = 3);
      return true; } ()); _element.markNeedsBuild(); }@protected
  @mustCallSuper
  void deactivate() { }

  @protected
  @mustCallSuper
  void dispose() {
    assert(_debugLifecycleState == _StateLifecycle.ready);
    assert(() {
      _debugLifecycleState = _StateLifecycle.defunct;
      return true; } ()); }@protected
  Widget build(BuildContext context);

  @protected
  @mustCallSuper
  void didChangeDependencies() { }

	// ..
}
Copy the code

question

We all know that updating data in a StatefullWidget makes the interface change by calling setState. Why?

Source code involved in setState

void markNeedsBuild() {
  assert(_debugLifecycleState ! = _ElementLifecycle.defunct);if(! _active)return;
 // ...     
  if (dirty)
    return;
  _dirty = true;
  owner.scheduleBuildFor(this);
}

void scheduleBuildFor(Element element) {
    ...
    if(! _scheduledFlushDirtyElements && onBuildScheduled ! =null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true;
    // ...
}

void scheduleFrame() {
  if(_hasScheduledFrame || ! _framesEnabled)return;
  assert(() {
    if (debugPrintScheduleFrameStacks)
      debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase. ');
    return true; } ());window.scheduleFrame();
  _hasScheduledFrame = true;
}

void _handleBuildScheduled() {
  / / call ensureVisualUpdate
  ensureVisualUpdate();
}

void ensureVisualUpdate() {
  switch (schedulerPhase) {
    case SchedulerPhase.idle:
    case SchedulerPhase.postFrameCallbacks:
      // When the schedulerPhase is set to schedulerphase. idle,
      / / SchedulerPhase postFrameCallbacks calls when scheduleFrame ()
      scheduleFrame();
      return;
    case SchedulerPhase.transientCallbacks:
    case SchedulerPhase.midFrameMicrotasks:
    case SchedulerPhase.persistentCallbacks:
      return; }}Copy the code

So I made a summary of the above source code:

  • The StatefullWidget needs to implement the createState method. The State subclass can store State variables, so it is different from the StatelessWidget
  • Call setState to StatefullWidget cause ~
    • Because of the callsetStateCall the processmarkNeedsBuild= >onBuildScheduled= >scheduleFrame= >drawFrame(SeeThe principle and process of Flutter setState update)
    • The core of the UI thread’s drawing process is to perform a WidgetsBindingdrawFrameMethod is then createdlayer treeThe view tree

The life cycle

class ZQLifeCycleStatefullWidget extends StatefulWidget {

  ZQLifeCycleStatefullWidget() {
    print("1. Call ZQLifeCycleStatefullWidget constructor method");
  }

  @override
  _ZQLifeCycleStatefullWidgetState createState() {
    print("2. Call ZQLifeCycleStatefullWidget createState method");
    return_ZQLifeCycleStatefullWidgetState(); }}class _ZQLifeCycleStatefullWidgetState extends State<ZQLifeCycleStatefullWidget> {

  _ZQLifeCycleStatefullWidgetState() {
    print("3. Call ZQLifeCycleStatefullWidgetState's constructor method");
  }

  @override
  void initState() {
    // TODO:Implement initState 📢 here you mustCallSuper (@mustcallsuper)
    super.initState();
    print("4. Call ZQLifeCycleStatefullWidgetState initState method");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("Call ZQLifeCycleStatefullWidgetState didChangeDependencies method");
  }

  @override
  void didUpdateWidget(covariant ZQLifeCycleStatefullWidget oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    print("Call ZQLifeCycleStatefullWidgetState didUpdateWidget method");
  }

  @override
  Widget build(BuildContext context) {
    print("5. Call ZQLifeCycleStatefullWidgetState build method");
    return Text("ZQLifeCycleStatefullWidgetState");
  }

  @override
  void dispose() {
    super.dispose();
    print("ZQLifeCycleStatefullWidgetState 6. Call the dispose method"); }}Copy the code
Flutter: 1. Call ZQLifeCycleStatefullWidget constructor method of flutter: 2. Call ZQLifeCycleStatefullWidget createState method of flutter: 3. Call ZQLifeCycleStatefullWidgetState constructor method of flutter: 4. Call ZQLifeCycleStatefullWidgetState flutter initState method: Call ZQLifeCycleStatefullWidgetState didChangeDependencies method of flutter: 5. Call ZQLifeCycleStatefullWidgetState build method > > > > button click count flutter: 1. Call ZQLifeCycleStatefullWidget flutter constructor method: Call ZQLifeCycleStatefullWidgetState didUpdateWidget method of flutter: 5. Call ZQLifeCycleStatefullWidgetState build methodCopy the code

The flow chart

Sequential trigger sequence

  • createState
    • Triggered during initialization construction
  • initState
    • Note: super.initState() must be called when overriding initState:
    • Similar iOS viewDidLoad
    • Mount is true
  • didChangeDependencies
    • Calling initState calls;
    • Change when you rely on some data from another object, such as an InheritedWidget
  • build
  • addPostFrameCallback
  • didUpdateWidget
    • The didUpdateWidget method is invoked when the rebuild is triggered by the parent Widget
  • deactivate
  • dispose

question

Why must super.initState() be called when overriding initState:?

  • The reason: the @mustCallsuper annotation requires that a subclass must call a superclass method

Method statement

Excerpt to: Portal

methods instructions
createState Framework will by calling StatefulWidget. CreateState to create a State.
initState The newly created State will be associated with a BuildContext where State is considered installed and initState will be called. In general, we can rewrite this function to initialize it.
didChangeDependencies This function is called after the initState call. In fact, this function is always called by the Framework when the dependency of the State object changes.
build After the above steps, the system decides that a State is ready and calls Build to build the view. We need to return a Widget in this function.
deactivate Deactivate This function is called when State is temporarily removed from the view tree. It is also called when the page switches, because State’s position in the view tree changes and needs to be temporarily removed before being added.
dispose The Framework calls this function when State is permanently removed from the view tree. Triggered before destruction, we can do the final resource release here. The deactivate function is always called before calling this function.
didUpdateWidget This function is called when the configuration of the Widget changes. For example, this function is called when hot overloading occurs. After calling this function, the build function is called.
setState When you need to update the view of State, you need to call this function manually, which triggers the build function.

StatefulWidget & State process diagram

quesion

Mounted is used in setState.

Conclusion:

  • The StatelessWidget is immutable while the state of the StatefullWidget is mutable, mainly due to the abstract methods it overwrites
    • StatelessWidget:Widget build(BuildContext context)
    • StatefullWidget: State createState()
  • What does the widget render in the end? What does the build method return, for exampleRenderObjectWidgetIf the StatefullWidget looks at the build returned by state

shortcuts

  • The inputstl 或 stfulShortcuts to quickly create widgets
  • alt + enterPackage components
  • option + enterTransfer StatelessWidget StatefullWidget
  • option + enter + wAs a widget
  • command + alt + bView the implementation class of the abstract class

Declarative programming & imperative programming

  • The main idea of imperative programming is to focus on the steps of execution, telling the computer step by step what to do first and what to do next
  • Declarative programming expresses the logic of program execution in the form of data structures. What should be done without specifying how

Code sample

portal

reference

  • The principle and process of Flutter setState update
  • Flutter rendering mechanism – UI thread
  • State Class Official document description
  • The stateful StatefulWidget of Flutter(7)
  • The life cycle of a Flutter