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