The ListView and GridView in flutter inherit from the BoxScrollView and the BoxScrollView inherit from the ScrollView.
basis
1, the ScrollView
const ScrollView({ Key key, this.scrollDirection = Axis.vertical, this.reverse = false, this.controller, bool primary, ScrollPhysics, this.shrinkwrap = false, this.center, this.anchor = 0.0, this.cacheextent, this.semanticChildCount, this.dragStartBehavior = DragStartBehavior.start, this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, this.restorationId, this.clipBehavior = Clip.hardEdge, })Copy the code
-
AxisDirection Scrolling direction.
-
The reverse parameter indicates that the scrolling direction is reversed, not vertical to horizontal, but vertical to scroll down by default. The reverse parameter is set to false, and the scrolling direction is changed to up, and horizontal to left
-
Controller is the scroll controller, which can monitor the roll position and set the scroll position
-
Controller cannot be set when primary is set to True, because controller uses PrimaryScrollController when primary is True. The advantage of this mechanism is that the parent component can control the scrolling behavior of scrollable components in the child tree, for example, Scaffolding uses this mechanism to go back to the top of the navigation bar in iOS
-
Physics represents the physical rolling properties of a scrollable component
- ClampingScrollPhysics: Android shimmer effect.
- BouncingScrollPhysics: Elastic effect in iOS.
-
DragStartBehavior: how and when to process the dragStartBehavior
2, the Scrollbar
The Scrollbar is a Material style scroll indicator (Scrollbar). To add a Scrollbar to a scrollable component, simply use the Scrollbar as any parent component of the scrollable component
Scrollbar(
child: ListView.builder(
reverse: false,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
height: 45,
alignment: Alignment.center,
child: Text('$index'),
),
);
},
itemCount: 30,
itemExtent: 50,
),
)
Copy the code
3, ScrollController
The ScrollController constructor looks like this:
ScrollController({double initialScrollOffset = 0.0, // The initial roll position this.keepScrollOffset = true,// Whether to save the roll position... })Copy the code
Common properties and methods:
- Offset: current scroll position of the scrollable component.
- JumpTo (Double offset), animateTo(Double offset,…) : These two methods are used to jump to a specified location. They differ in that the latter performs an animation while the former does not.
The scrollListener ScrollController indirectly inherits from Listenable, and we can listen for scroll events according to the ScrollController
controller.addListener(()=>print(controller.offset))
Copy the code
ScrollPosition
ScrollPosition is used to save the ScrollPosition of a scrollable component. A ScrollController object can be used by multiple scrollable components simultaneously. The ScrollController creates a ScrollPosition object for each scrollable component. These Scrollpositions are stored in the Positions property of the ScrollController (List). ScrollPosition is the object that actually holds the slide position information, and offset is just a convenience property
double get offset => position.pixels;
Copy the code
A ScrollController can correspond to multiple scrollable components, but some operations, such as reading the scroll position offset, need one to one! For example, if a ScrollController is used by two scrollable components at the same time, we can read their scrollpositions separately as follows
controller.positions.elementAt(0).pixels
controller.positions.elementAt(1).pixels
Copy the code
We can through the controller. The positions. The length to determine the controller is used by several scrollable components.
class _ScrollControllerPageState extends State<ScrollControllerPage> { ScrollController _controller = new ScrollController(); bool showToTopBtn = false; @override void initState() {super.initState(); _controller.addListener(() {print(_controller.offset); If (_controller.offset < 1000 && showToTopBtn) {setState(() {showToTopBtn = false; }); } else if (_controller.offset >= 1000 && showToTopBtn == false) { setState(() { showToTopBtn = true; }); }}); } @override void dispose() {// To avoid memory leaks, call _controller.dispose _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Scrollbar( child: ListView.builder( itemCount: 100, itemExtent: 50.0, // It is a good habit to specify the height explicitly when the item height is fixed. (context, index) { return ListTile(title: Text("$index"),); } ), ), floatingActionButton: ! showToTopBtn ? null : FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () {// return to the top of _controller.animateto (.0, duration: duration (milliseconds: 200), curve: Curves. Ease); })); }}Copy the code
1, the ListView
1, SingleChildScrollView
SingleChildScrollView can accept only one child component.
SingleChildScrollView is a scrolling component that can only contain a single component. If there is a lot of content, it is recommended to use ListView, etc., because SingleChildScrollView does not have “lazy loading” mode. Not performing as well as ListView.
Container(
child: SingleChildScrollView(
child: Column(
children: List.generate(10, (index) {
return Container(
height: 100,
color: randomColor(),
);
}).toList(),
),
),
)
Copy the code
2, ListView
ListView is a scrolling component, one of the most commonly used components for displaying lists of large amounts of data. There are four ways to initialize a ListView
- 1. Default: ListView()
- 2, ListView. Builder
- 3, ListView. Separated
- 4, ListView. Custom
Let’s introduce some parameters
- TemExtent: If this parameter is not null, the length of children will be forced to be itemExtent. Here, “length” refers to the length of the child component along the scrolling direction. That is, if the scrolling direction is vertical, itemExtent represents the height of the child component. If scrolling is horizontal, itemExtent represents the width of the child component. In the ListView, specifying itemExtent is more efficient than letting subcomponents determine their own length. This is because specifying itemExtent allows the scrolling system to know the length of the list in advance without having to evaluate it each time a subcomponent is built. This is especially true if the scrolling position changes frequently (the scrolling system needs to calculate the list height frequently).
- ShrinkWrap: This property indicates whether to set the ListView length based on the total length of child components. The default is false. By default, the ListView takes up as much space as possible in the scrolling direction. ShrinkWrap must be true when the ListView is in a container with no boundaries (scroll direction).
- AddAutomaticKeepAlives: This property indicates whether to wrap the list item (child component) in the AutomaticKeepAlive component. Typically, in a lazy loaded list, if you wrap a list item in an AutomaticKeepAlive, it won’t be GC (garbage collected) when the list item slides out of the viewport, it uses KeepAliveNotification to store its state. This parameter must be set to false if the list item itself maintains its KeepAlive state.
- 4. AddRepaintBoundaries: This property indicates whether the list items (subcomponents) are wrapped within the RepaintBoundary component. Wrapping a list item in a RepaintBoundary can avoid list item redrawing while a scrollable component is scrolling, but it is more efficient not to add a RepaintBoundary when the overhead of list item redrawing is very small (such as a color block, or a short text). As with addAutomaticKeepAlive, this parameter must be set to false if the list item maintains its own KeepAlive state.
1. Default constructor
The default constructor takes a children argument, which takes a List of widgets. This approach works well with a small number of child components because it requires all children to be created in advance (which is a lot of work), rather than waiting until the child widgets are actually displayed, meaning that the ListView built with the default constructor does not apply the lazy loading model based on Sliver. The ListView created this way is essentially the same as the ListView created using SingleChildScrollView+Column
ListView(padding: const EdgeInsets. All (20.0) shrinkWrap: true, children: [Text (" default constructor "), Text (" default constructor "), Text (" default constructor "), Text (" default constructor "),],)Copy the code
2, ListView. Builder
The ListView.builder is suitable for a large (or infinite) list of items, because it will only be created when the subcomponents are actually displayed, meaning that the ListView created by this constructor supports the lazy loading model based on Sliver
Listview. builder({// ListView public arguments have been omitted... @required IndexedWidgetBuilder itemBuilder, int itemCount, ... })Copy the code
- ItemBuilder: This is a builder of list items, of type IndexedWidgetBuilder, and returns a widget. When the list scrolls to a specific index position, the builder is called to build the list item.
typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index);
Copy the code
- ItemCount: Number of items in a list. If null, an infinite list
Listview. builder(itemCount: 100, itemExtent: 100.0, itemExtent: 100.0, itemExtent: 100.0, itemExtent: 100.0) (BuildContext context, int index){ return CatItem(); })Copy the code
We create a catItem as a cell
class CatItem extends StatelessWidget { @override Widget build(BuildContext context) { return Container( height: 100, color: Colors.white, padding: EdgeInsets.all(15), child: Row( children: <Widget>[ ClipRRect( borderRadius: BorderRadius.circular(4), child: Image.asset( 'images/cat.png', fit: BoxFit.cover, width: 80, height: 80, ), ), Padding(padding: EdgeInsets.only(left: 15)), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[Text(' cat ', style: TextStyle(fontSize: 18, fontWeight: fontweight.w500, color: RGB (51, 51, 51)) Color(0xFF333333),),), Text('2021.01.21', style: TextStyle(fontSize: 13, Color: Color(0xFF999999),),)],), Padding(Padding: EdgeInsets. Only (top: 20)), Text(' I want to learn the Flutter list ', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15, color: Color(0xFF999999), ), ), ], ), ), ], ), ); }}Copy the code
3, ListView. Separated
Separated components Can be added between generated list items by Adding a separatorBuilder parameter, which is a separatorBuilder, over ListView.builder.
ListView.separated( itemBuilder: (BuildContext context, int index){ return CatItem(); }, separatorBuilder: (BuildContext context, int index){ return index%2==0? Divider(color: Colors.red):Divider(color: Colors.black); }, itemCount: 100)Copy the code
4, ListView. Custom
Listview.custom is nothing special.
Let’s introduce a few list styles here
style
1, ListTile
Const ListTile({Key Key, this.leading, // item header this.title, // item header this.subtitle, // item subtitle this.trailing, // item subtitle this.trailing, // item subtitle this.trailing, This.isthreeline = false, // If the three lines of the item show this.dense. This. enabled = true, this.onTap, // item onTap event this.onLongPress, // item onLongPress eventthis. selected = false, // item is selected state})Copy the code
ListView.builder( itemCount: 50, itemBuilder:(BuildContext context, int index){ return ListTile( leading: New Icon(Icons. Keyboard), title: new Text("Flutter"), subtitle: new Text("ListTile learning "), trailing: New Icon(Icons. Arrow_forward_ios), contentPadding: EdgeInsets. Symmetric (horizontal: 20.0), enabled: true, onTap: () = > print (" $index is clicked "), onLongPress: () = > print (" $index was long press the "),); })Copy the code
2. Card
Card is the easiest way to make your list look cool. You just have to wrap Card around the ListTile
ListView.builder( itemCount: 50, itemBuilder:(BuildContext context, int index){ return Card( child: ListTile( leading: New Icon(Icons. Keyboard), title: new Text("Flutter"), subtitle: new Text("ListTile learning "), trailing: New Icon(Icons. Arrow_forward_ios), contentPadding: EdgeInsets. Symmetric (horizontal: 20.0), enabled: true, onTap: () = > print (" $index is clicked "), onLongPress: () = > print (" $index was long press the "),),); })Copy the code
Refresh & load
In real products, we will also encounter requirements such as drop-down refresh and drop-down load of lists. Next, let’s learn how to implement this kind of interaction in Flutter.
1. Refreshindicators
The drop-down refresh
Pull down refresh we can use a RefreshIndicator component
/** *const RefreshIndicator ({Key Key, @required this.child, this.displacement: 40.0, // The distance to trigger the pull-down refresh // the pull-down callback method, the method needs to have async and await keywords, without await, the refresh icon will disappear immediately, // No async, @required this.onRefresh, this.color, // The foreground color of the progress indicator defaults to the system theme color this.backgroundColor, / / background color enclosing notificationPredicate: defaultScrollNotificationPredicate,}) * /Copy the code
It is very easy to implement drop-down refresh of lists in Flutter because Flutter encapsulates a RefreshIndicator component and is very convenient to use.
Future onRefresh() {return future.delayed (Duration(seconds: 1), () {toast.show (' currently updated ', context); }); } @override Widget build(BuildContext context) { return RefreshIndicator( onRefresh: onRefresh, color: red, child: ListView.builder( itemBuilder: (context, index){ return Card( child: CatItem()); }, itemCount: 10, ), ); }Copy the code
Pull on loading
In addition to pull-down refreshing, pull-up loading is another list operation that is often encountered. This time, however, Flutter does not provide a ready-made component that can be invoked directly like a drop-down refresh. The pull-up load interaction needs to be done by ourselves
Here we’re going to use the ScrollController to listen for various states
- 1. Various initialization data
Bool isLoading = false; // ListView controller scrollController_scrollController = ScrollController(); List<String> data = new List<String>. Generate (10, (int index) => "${index}");Copy the code
- 2. Add a listener to ScrollController
@override void initState() { super.initState(); / / give added to scroll the list to monitor _scrollController addListener () {/ / slide to the bottom of the key to judge the if (! IsLoading && _scrollController. Position. Pixels. > = _scrollController position. MaxScrollExtent) {/ / load the data setState (() { isLoading = true; loadMoreData(); }); }}); }Copy the code
- 3. Load more data
/// Load more Future loadMoreData(){return future.delayed (Duration(seconds: 1), (){setState(() {isLoading = false; List<String> list = new List<String>.generate(10, (int index) => "${index}"); data.addAll(list); }); }); }Copy the code
- 4. Load more widgets at the bottom
Widget renderBottom() { if(this.isLoading) { return Container( padding: EdgeInsets.symmetric(vertical: 15), child: Row (mainAxisAlignment: mainAxisAlignment center, children: < widgets > [Text ('... 'in trying to load, style: TextStyle (fontSize: 15, color: Color(0xFF333333), ), ), Padding(padding: EdgeInsets.only(left: 10)), SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 3), ), ], ), ); } else { return Container( padding: EdgeInsets.symmetric(vertical: 15), alignment: Alignment.center, child: TextStyle(fontSize: 15, color: color (0xFF333333),),); }}Copy the code
- 5. Add related judgments to listView
@override Widget build(BuildContext context) { return RefreshIndicator( onRefresh: onRefresh, color: red, child: ListView.builder( controller: _scrollController, itemCount: data.length + 1, itemBuilder: (context, index){ if(index < data.length){ return Card( child: CatItem()); }else{ return renderBottom(); }},),); }Copy the code
2. Refresh component (flutter_easyrefresh)
EasyRefresh makes it easy to implement pull-down refresh and pull-up load operations on Flutter applications. It supports almost all Flutter controls
- 1. Add dependencies in pubspec.yaml
Flutter_easyrefresh: path flutter_easyrefresh: path Project path dependencies: / / git way flutter_easyrefresh: git: url: git://github.com/xuelongqy/flutter_easyrefresh.gitCopy the code
- 2. Add EasyreFresh to the layout file
import 'package:flutter_easyrefresh/easy_refresh.dart'; . EasyRefresh(Child: ScrollView(), onRefresh: () async{.... }, onLoad: () async { .... Custom (slivers: <Widget>[], onRefresh: () async{.... }, onLoad: () async { .... },) // EasyRefresh. Builder (Builder: (context, physics, header, footer) {return CustomScrollView(physics: physics, slivers: <Widget>[ ... header, ... footer, ], ); } onRefresh: () async{ .... }, onLoad: () async { .... },)Copy the code
- 3. Refresh and load actions are triggered
EasyRefreshController _controller = EasyRefreshController(); . EasyRefresh( controller: _controller, .... ) ; . _controller.callRefresh(); _controller.callLoad();Copy the code
- 4. Control load and refresh completion
EasyRefreshController _controller = EasyRefreshController(); . EasyRefresh( enableControlFinishRefresh: true, enableControlFinishLoad: true, .... ) ; . _controller.finishRefresh(success: true); _controller.finishLoad(success: true, noMore: false);Copy the code
2, the GridView
The GridView has five constructors:
- The GridView.
- The GridView. Builder,
- The GridView. Count,
- GridView.extent
- GridView.custom
1. Default constructor
The default constructor source is as follows:
GridView({ Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, bool primary, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, @required SliverGridDelegate gridDelegate, Bool addAutomaticKeepAlives = true, bool addRepaintBoundaries = true, double cacheExtent, List<Widget> children = const <Widget>[], })Copy the code
And the other parameters, you can kind of get a sense of what they mean, but there’s one proxy, gridDelegate. The gridDelegate parameter, of type SliverGridDelegate, controls how the GridView child components are arranged.
SliverGridDelegate is an abstract class that defines GridView Layout-related interfaces that subclasses need to implement to implement specific Layout algorithms.
A subclass of Flutter provides two SliverGridDelegate SliverGridDelegateWithFixedCrossAxisCount and SliverGridDelegateWithMaxCrossAxisExtent, We can just use it
SliverGridDelegateWithFixedCrossAxisCount
This subclass implements a layout algorithm with a fixed number of child elements on the horizontal axis. Its constructors are:
SliverGridDelegateWithFixedCrossAxisCount({
@required double crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
Copy the code
- CrossAxisCount: the number of columns that have children in a row;
- MainAxisSpacing: Spacing in the main axis direction;
- CrossAxisSpacing: spacing along the sub-axis;
- ChildAspectRatio: Width to height ratio of child elements
class GridViewPage extends StatelessWidget { const GridViewPage({Key key}) : super(key: key); _createGridViewItem(Color color){ return Container( height: 80, color: color, ); } @override Widget build(BuildContext context) { return Container( child: GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing:20, crossAxisSpacing:10 ), children: [ _createGridViewItem(Colors.primaries[0]), _createGridViewItem(Colors.primaries[1]), _createGridViewItem(Colors.primaries[2]), _createGridViewItem(Colors.primaries[3]), _createGridViewItem(Colors.primaries[4]), _createGridViewItem(Colors.primaries[5]), _createGridViewItem(Colors.primaries[6]), _createGridViewItem(Colors.primaries[7]), ], ) ); }}Copy the code
2, the GridView. Count
The GridView. Count used SliverGridDelegateWithFixedCrossAxisCount inside the constructor, through which we can quickly create transverse fixed number of child elements of the GridView
@override
Widget build(BuildContext context) {
return Container(
child: GridView.count(
crossAxisCount: 3,
mainAxisSpacing:20,
crossAxisSpacing:10,
children: [
_createGridViewItem(Colors.primaries[0]),
_createGridViewItem(Colors.primaries[1]),
_createGridViewItem(Colors.primaries[2]),
_createGridViewItem(Colors.primaries[3]),
_createGridViewItem(Colors.primaries[4]),
_createGridViewItem(Colors.primaries[5]),
_createGridViewItem(Colors.primaries[6]),
_createGridViewItem(Colors.primaries[7]),
],
)
);
}
Copy the code
3, the GridView. Among
The GridView. Among the constructor for internal use SliverGridDelegateWithMaxCrossAxisExtent, through which we can quickly create a longitudinal axis element for a fixed maximum length of the GridView
SliverGridDelegateWithMaxCrossAxisExtent
The subclass implements a layout algorithm whose horizontal axis child elements are fixed maximum length. Its constructor is:
SliverGridDelegateWithMaxCrossAxisExtent ({double maxCrossAxisExtent, double mainAxisSpacing = 0.0, Double crossAxisSpacing = 0.0, double childAspectRatio = 1.0,})Copy the code
4, the GridView. Builder
Each GridView requires an array of widgets as its children. These methods build all the child widgets in advance, so they only work when there are a small number of child widgets. We can dynamically create child widgets using gridView.Builder. Gridview.builder must specify two parameters
GridView.builder(
...
@required SliverGridDelegate gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
)
Copy the code
class MyWidget extends StatelessWidget { Widget _itemBuilder(BuildContext context, int index){ return Container( height: 80, color: Colors.red, ); } final SliverGridDelegate _gridDelegate = SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing:20, crossAxisSpacing:10 ); @override Widget build(BuildContext context) { return Container( child: GridView.builder( gridDelegate: _gridDelegate, itemCount: 10, itemBuilder: _itemBuilder) ); }}Copy the code
5. Waterfall flow
The Flutter gridview is a layout that supports different sizes of multi-column grids, and works on Android, iOS, and Web, where each cell can be called a Tile. It has the following characteristics:
- Multiple columns can be set up like a GridView
- You can set the number or ratio of tiles to be occupied on the vertical and main axes (e.g. CrossAxisCount: 4,
Staggeredtile. fit(2) means there are two columns on the vertical axis. If staggeredtile. fit(1) means there are four columns on the vertical axis. 2.5:3) indicates that there are two columns on the vertical axis and the size of the first Tile along the main axis is 2.5:3 of the height of the other tiles.
- You can set the row spacing and column spacing between tiles
- Can be used within CustomScollerView (with shrinkWrap:true and ScrollerController associated with two widgets)
- Tile can adjust height in the main axis (this is better than GridView, no aspect ratio, no overflow)
Pubspec.yaml adds dependencies
dependencies:
flutter_staggered_grid_view:
Copy the code
Guide package
The import 'package: flutter_staggered_grid_view/flutter_staggered_grid_view. Dart';Copy the code
The use of StaggeredTile
- Staggeredtile. count: Fixed the number on the vertical and main axes
- Staggeredtile. extent: The amount on the vertical axis and the maximum extent on the main axis
- Staggeredtile. fit: Quantity on the vertical axis
StaggeredGridView.countBuilder( crossAxisCount: 4, itemCount: 8, itemBuilder: (BuildContext context, int index) => new Container( color: Colors.red, child: new Center( child: new CircleAvatar( backgroundColor: Colors.white, child: new Text('$index'), ), )), staggeredTileBuilder: (int index) => new StaggeredTile.count(2, index.isEven ? 2:1), mainAxisSpacing: 4.0, crossAxisSpacing: 4.0,)Copy the code
Code address: github.com/SunshineBro…