How does Flutter render?

To answer this question, you first need to realize that there are three trees in Flutter: the Widget tree, the Element tree, and the RenderObject tree.

When the application starts, Flutter will traverse and create all Widgets to form a Widget Tree, which corresponds to the Widget Tree, and creates each Element object by calling the createElement() method on the Widget to form an Element Tree.

Finally, call the createRenderObject() method of Element to create each render object to form a Render Tree.

Then you need to know what Widget, Element and RenderObject are and what they do.

 

What is a Widget

Widget is the core part of Flutter and is an immutable description of the user interface. As Flutter's slogan Everything's a widget, developing an app with Flutter is writing a Widget :dog:.

Flutter's Widget not only represents UI controls, but also some functional components, such as routing jump Navigator, gesture detection GestureDetector components, etc.

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });
  final Key key;

  /// ...

  @protected
  Element createElement();

  /// ...

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key;
  }
}

Widget's canUpdate method determines the Element corresponding to the updated widget by comparing whether the runtimeType and key properties of the new widget and the old widget are the same.

 

What is Element

Element is an instantiated Widget object, which is generated using the Widget configuration data at a specific location through the Widget's createElement() method.

Elements are used to manage updates and changes to the app's UI, manage the lifecycle of widgets, and each Element contains references to Widget s and RenderObject s.

When the Widget changes, if the runtimeType and key properties of the two Widgets are the same, the new Element will update the old Element through Element.update(), otherwise the old Element will be deleted, and the newly generated Element will be inserted into the tree.

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
  ///
  /// Typically called by an override of [Widget.createElement].
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  /// Change the widget used to configure this element.
  ///
  /// The framework calls this function when the parent wishes to use a
  /// different widget to configure this element. The new widget is guaranteed
  /// to have the same [runtimeType] as the old widget.
  ///
  /// This function is called only during the "active" lifecycle state.
  @mustCallSuper
  void update(covariant Widget newWidget) {
    /// ...
  }

  /// Creates an instance of the [RenderObject] class that this
  /// [RenderObjectWidget] represents, using the configuration described by this
  /// [RenderObjectWidget].
  ///
  /// This method should not do anything with the children of the render object.
  /// That should instead be handled by the method that overrides
  /// [RenderObjectElement.mount] in the object rendered by this object's
  /// [createElement] method. See, for example,
  /// [SingleChildRenderObjectElement.mount].
  @protected
  RenderObject createRenderObject(BuildContext context);
}

 

What is RenderObject

RenderObject is used for the layout and drawing of the application interface, which saves the size, layout and other information of elements. It is very energy-consuming to instantiate a RenderObject.

When the application is running, Flutter uses the data of the RenderObject to draw the application interface, and finally forms a Render Tree.

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
  /// Initializes internal fields for subclasses.
  RenderObject() {
    _needsCompositing = isRepaintBoundary || alwaysNeedsCompositing;
  }

  /// The render object at (or below) this location in the tree.
  ///
  /// If this object is a [RenderObjectElement], the render object is the one at
  /// this location in the tree. Otherwise, this getter will walk down the tree
  /// until it finds a [RenderObjectElement].
  RenderObject get renderObject {
    RenderObject result;
    void visit(Element element) {
      assert(result == null); // this verifies that there's only one child
      if (element is RenderObjectElement)
        result = element.renderObject;
      else
        element.visitChildren(visit);
    }
    visit(this);
    return result;
  }

  void layout(constraints constraints, { bool parentUsesSize = false }) {
    /// ...
  }

  /// ...

  void paint(PaintingContext context, Offset offset) {
    /// ...
  }

}

 

Why three trees are needed

The purpose of using three trees is to reuse Element as much as possible.

Reusing Element is very important for performance, because Element has two key pieces of data: the state object of the Stateful widget and the underlying

RenderObject .

When the structure of the application is very simple, this advantage may not be reflected. Once the application is complicated, there are more and more elements that make up the page, and the cost of recreating the three trees is very high, so the update operation needs to be minimized.

When Flutter can reuse Element, the logical state information of the user interface is unchanged, and the previously calculated layout information can be reused to avoid traversing the entire tree.

 

give an example

Create a simple Flutter application with the following code

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      color: Colors.white,
      debugShowCheckedModeBanner: false,
      builder: (context, child) => HomePage(),
    ),
  );
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _isWorld = true;

  Widget _buildWorld() {
    return RichText(
      text: TextSpan(
        text: 'Hello world',
        style: TextStyle(color: Colors.black),
      ),
    );
  }

  Widget _buildFlutter() {
    return RichText(
      text: TextSpan(
        text: 'Hello flutter',
        style: TextStyle(color: Colors.black),
      ),
    );
  }

  void changeText() {
    setState(() {
      _isWorld = !_isWorld;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Center(
            child: _isWorld ? _buildWorld() : _buildFlutter(),
          ),
          SizedBox(height: 20.0),
          // Padding(padding: EdgeInsets.only(top: 20.0)),
          IconButton(icon: Icon(Icons.refresh), onPressed: changeText)
        ],
      ),
    );
  }
}

 

It can be found that Flutter just updated the text data and reused the Element and RenderObject corresponding to RichText.

When using the SizedBox widget instead of the Padding widget.

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Center(
          child: RichText(
            text: TextSpan(
              text: 'Hello $text',
              style: TextStyle(color: Colors.black),
            ),
          ),
        ),
        SizedBox(height: 20.0),
        // Padding(padding: EdgeInsets.only(top: 20.0)),
        IconButton(icon: Icon(Icons.refresh), onPressed: changeText)
      ],
    ),
  );
}

Both the Element and RenderObject corresponding to the Padding widget will be removed from the tree and replaced with the newly generated SizedBox.

Guangzhou vi designhttp://www.maiqicn.com Office resource website Daquanhttps://www.wode007.com

Summarize

Widget is the declaration information of the application interface.

Element links Widget and RenderObject to manage interface updates and modifications.

RenderObject saves specific layout information and is responsible for drawing UI.

Tags: Framework

Posted by Toboe on Mon, 16 May 2022 01:02:09 +0300