[Flutter from entry to pitfall 5] Do you really know how to use State in Widget?

[Flutter from entry to pitfall] Flutter knowledge system
[Flutter from entry to pitfall 1] Flutter introduction, installation and use
[Flutter from entry to pitfall 2] Dart language basic overview
[Flutter from entry to pitfall 3] How Flutter works
[Flutter from entry to pitfalls Part 4] The cornerstone of building the Flutter interface - Widget



cutting edge

In the previous section [Flutter from Getting Started to Pitfalls Part 4] , we learned about how Widget, Element, and RenderObject cooperate with each other to achieve graphics rendering work, which is the cornerstone of Flutter's interface - Widget. Flutter has done a lot of rendering optimization work at the bottom level, so that we can build an interface with any function and any complexity just by combining and nesting different types of Widgets.

We learned that there are two types of Widgets StatelessWidget: and . StatefulWidget deals with scenes that require interaction and dynamically changing visual effects; while StatelessWidget is used to handle static, stateless view display.StatefulWidget

The scenario of StatefulWidget has completely covered StatelessWidget, so when we build interfaces, we often use StatefulWidget extensively to handle static view display requirements, and it seems that there is no problem.

Today, we will focus on introducing the differences between these two types to help you better understand Widgets and master the correct timing of using different types of Widgets.

Through this article, you can learn:

  • Through the source code, understand the basic design ideas of StatelessWidget and StatefulWidget;
  • Re-understand the UI update mechanism of Widget;

UI programming paradigm

First, we need to understand: how to adjust the display style of a control (Widget) in Flutter, that is, through the UI programming paradigm.

If you have relevant native development experience, you should know that view development is imperative , and you need to tell the operating system or browser exactly how to do things.
A very simple example: If we want to change a certain copy of the interface, we need to find the specific text control and call its control method command to complete the text change.

// 原生JavaScript设置某文本控件展示文案为Hello World
document.querySelector("#demo").innerHTML = "Hello World!";

Different from this, Flutter's view development is declarative, and its core design idea is to separate the view and data, which is consistent with React's design idea.

Imperative VS Declarative

Imperative programming emphasizes precise control of process details; while declarative programming emphasizes the overall output of results through intention. Corresponding to Flutter, the intent is the State bound to the component state, and the result is the re-rendered component. During the life cycle of the Widget, any changes applied to the State will force the Widget to be rebuilt.

For scenarios where the component does not need to be changed after it is created, state binding is optional . The "optional" here distinguishes two types of Widgets, namely: StatelessWidget without binding state, and StatefulWidget with binding state. When the user interface you want to build does not change with changes in any state information, you need to choose StatelessWidget, otherwise choose StatefulWidget. The former is generally used for the display of static content, while the latter is used for content presentation with interactive feedback.


See the difference through source code

Below, we will introduce Widget StatelessWidgetand Widget in detail StatefulWidget, analyze their differences from the perspective of source code, and summarize some basic principles for Widget selection.

StatelessWidget (stateless component)

In Flutter, Widgets are built in a parent-to-child, top-down manner. The parent Widget controls the display style of the child Widget, and its style configuration is provided by the parent Widget during construction.

Widgets built in this way, such as Text, Container, Row, Columnetc., do not rely on any other information except these configuration parameters when they are created. In other words, once they are successfully created, they no longer care about or respond to any data. Changes are redrawn. In Flutter, such a Widget is called a StatelessWidget (stateless component).

  • StatelessWidget diagram

    StatelessWidget diagram

  • Source code analysis

    Below, we Textillustrate the construction process of StatelessWidget through part of the source code of the component.

    class Text extends StatelessWidget {
          
               
      //构造方法及属性声明部分
      const Text(this.data, {
          
          
        Key key,
        this.textAlign,
        this.textDirection,
        //其他参数
        ...
      }) : assert(data != null),
         textSpan = null,
         super(key: key);
         
      final String data;
      final TextAlign textAlign;
      final TextDirection textDirection;
      //其他属性
      ...
      
      
      Widget build(BuildContext context) {
          
          
        ...
        Widget result = RichText(
           //初始化配置
           ...
          )
        );
        ...
        return result;
      }
    }
    

    It can be seen that after the constructor assigns a value to its property list, buildthe method then initializes the subcomponent RichTextthrough its property list (such as text data, alignment textAlign, text display direction textDirection, etc.) and returns. After that, Textthe method no longer responds to changes in external data.

  • In what scenarios should StatelessWidget be used?

    Simple judgment rule: Can the parent Widget fully control its UI display effect through initialization parameters? If so, then we can use StatelessWidget to design the constructor interface.

  • Two small examples to help you understand

    The first small example is that I need to create a custom pop-up window control to prompt users with some error messages that occur while using the App. The parent Widget of this component can completely pass the style information and error message information required by the component to the child Widget when it is initialized, which means that the parent Widget can fully control its display effect through initialization parameters. Therefore, I can customize the component by inheriting StatelessWidget.

    The second small example is that I need to define a counter button. Every time the user clicks the button, the color of the button will deepen. As you can see, the parent Widget of this component can only control the initial style display effect of the child Widget, but cannot control the color changes that occur during the interaction process. Therefore, I cannot customize the component by inheriting StatelessWidget. So, this time it is StatefulWidget's turn to appear.

StatefulWidget (stateful component)

Corresponding to StatelessWidget, the display of some Widgets (such as Image, Checkbox), in addition to the static configuration passed in when the parent Widget is initialized, also needs to handle user interaction (for example, the user clicks a button) or changes in its internal data (such as , network data return packet), and reflected on the UI.
After these Widgets are created, they still need to care about and respond to data changes for redrawing. In Flutter, this type of Widget is called StatefulWidget (stateful component).

  • StatefulWidget diagram

    StatefulWidget diagram
    If you have read the previous article [Flutter from Getting Started to Pitfalls Part 4], which is the cornerstone of the Flutter interface - Widget , it was mentioned in it: Widget is immutable and needs to be destroyed and rebuilt when it changes, so there is no state. Are you confused? So, what is going on here?

    In fact, StatefulWidgetit is implemented by the design method of Stateclass proxy construction.Widget

    Please read on, we will take the source code as an example to help you understand this topic.

  • Source code analysis

    Below, we use Imagepart of the source code of the component to explain the construction process of StatefulWidget, and to help understand the above knowledge point.

    As mentioned above Text, Imagethe class's constructor receives the attribute parameters to be used by the class. However, the difference is that Imagethe class does not have builda method to create a view. Instead, createStateit creates an object _ImageStateof type through the method state, and then this object is responsible for the construction of the view.

    This stateobject holds and handles Imagethe state changes in the class, so I will take _imageInfothe properties as an example to analyze them together.

    _imageInfoThe attribute is used to load real images to the Widget. Once Statethe object _handleImageChangeddetects that _imageInfothe attribute has changed through the method, it will immediately call the method _ImageStateof the class setStateto notify the Flutter framework: "The data here has changed, please use the updated _imageInfo data to reload picture!". However, the Flutter framework will mark the view state and update the UI.

    Please see the source code

    class Image extends StatefulWidget {
          
          
      //构造方法及属性声明部分
      const Image({
          
          
        Key key,
         this.image,
        //其他参数
      }) : assert(image != null),
           super(key: key);
      final ImageProvider image;
      //其他属性
      ...
      
      
      _ImageState createState() => _ImageState();
      ...
    }
    class _ImageState extends State<Image> {
          
          
      ImageInfo _imageInfo;
      //其他属性
      ...
      void _handleImageChanged(ImageInfo imageInfo, bool synchronousCall) {
          
          
        setState(() {
          
          
          _imageInfo = imageInfo;
        });
      }
      ...
      
      Widget build(BuildContext context) {
          
          
        final RawImage image = RawImage(
          image: _imageInfo?.image,
          //其他初始化配置
          ...
        );
        return image;
      }
     ...
    }
    

    In this example, Imageit works in a dynamic way: listening for changes and updating the view . Unlike StatelessWidget, which fully controls the UI display through the parent Widget, the StatefulWidget's parent Widget only defines its initialization state, and the running state of its own view needs to be processed by itself, and the UI display is updated immediately according to the processing situation.


StatefulWidget is not a panacea, so use it with caution

Above we have understood these two types of Widgets through the source code of StatelessWidgetand . StatefulWidgetYou may ask, since StatefulWidget can not only respond to state changes, but also display static UI, is it necessary for StatelessWidget, a Widget that can only display static UI, to exist?

The answer is yes, of course it is necessary!

For UI frameworks, the same display effect can generally be achieved through a variety of controls. By definition, StatefulWidget seems to be omnipotent, and it seems reasonable to replace StatelessWidget. Therefore, StatefulWidget is also prone to abuse.

But the fact is that the abuse of StatefulWidget will directly affect the rendering performance of Flutter applications.

Let's review the update mechanism of Widget to help you realize the cost of fully using StatefulWidget.


Widget update mechanism;

Widgets are immutable, and updating means destroying + rebuilding (build). StatelessWidget is static and does not need to be updated once created; for StatefulWidget, calling the setState method in the State class to update data will trigger the destruction and reconstruction of the view, and will also indirectly trigger the destruction and reconstruction of each of its sub-Widgets.

This means that if our root layout is a StatefulWidget, every time the update UI is called in its State, all Widgets on the entire page will be destroyed and rebuilt.

Although Flutter internally uses the Element layer to minimize modifications to the real rendering view and improve rendering efficiency, it does not destroy the entire RenderObject tree and rebuild it. However, the destruction and reconstruction of a large number of Widget objects is unavoidable. If the reconstruction of a sub-Widget involves some time-consuming operations, the rendering performance of the page will drop sharply.

Therefore, correctly evaluating your view display needs and avoiding unnecessary use of StatefulWidget is the simplest and most direct way to improve the rendering performance of Flutter applications.


summary

This chapter is mainly based on the discussion of StatelessWidget and StatefulWidget.

  • By understanding Flutter's declarative-based UI programming paradigm, and by reading the source code of two typical Widgets ( Textand Image), I learned the basic design ideas of StatelessWidget and StatefulWidget.

    Since Widgets are built in a parent-to-child, top-down manner, when customizing components, we can determine whether to inherit StatelessWidget based on the basic principle of whether the parent Widget can fully control its UI display effect through initialization parameters. Or StatefulWidget.

  • Understand that StatefulWidget is not a panacea, and revisit the UI update mechanism of Widget. Although Flutter will use the Element layer to minimize modifications to the real rendering view, a large number of Widget destruction and reconstruction cannot be avoided. Therefore, avoiding the abuse of StatefulWidget is the simplest and most direct way to improve application rendering performance.

  • It should be noted that, in addition to us actively refreshing the UI through State, in some special scenarios, the Widget's build method may be executed multiple times. Therefore, we should not place too many time-consuming operations inside this method. [About the detailed explanation of this part, I will put it in the next chapter: detailed analysis in the life cycle]


Friends are welcome to leave a message in the comment area to share your views. You are also welcome to use one-click three-way links. I will continue to update [Flutter from entry to pit series]. You are also welcome to share this article with more people. Read with friends.

The content of this chapter is mainly summarized from the "Flutter Core Technology and Practical Combat" course taught by Chen Hang.

Guess you like

Origin blog.csdn.net/XH_jing/article/details/130391610