introduce

Listview in the construction of the principle of the analysis recorded some notes, I hope to help you. Listview as a whole has a large structure, so it will consist of several chapters. To read the following, you need to have an understanding of how to build, layout, and draw flutter widgetsCopy the code

In this article we mainly analyze the listView to construct child-view process.

ListView source analysis of the role of Viewport

Listview

Listview has a number of constructors such as:

ListView ListView.builder ListView.separated ... , etc.Copy the code

We use listView.Builder for analysis.

ListView.builder

First let’s look at a simple usage code

ItemBuilder: (CTX,index){return Container(alignment: Alignment.center, width: size.width,height: 250, color: index%2 == 0 ? Colors.red:Colors.blueAccent, child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 30),), ); })Copy the code

This builds a red and blue list widget that scrolls vertically.

Let’s click on the listView source code.

ListView. Builder structure

In order not to stray from our goal and get lost in the source code, I will only annotate relevant parameters and methods and ignore the restCopy the code

As you can see from the figure above, some parameters related to drawing and layout are defined. There is only one way:

// Return a SliverList(delegate: childrenDelegate) @override Widget buildChildLayout(BuildContext context) { if (itemExtent ! = null) { return SliverFixedExtentList( delegate: childrenDelegate, itemExtent: itemExtent, ); } return SliverList(delegate: childrenDelegate); }Copy the code

A childrenDelegate is passed in the returned sliverList, which is created when the constructor is initialized.

Builder ({@required IndexedWidgetBuilder itemBuilder,}) : listView.builder ({@required IndexedWidgetBuilder itemBuilder,}) : childrenDelegate = SliverChildBuilderDelegate( itemBuilder, childCount: itemCount, addAutomaticKeepAlives: addAutomaticKeepAlives, addRepaintBoundaries: addRepaintBoundaries, addSemanticIndexes: addSemanticIndexes, );Copy the code

We stopped to glance at the SliverChildBuilderDelegate

SliverChildBuilderDelegate

As you can see, this simply rewraps our itemBuilder() method and acts as a proxy class for the Sliver building child.

Let’s go back to buildChildLayout(BuildContext Context), where the sliver is created.

Where is this method called? As a rule of thumb, it should have something to do with its parent.

BoxScrollView

From the comments, it is a scrollView that uses the single-child layout model.

In addition to receiving arguments from subclasses, there are two methods:

This is the method we implemented above inside the ListView

  /// Subclasses should override this method to build the layout model.
  @protected
  Widget buildChildLayout(BuildContext context);

Copy the code

Yo, you see where we call buildChildLayout (), the first line, and then we do some decorative wrapping for the sliver, we don’t have to worry about it.

@override List<Widget> buildSlivers(BuildContext context) { Widget sliver = buildChildLayout(context); EdgeInsetsGeometry effectivePadding = padding; if (padding == null) { final MediaQueryData mediaQuery = MediaQuery.of(context, nullOk: true); if (mediaQuery ! = null) { // Automatically pad sliver with padding from MediaQuery. final EdgeInsets mediaQueryHorizontalPadding = MediaQuery. Padding. CopyWith (top: 0.0, bottom: 0.0); Final EdgeInsets mediaQueryVerticalPadding = mediaQuery. Padding. CopyWith (left: 0.0, right: 0.0); // Consume the main axis padding with SliverPadding. effectivePadding = scrollDirection == Axis.vertical ? mediaQueryVerticalPadding : mediaQueryHorizontalPadding; // Leave behind the cross axis padding. sliver = MediaQuery( data: mediaQuery.copyWith( padding: scrollDirection == Axis.vertical ? mediaQueryHorizontalPadding : mediaQueryVerticalPadding, ), child: sliver, ); } } if (effectivePadding ! = null) sliver = SliverPadding(padding: effectivePadding, sliver: sliver); return <Widget>[ sliver ]; }Copy the code

So, where does buildSlivers () get called? Let’s look at its parent class

ScrollView

Abstract Class ScrollView extends StatelessWidget {}Copy the code

Finally, something familiar :StatelessWidget Take a look at the structure diagram of a ScrollView

Official comment for scrollView:

A Scrollable wiget consists of three parts: 1. A Scrollable widget that listens for gestures from the user. A viewport widget that displays part of the Scroll View while scrolling. 3. One or more Slivers that can be used to form a widget for various scrolling effectsCopy the code

Now that we understand the composition, let’s go back down the road. Scrollview has three methods:

So that’s what boxScrollView does

  @protected
  List<Widget> buildSlivers(BuildContext context);
Copy the code

This method is used to create a window and here we go to the second return

  @protected
  Widget buildViewport(
    BuildContext context,
    ViewportOffset offset,
    AxisDirection axisDirection,
    List<Widget> slivers,
  ) {
    if (shrinkWrap) {
      return ShrinkWrappingViewport(
        axisDirection: axisDirection,
        offset: offset,
        slivers: slivers,
      );
    }
    return Viewport(
      axisDirection: axisDirection,
      offset: offset,
      slivers: slivers,
      cacheExtent: cacheExtent,
      center: center,
      anchor: anchor,
    );
  }
Copy the code

The familiar build() method

// The boxScrollview buildSlivers method is called. Final List<Widget> slivers = buildSlivers(context); final AxisDirection axisDirection = getDirection(context); final ScrollController scrollController = primary ? PrimaryScrollController.of(context) : controller; Final Scrollable Scrollable = Scrollable(dragStartBehavior: dragStartBehavior, axisDirection: axisDirection, controller: scrollController, physics: physics, semanticChildCount: semanticChildCount, viewportBuilder: (BuildContext context, ViewportOffset offset) {// buildViewport is called here, Return buildViewport(Context, offset, axisDirection, slivers); }); // I delete the extraneous ones, and eventually return scrollable (wrap it up a bit)}Copy the code

At this point, we have a general understanding of Listview call logic, as shown in the following figure:

According to the graph above, we know that

BuildSlivers () returns the sliverList and this is relevant to our child-view. BuildChildLayout () decorates the sliverList a little bit and build() calls the above methods in turn, Wrap with the Scrollable widget and create a viewPortCopy the code

So, the sliverList is probably relevant to our child-view build, so let’s take a look

SliverList

As you can see above, the sliverList ultimately inherits from renderObjectWidget, in other words it is a custom view.

RenderObjectWidget is used to create renderObjectWidget for widgets. In brief, mainly responsible for 'add, delete, change and check' if you do not understand, you can go to the layout, drawing related articlesCopy the code

Back again, the sliver list implements a superclass method:

  @override
  RenderSliverList createRenderObject(BuildContext context) {
    final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
    return RenderSliverList(childManager: element);
  }
Copy the code

Where is it called, we don’t know, look at its parent class

SliverMultiBoxAdaptorWidget

He has a solution:

And, of course, the createRenderObject() method implemented by the subclass aboveCopy the code
/ / look! Create the element also has handed in to the widget @ override SliverMultiBoxAdaptorElement createElement method () = > SliverMultiBoxAdaptorElement(this);Copy the code
Stateful Widgets were built over a container of widgets.Copy the code

Let’s look at this element

SliverMultiBoxAdaptorElement

If you look inside, it looks exactly like the Element.

There are a bunch of methods inside, all related to widget building, such as:

performRebuild() update() updateChild() didFinishChildLayout() ... And so on and so onCopy the code

A quick word about the performRebuild() method

All widgets have this method on their element, which is driven by vsync signals and will eventually call the familiar Build () method to build your widget.

As usual setState() triggers this method, otherwise it wouldn't be called XXrebuildCopy the code

We notice that in the performRebuild method, the _build method is called

  Widget _build(int index) {
    return widget.delegate.build(this, index);
  }
Copy the code

A delegate is familiar with it (SliverChildBuilderDelegate)? Its build method is a wrapper around our itemBuilder.

Remember the listView code we used at the beginning?

ListView.builder(
          itemCount: 200,

            itemBuilder: (ctx,index){
            return Container(
              alignment: Alignment.center,
              width: size.width,height: 250,
              color: index%2 == 0 ? Colors.red:Colors.blueAccent,
              child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 30),),
            );
        })
Copy the code

Is the itemBuilder method the same?

Note: BuildContext is elementCopy the code

That concludes our analysis of child-View construction, and we’ll cover viewports and Scrollable widgets in a later article. Thank you for reading.

Related articles

The structure of PageView has many similarities and even similarities with listView. Maybe the following two chapters can help you understand the principle of flutter scroll view more comprehensively from another perspective

PageView PageController source analysis notes

PageView source and Gesture Arena disambiguation analysis