This article will help you understand the structure of Flutter lists and slides. From the common ListView to the internal implementation of NestedScrollView, this article will help you better understand and use Flutter lists.
This is not a tutorial on how to use the API, but something that you don’t usually touch on in your daily development, but is important.
Flutter slip list
The common sliding-list scenario in Flutter simply consists of three parts:
Viewport
: it’s a MultiChildRenderObjectWidget controls,“What it provides is a window, the size of the viewable area where the list is located;”Scrollable
:“It swipes through gestures.”, such as VerticalDragGestureRecognizer and HorizontalDragGestureRecognizer;Sliver
RenderSliver, to be exact,“It’s primarily used to lay out and render content in a Viewport;”
Take ListView for example, as shown in the figure above is the change of ListView sliding process, where:
- green
Viewport
That’s the size of the list window that we saw; - The purple is for gestures
Scrollable
Let the yellow partSliverList
在Viewport
To produce sliding; - The yellow part is
SliverList
That’s what happens when we slideViewport
The position of the inside has changed;
With this basic idea in mind, Viewport and Scrollable implementations are generally common, so “different slivers can be implemented in a Flutter by customizing and combining different slivers to complete the layout”.
“Exactly, the performLayout of RenderSliver is completed, and the Corresponding SliverGeometry is obtained through SliverConstraints.”
So in Flutter:
ListView
Using theSliverFixedExtentList
orSliverList
;GridView
Using theSliverGrid
;PageView
Using theSliverFillViewport
;
Of course, there is a special SingleChildScrollView, because it is a slider for a single child. Instead of using RenderSliver, it directly defines a RenderObject (RenderBox). And ** “Slide child offset directly on performLayout” **.
RenderSliver
We all know that the overall rendering process in Flutter is Widget -> Element -> RenderObejct -> Layer, and that “the layout and rendering logic in Flutter is RenderObejct”.
RenderObejct can actually be divided into two basic subclasses:
RenderBox
RenderBox: We ** use RenderBox for layout controls **;RenderSliver
:“Mainly used in Viewport for layout.”The immediate children in Viewport also need to be RenderSliver;
So at this point you might have a question: RenderSliver is not used in SingleChildScrollView. RenderBox can also be used to slide a list.
RenderBox
RenderBox is used inside SingleChildScrollView, so naturally the entire child will be arranged and calculated during the layout process, and offset and clip are used to complete the movement effect during drawing. Such an implementation “suffers when the child is complex or too long”.
RenderSliver
RenderSliver is more complex than RenderBox. RenderSliver is designed to obtain a SliverGeometry through SliverConstraints.
-
In SliverConstraints there is remainingPaintExtent which can be used to indicate the specific size of the remaining drawable;
-
SliverGeometry also has parameters such as scrollExtent (the sliding distance), paintExtent (the drawing size), layoutExtent (the size range of the layout), visible(whether to draw), and so on.
So by using this part of the parameter, “the Viewport can achieve dynamic management, saving resources, according to the SliverGeometry to determine how much content needs to be drawn, how much content is left to draw, what layout needs to load, and so on.”
“Simply put, you can ‘lazily load’ and draw on demand, resulting in a smoother sliding experience.”
Take the ListView for example, as shown in the figure above is a ListView with a height of 701. After the actual layout is rendered, the SliverGeometry output of the SliverList will be:
- Set the height of each item to 114;
scrollExtent
It’s 2353, so the whole sliding distance is equal to 2353;paintExtent
It’s 701 becauseListView
的Viewport
It’s 701, so fromSliverConstraints
To get theremainingPaintExtent
Is 701.“So by default you only need to draw and lay out the section at height 701;”(Because the default paintExtent = layoutExtent)- The extra blue 8-9 parts of item are due to the
SliverConstraints
There will be one calledremainingCacheExtent
, which represents the area of the layout that needs to be cached ahead of time. This is the “pre-layout” area, which has a default size ofDefaultCacheExtent = 250.0;
The ListView height is 701, and the defaultCacheExtent extent is 250 by default. If the ListView height is 114, then the ListView height is 8.3. Take integers, that is, 9 items, and you get 114 * 9 = 1026. Inside the SliverList is the endScrollOffset parameter.
So, “The ListView will output a SliverGeometry with a paintExtent of 701 and a cacheExtent of 1026.”
As you can see from this example, “RenderSliver is much better and more flexible in terms of the overhead and logic of implementing slidable lists than RenderBox”, which is why RenderSliver is used in Viewport instead of RenderBox.
⚠️ Note that it is easy to make a mistake here: The ListView consists of Viewport + Scrollable and a RenderSliver, so there is only one RenderSliver in the ListView, not multiple renderslivers. To use multiple RenderSlivers, you need to use a CustomScrollView.
Finally, the CustomScrollView is actually a “slider that opens the RenderSliver array to be configurable”, for example:
- Through the use of
SliverList
+SliverGrid
You can put together a variety of sliding lists; - through
CupertinoSliverRefreshControl
+SliverList
Implement a similar iOS native drop-down refresh list;
Other built-in Slivers available include: SliverPadding, SliverFillRemaining, SliverFillViewport, SliverPersistentHeader, SliverAppbar, and more.
NestedScrollView
Why is NestedScrollView singled out? This is because NestedScrollView is not the same as the sliding list implementation described earlier.
An internal
As shown in the figure above, NestedScrollView inherits CustomScrollView and then defines a custom NestedScrollViewViewport to achieve the linkage effect.
So what’s so special about that? As shown in the following code, this is a common mode for using NestedScrollView. Do you see anything special about it?
The body of the NestedScrollView is nested with a ListView. The ListView itself is a Viewport + Scrollable + SliverList. And NestedScrollView itself also has a NestedScrollViewViewport.
“So the implementation of NestedScrollView is essentially a Viewport nested within a Viewport, with two scrollables.” And the nested ListView is placed inside the Sliver of NestedScrollView, as shown below.
There are several key objects in this, among them:
-
SliverFillRemaining: Fill up the remaining space of the Viewport. In NestedScrollView, fill up the remaining space beyond the header.
-
NestedScrollViewViewport: On the basis of the original Viewport, added a SliverOverlapAbsorberHandle parameters is a ChangeNotifier SliverOverlapAbsorberHandle itself, It is mainly used to send notifications when markNeedsLayout, such as the header part;
So NestedScrollView is essentially nested between two viewports, so how do you handle the sliding relationship between them? “That brings us to the _NestedScrollCoordinator object in NestedScrollView.”
_NestedScrollCoordinator
The _NestedScrollCoordinator implementation is complicated. A _NestedScrollCoordinator creates two _nestedScrollControllers:
_outerController
: belong to_NestedScrollViewCustomScrollView
Its controller, which is its own controller;_innerController
: belong tobody
The controller;
Within the ListView superclass ScrollView, is used by default PrimaryScrollController. Of this controller (context), Because PrimaryScrollController is an InheritedWidget.
The whole linkage sliding process is mainly related to the two _NestedScrollControllers created by _NestedScrollCoordinator:
-
The main function of _NestedScrollController is to replace ScrollPosition with _NestedScrollPosition;
-
The _NestedScrollCoordinator combines the _outer and _inner two _nestedScrollControllers (_outer and _inner are applied to the NestedScrollView and _inner, respectively) body);
-
_NestedScrollPosition Transfers gesture operations such as Drag back to the _NestedScrollCoordinator.
-
Finally, the drag and applyUserOffset methods of _NestedScrollCoordinator are used to assign inside and outside scrolling.
SliverPersistentHeader
The SliverPersistentHeader is the SliverAppBar that is commonly used in NestedScrollView. Essentially, “SliverAppBar is built on SliverPersistentHeader.”
SliverPersistentHeader has two properties, floating and pinned, the main difference is that the RenderSliver implementation is used, And ** “The final difference is actually the different output SliverGeometry” **.
In the first _SliverFloatingPinnedPersistentHeader and finally a comparison between _SliverScrollingPersistentHeader for example, as shown in the following code, On the floating and pinned Sliver, you can see that both paintExtent and layoutExtent have a minimum value.
“So,Sliver
The principle of being immobilized, in fact, is thatViewport
I got itpaintExtent
和 layoutExtent
It’s not zero, so it’s going to continue to be thisSliver
Draw the content of the corresponding area.”
Finally, note that “when you use SliverPersistentHeader to anchor the head, the body list does not know that there is a fixed area at the top.” So if you don’t do any extra processing at this point, then for the body, the paintOrigin starts at the top rather than below the fixed area.
As you can see in the GIF above, item0 doesn’t stop sliding in the orange area, but keeps sliding up because the body list doesn’t know there’s a fixed area at the top.
This can be solved by using the combination of SliverOverlapAbsorber + SliverOverlapInjector:
-
Embedded in the outer layer of the SliverPersistentHeader is a SliverOverlapAbsorber for absorbing the height of the SliverPersistentHeader;
-
The SliverOverlapInjector configured this height into the body list to let the list know that there was a fixed-height region at the top;
Android Advanced Development System Advanced notes, the latest interview review notes PDF,My lot
At the end of the article
Your likes collection is the biggest encouragement to me! Welcome to follow me, share Android dry goods, exchange Android technology. If you have any comments or technical questions about this article, please leave a comment in the comments section.