Unfinished ~
Lists everywhere are a hassle because there’s so much gameplay being developed right now, like: Basic multi-type items, adapter design, complex item view design, item update granularity hierarchy optimization, list caching design principles and optimization, list display optimization, list nesting, sliding conflict, nested scrolling, linkage with other views, etc., all need to be understood to play on any platform
The list in Flutter I personally think is a little bit better than the Android design. First of all, the name goes back to the ListView. Secondly, Flutter uses a build interface instead of an Adapter, and separator lines and things like that are also treated as items using a build. This is easier to write than Android, much easier to understand, more flexible, and less disruptive to the list itself
There are four construction methods
The ListView of Flutter has 4 construction methods, that is, 4 constructors. The core idea is to pass the component that generates the item into the constructors as an object function (various builds). The Listview of Flutter really likes the build design pattern, not only items, dividers, but many types are packaged as builds
listview
– This is a build that passes fixed data to Childen as items
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
padding: EdgeInsets.all(20),
children: <Widget>[
Text("AA"),
Text("BB"),
Text("CC"),]); }Copy the code
ListView.builder
– Build a list of items of type item. Of course, if and SLSE can also be used in itemBuilder. ItemCount is the number of items in the list
Widget build(BuildContext context) {
// TODO: implement build
return ListView.builder(
padding: EdgeInsets.all(20),
itemCount: 50,
itemBuilder: (context, index) {
return Text("item:${index}"); }); }Copy the code
ListView.separated
– List with secant lines, which the Flutter handles as a widget, added hereseparatorBuilder
To return the divider widget, but the divider features are:It doesn't include the last term
That is, the dividing line does not show the range of the list. This I really like the design, the line or separator as list item, can greatly facilitate our design list, can play temperament activity can be greatly increased, at least we can find out the type of item before and after according to the index, then consider can adopt different types of separate item, the most typical application is to insert ads, There are no changes to the list
Widget build(BuildContext context) {
// TODO: implement build
return ListView.separated(
padding: EdgeInsets.all(20),
itemCount: 50,
itemBuilder: (context, index) {
return Text("item:${index}");
},
separatorBuilder: (context,index){
return Container(
height: 1, color: Colors.pink, ); }); }Copy the code
ListView.custom()
– need to an implementation of a SliverChildDelegate components, such as SliverChildListDelegate and SliverChildBuilderDelegate. I’t here, because there is no need to, SliverChildListDelegate is listview, SliverChildBuilderDelegate is listview. Builder. The listView. custom is also used less often
Commonly used attributes
ScrollDirection
– Scroll direction: Supports horizontal scroll, default is vertical scroll
scrollDirection: Axis.horizontal,
Copy the code
reverse
– Determines if the scrolling direction is the same as the reading direction,true
When the list is displayed, scroll to the bottom and the last item displays the first data
scrollController
– The main function is to control the scroll position and listen for scroll events, I will explain in detail in a separate sectionprimary
– Whether scrolling is supported when the content is not enough to scroll; Another effect for iOS is whether the user swipes to the top of the status bar when they click. This is important, especially when using lists to build a page with a drop-down refresh, and it works if the amount of data is smaller than the height of the screen. A default scroll controller, PrimaryScrollController, is automatically set for the ListView. The advantage is that the parent can control the scrolling behavior of the scrollable components in the subtreeitemExtent
– You can directly set the height of list items to improve the performance of the listshrinkWrap
– Whether to set the ListView length according to the total length of the child components. The default value is false, so it can scroll. Rolling components are nested, shrinkWrap attribute to set up the true, can solve a rolling conflict with NeverScrollableScrollPhysicsaddAutomaticKeepAlives
– This property indicates whether to wrap a list item (child component) in an AutomaticKeepAlive component. In a lazily loaded list, if the list item is wrapped in an AutomaticKeepAlive list, it will not be retrieved when it slides out of the viewport. It uses KeepAliveNotification to save its state. If the list item maintains its KeepAlive state itself, then this parameter must be set to falseaddRepaintBoundaries
– This property indicates whether to wrap a list item (child component) in a RepaintBoundary component. Wrapping a list item in a RepaintBoundary can avoid redrawing a list item when a scrollable component is rolling, but when the overhead of redrawing a list item is very small (such as a color block, or a short text), it is more efficient not to add a RepaintBoundary. As with addAutomaticKeepAlive, this parameter must be set to false if the list item maintains its KeepAlive state itselfcacheExtent
– A cacheExtent is the number of items in the list that are displayed, and the package is preloaded. We can calculate the proper configuration based on the list length and item’ height
ScrollController Scroll control
If you look at the name “Controller” that means it’s a function of controlling lists. ScrollController’s main function is to listen for scroll status and provide methods to scroll to a specified position in the list
1. ScrollController constructor
ScrollController({
double initialScrollOffset = 0.0.// Initial scroll position
this.keepScrollOffset = true.// Whether to save the scroll position. })Copy the code
ScrollController requires us to create a new object. With these two parameters in the constructor, we can set where the selection list starts. With itemExtent, a fixed height for each column is a good idea
2. Core parameters and methods:
offset
– The current scroll position. Note that this data is a cumulative value, not the amount of each scrolljumpTo(double offset)
– Scroll to the specified position without drawinganimateTo(double offset,...)
– Scroll to the specified position to draw, you can specify the time
Use 3.
- I’m going to create a new scrollController object
- Add listeners to scrollController in initState
- Set the scrollController to the ListView in the layout
class TestWidgetState extends State<TestWidget> {
var scrollController = ScrollController();
@override
void initState() {
super.initState();
scrollController.addListener(() {
print("Current scroll position:${scrollController.offset}");
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView.builder(
padding: EdgeInsets.all(20),
physics: BouncingScrollPhysics(),
itemCount: 50,
controller: scrollController,
itemBuilder: (context, index) {
return Container(
width: 50,
height: 30,
alignment: Alignment.center,
child: Text("item:${index}")); }); }void dispose() {
// To avoid memory leaks, dispose needs to be called
scrollController.dispose();
super.dispose(); }}Copy the code
Characteristics of 4.
- ScrollController can add more than one Listener, from its methods scrollController. AddListener can see come out, walk into _listeners is a collection types in the source code
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
Copy the code
- Scrollcontroller. offset scrollController.offset scrollController.offset scrollController.offset scrollController.offset scrollController.offset scrollController.offset scrollController.offset scrollController.offset scrollController
I/flutter (7435): Current scroll position: 1.16666666666667 Current scroll position: 3.71212121212848 I/flutter (7435) Current scroll position: 5.5303030303041 I/flutter (7435) Current scroll position: 7.3484848484911 I/flutter (7435): Current scroll position: 8.07575757575766Copy the code
5. JumpTo, animateTo
These two methods are used to specify the scroll of the list at the specified position. Currently, we can only scroll to the specified px
jumpTo
– This method is easy to use, only an offset argument
scrollController.jumpTo(500);
Copy the code
animateTo
– This is different, it is drawn, you can specify the time and interpolator, but both must be set together, not individually
scrollController.animateTo(500,duration: Duration(milliseconds: 300), curve: Curves.ease);
Copy the code
Here’s a GIF demo:
6.
ScrollPosition
ScrollController can be set to multiple scrollable components. The idea is that the ScrollController generates a ScrollPosition for each scrollable component that is set to it. The ScrollController Positions property stores this data
Naturally, all data about scrolling is stored in the ScrollPosition, such as the scrolling value:
double get offset => position.pixels;
Copy the code
The value offset returns the scrolling data of the displayed or topmost ScrollController, which is incorrect if the ScrollController is bound to multiple scrollable components. Note that the jumpTo, animateTo methods will roll all the ScrollController’s bound scrollable components like numbers
Of course, we don’t have nothing to do:
controller.positions.elementAt(0).pixels
controller.positions.elementAt(1).pixels
Copy the code
This will get the scrollvalue of unscrollable components, but we need to know exactly what order scrollable components were originally inserted
NotificationListener Scrolling listener
We used scrollController to listen for list scrolling, but here we have another idea, inheriting the Android idea of nested scrolling
NestedScrollingParent and NestedScrollingChild. NestedScrollingChild sends scroll events. NestedScrollingParent controls the propagation and value of scroll events
Flutter follows this idea. In the Widget tree, a scrollable widget passes a notification of a scroll event up one by one when it is scrollable. We can monitor this notification through NotificationListener. A NotificationListener is also a widget, so long as the NotificationListener is laid out at a higher level than the ListView, it doesn’t matter how many levels it is
All the scrolling widgets in the Flutter, such as ScrollView, ListView, PageView, etc. can use NotificationListener to listen for scrolling events
- The classic android apps are:
Behavior CoordinatorLayout RecycleView
- In The Flutter:
NotificationListener listview
1. Listening method
The onNotification(ScrollNotification Notification) method in NotificationListener gets the scroll event, and the value is wrapped in the ScrollNotification parameter. Requires that we return a Boolean value:
true
Notifcation stops here. We intercept scroll events and do not continue to upload scroll events, but do not affect the listView widget’s scrollingfalse
– Then the Notification will continue to be passed to the outer widget
2. Monitored data
The data for a rolling event is of type ScrollNotification, and the parameters are in its metrics parameters:
metrics.pixels
– The current location, starting with 0. Default is 0metrics.atEdge
– Whether it is at the top or the bottommetrics.axis
– Roll vertically or horizontallymetrics.axisDirection
– Scroll in down or up direction, test, both are downmetrics.extentAfter
– How far the bottom of the widget is from the bottom of the listmetrics.extentBefore
– How far is the top of the widget from the top of the listmetrics.extentInside
– Indicates the length of the list within the scope of the widgetmetrics.maxScrollExtent
– Maximum scroll distance, list length – Widget lengthmetrics.minScrollExtent
– Minimum rolling distancemetrics.viewportDimension
– the length of the widgetmetrics.outOfRange
– Whether it crossed the border
3. After the test
The interpretation of the documents is uncertain, and we’ll have to try it ourselves
axisDirection
– It has always been down. It is more accurate for us to judge by ourselves according to the change of the valuepixels
– Pull up to load moreIt's getting bigger and bigger
, pull down to refreshIt gets smaller and smaller, all the way to 0
atEdge
– it is possible to determine whether it is at the top or at the bottom. It is true when it is at the top or at the bottomRolling numbers
– extentAfter + extentBefore really = maxScrollExtentThe length of the widget
– viewportDimension and extentInside have the same valueoutOfRange
– Bottom or top is false by default, and will only become true if we continue scrolling with the physics properties. Note the critical value
4. Sample:
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification) {
print("pixels:${notification.metrics.pixels}");
print("atEdge:${notification.metrics.atEdge}");
print("axis:${notification.metrics.axis}");
print("axisDirection:${notification.metrics.axisDirection}");
print("extentAfter:${notification.metrics.extentAfter}");
print("extentBefore:${notification.metrics.extentBefore}");
print("extentInside:${notification.metrics.extentInside}");
print("maxScrollExtent:${notification.metrics.maxScrollExtent}");
print("minScrollExtent:${notification.metrics.minScrollExtent}");
print("viewportDimension:${notification.metrics.viewportDimension}");
print("outOfRange:${notification.metrics.outOfRange}");
print("____________________________________________.");
return true;
},
child: Container(
child: Stack(
children: <Widget>[
ListView.builder(
padding: EdgeInsets.all(20),
physics: BouncingScrollPhysics(),
itemCount: 50,
itemExtent: 35,
controller: scrollController,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
child: Text("item:${index}")); }, ), Positioned( left:20,
top: 20,
child: RaisedButton(
child: Text("Click and scroll"),
onPressed: () {
scrollController.animateTo(500,
duration: Duration(milliseconds: 300),
curve: Curves.ease);
print("AAA"); },),),],),); }Copy the code
physics
This is the Behavior of the Flutter, depending on the nested scrolling mentioned above, it allows us to do additional operations on the entire list when it reaches the top or bottom, typically by dropping to the top and then bouncing back
Code:
physics: BouncingScrollPhysics(),
Copy the code
The system provides several default implementations, and it is not clear if they can be customized:
NeverScrollablePhysics
– Lists cannot be scrolledBouncingScrollPhysics
– Rebound effect, which is the effect of the GIF aboveClampingScrollPhysics
– System default, at the end of the display water ripplePageScrollPhysics
– For PageView, if the listView is set, there will be a big bounce and bounce at the end of the slideAlwaysScrollableScrollPhysics
– Lists are always scrollable. There is a rebound effect on iOS, but not on Android. If the primary is set to false, but set AlwaysScrollableScrollPhysics, list at this time can be slidingFixedExtentScrollPhysics
– This must be done in conjunction with the responding widget, listView is not available, more details will be written later
: general
Scroll data cache
The ListView inherits from Scrollable and is itself a StatefulWidget, which naturally holds data. The scroll offset is also saved, so the main ListView is not removed from the Widget tree and the scroll state is always there
Sometimes the ListView is removed due to changes in the widget tree. For example, the State of the scrollable component of the TabBarView is destroyed when the Tab is switched. If we want to show the last scrollable position when we switch back and forth between tabs, we must cache the scrollable offset
Here we use PageStorage, which is a component used to save page (routing) related data, PageStorage declaration cycle is the entire app, no matter how many pages can save data without loss, The core is to set the PageStorageKey to the wigdet constructor
We’re going to set the PageStorageKey of each TAB to its own string
new TabBarView(
children: myTabs.map((Tab tab) {
new MyScrollableTabView(
key: new PageStorageKey<String>(tab.text), // like 'Tab 1'
tab: tab,
),
}),
)
Copy the code
That’s all we have right now
Fixed head widget
We always need to fix the header in the list, and the header view doesn’t scroll down the list. Here’s the easiest way to do it: Column + expanded. Column inherits from Flex and automatically adjusts the widget length, and Expanded automatically stretches the component size, so it makes sense to use both
Widget build(BuildContext context) {
// TODO: implement build
return Column(
children: <Widget>[
Text("I am the head"),
Expanded(
child: ListView.builder(
padding: EdgeInsets.all(20),
physics: BouncingScrollPhysics(),
cacheExtent: 10,
itemCount: 50,
itemExtent: 35,
controller: scrollController,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
child: Text("item:${index}")); },),),],); }}Copy the code
Common design ideas
- with
ListView.separated
Insert ads