Understanding Flutter Render Engine

前端 未结 1 1697
情深已故
情深已故 2020-12-24 15:05

The docs here about how to update a ListView say:

In Flutter, if you were to update the list of widgets inside a setState(), you would

相关标签:
1条回答
  • 2020-12-24 15:45

    Flutter isn't made only of Widgets.

    When you call setState, you mark the Widget as dirty. But this Widget isn't actually what you render on the screen. Widgets exist to create/mutate RenderObjects; it's these RenderObjects that draw your content on the screen.

    The link between RenderObjects and Widgets is done using a new kind of Widget: RenderObjectWidget (such as LeafRenderObjectWidget)

    Most widgets provided by Flutter are to some extent a RenderObjectWidget, including ListView.

    A typical RenderObjectWidget example would be this:

    class MyWidget extends LeafRenderObjectWidget {
      final String title;
    
      MyWidget(this.title);
    
      @override
      MyRenderObject createRenderObject(BuildContext context) {
        return new MyRenderObject()
          ..title = title;
      }
    
      @override
        void updateRenderObject(BuildContext context, MyRenderObject renderObject) {
          renderObject
            ..title = title;
        }
    }
    

    This example uses a widget to create/update a RenderObject. It's not enough to notify the framework that there's something to repaint though.

    To make a RenderObject repaint, one must call markNeedsPaint or markNeedsLayout on the desired renderObject.

    This is usually done by the RenderObject itself using custom field setter this way:

    class MyRenderObject extends RenderBox {
      String _title;
      String get title => _title;
      set title(String value) {
        if (value != _title) {
          markNeedsLayout();
          _title = value;
        }
      }
    }
    

    Notice the if (value != previous).

    This check ensures that when a widget rebuilds without changing anything, Flutter doesn't relayout/repaint anything.

    It's due to this exact condition that mutating List or Map doesn't make ListView rerender. It basically has the following:

    List<Widget> _children;
    List<Widget> get children => _children;
    set children(List<Widget> value) {
      if (value != _children) {
        markNeedsLayout();
        _children = value;
      }
    }
    

    But it implies that if you mutate the list instead of creating a new one, the RenderObject will not be marked as needing a relayout/repaint. Therefore there won't be any visual update.

    0 讨论(0)
提交回复
热议问题