preface

It has been much easier to look at the Flutter foundation and layout since the last blog post, with a general familiarity with the simple use of Flutter foundation widgets and Flutter layout widgets in your project, This blog will continue to talk about blog length Flutter about the scroll Widget [most used in the project] —- which is very important

  • Flutter scrolls the Widget-ListView component
  • Flutter scroll widget-GridView component
  • Flutter rolling combination – Slivers component
  • A Flutter listens for scroll events

Hopefully, you can follow suit and learn about Flutter in one or two months.Just write! Just write! Began to write, certainly can have the development level! I will continue to provide quality blog content to you, currently covering objective-C, Swift, Flutter, small program development.Welcome to praise blog and pay attention to myself, common progress is the purpose of sa ~~~

ListView component

When there is a large amount of data on the mobile terminal, it is displayed through lists, such as commodity data, chat list, contacts, moments of friends, etc. In Android, you can use ListView or RecyclerView to achieve; In iOS, you can do this with UITableView. There is also a corresponding list Widget in Flutter, the ListView.

2.1 ListView base

2.1.1 Basic Use of ListView

The ListView can arrange all its child widgets in one direction (vertical or horizontal, vertical by default). One of the easiest ways to do this is to simply place all the child widgets that need to be sorted in the Children property of the ListView.

ListView code walkthrough

To create some spacing between the text, a Padding Widget is used

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { final TextStyle textStyle = TextStyle(fontSize: 20, color: Colors.redAccent); @override Widget build(BuildContext context) { return ListView( children: <Widget>[ Padding( padding: Const EdgeInsets. All (9.0), child: Text(" the human is in the nature of their own incapacity. ", style: textStyle),), Padding(Padding: All (9.0), const EdgeInsets. All (9.0), child: Text(" There is no room for error in this world; there is no room for error in this world; there is no room for error in this world; Text-style),), paddING-style (const edgeinset.all (9.0), child: Text(" I live in this world, I want to see something interesting. ", style: textStyle), ), ], ); }}Copy the code

The results

2.1.2 Use of ListTitle

In development, it’s common to see a list that has an Icon or image, a Title, a Subtitle, and an Icon at the end.

At this point, you can use ListTile.

Code walkthroughs:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return ListView( Children: <Widget>[ListTile(leading: Icon(Icons. People, size: 36,), title: Text(" Icons "), subtitle: Text(" Icons "), trailing: Icon(icon.arrow_forward_ios),), ListTile(leading: Icon(icon.email, size: 36,), title: Trailing: Icon(Icons. Arrow_forward_ios),), ListTile(leading: Icon(Icons. Message, size: 36,), title: Text(" message "), subtitle: Text(" message details "), trailing: Icon(Icons. Arrow_forward_ios),), ListTile(leading: Icon(Icons. Map, size: 36,), title: Text(" address "), subtitle: Trailing: Icon(Icons. Arrow_forward_ios),)],); }}Copy the code

Running results:

2.1.3 Vertical scrolling

You can control the scrolling direction of a view by setting the scrollDirection parameter.

Implement a horizontal scroll with the following code:

  • Note that you need to set width to the Container, otherwise it will have no width and will not display properly.

  • Or we can set an itemExtent to the ListView, which sets the width of each item in the scrolling direction.

Code to rehearse

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return ListView( scrollDirection: Axis.horizontal, itemExtent: 200, children: <Widget>[ Container(color: Colors.redAccent,width: 200,), Container(color: Colors.blue,width: 200,), Container(color: Colors.yellow,width: 200,), Container(color: Colors.purple,width: 200,), Container(color: Colors.pink,width: 200,), Container(color: Colors.orange,width: 200,,,); }}Copy the code

Running results:

2.2 ListView. Build

There is a problem with passing all child widgets through children in the constructor: all child widgets are created by default. Building all the widgets at once makes no difference to the user, but it creates performance issues for our application and increases the rendering time of the first screen. You can build child widgets by listView.build to provide performance.

2.2.1 Listview. build Basic use

Listview. build is suitable for scenarios where there are many child widgets. This constructor hands the creation of the child widgets to an abstract method that the ListView manages, and the ListView creates the child widgets when it really needs to, rather than initially initializing them all.

This method takes two important parameters:

  • ItemBuilder: Method for creating list items. The ListView automatically calls this method to create the corresponding child Widget when the list scrolls to the corresponding location. The type is IndexedWidgetBuilder, which is a function type.

  • ItemCount: Indicates the number of list items. If empty, the ListView is an infinite list.

Code walkthrough 1:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.builder( itemCount: 100, itemExtent: 80, itemBuilder: (BuildContext context, int index){ return ListTile(title: Text(" $index"), subtitle: Text($index); }); }}Copy the code

Operating Result 1:

Code walkthrough 2:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return ListView( children: List.generate(100, (index) { return ListTile( leading: Icon(Icons.people), trailing: Icon(Icons. Delete), title: Text(" Icons + 1}"), subtitle: Text(" Icons + 1}"),); })); }}Copy the code

Running Result 2:

2.2.2 ListView. Separated

Separated lists generate separators between list items, except for the separatorBuilder parameter, which is a separator generator.

Requirements: add a blue underscore to odd lines and a red underscore to even lines:

Code walkthroughs:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { Divider blueColor = Divider(color: Colors.blue,); Divider redColor = Divider(color: Colors.red,); @override Widget build(BuildContext context) { return ListView.separated( itemBuilder: (BuildContext context, int index){ return ListTile( leading: Icon(Icons.people), title: The Text (" ${index + 1} contact "), the subtitle: Text (" ${index + 1} contact phone "),); }, separatorBuilder: (BuildContext context, int index){ return index % 2 == 0 ? redColor : blueColor; }, itemCount: 100 ); }}Copy the code

Running results:

The GridView components

GridView is used to display multi-column displays, which are also common in development, such as host lists in live streaming apps, product lists in e-commerce, and so on. Flutter can be implemented using a GridView similar to ListView.

3.1 GridView constructor

Learn how to use the GridView constructor

One way to use a GridView is to use a constructor that takes a special parameter to compare to the ListView: GridDelegate is used to control the number or width of items on the cross axis. The type we need to pass in is SliverGridDelegate, but it is an abstract class, so we need to pass in its subclasses:

1, SliverGridDelegateWithFixedCrossAxisCount

SliverGridDelegateWithFixedCrossAxisCount({ @required double crossAxisCount, Double mainAxisSpacing = 0.0, double crossAxisSpacing = 0.0 Double childAspectRatio = 1.0, // Width ratio of child widgets})Copy the code

Code walkthrough 1:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter Widget"),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 1.0), children: getGridWidgets(),); } List<Widget> getGridWidgets() { return List.generate(100, (index){ return Container( color: Colors.purple, alignment: Alignment(0,0), child: Text("item$index", style: TextStyle(fontSize: 20, color: color.white)),); }); }}Copy the code

Running results:

2, SliverGridDelegateWithMaxCrossAxisExtent

SliverGridDelegateWithMaxCrossAxisExtent ({double maxCrossAxisExtent, / / the item width of the cross shaft double mainAxisSpacing = 0.0, // double crossAxisSpacing = 0.0, // crossAxisSpacing double childAspectRatio = 1.0, // child Widget width ratio})Copy the code

Code walkthroughs:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("GridView scroll "),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return GridView( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 150, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 1.0), children: getGridWidgets(),); } List<Widget> getGridWidgets() { return List.generate(100, (index){ return Container( color: Colors.purple, alignment: Alignment(0,0), child: Text("item$index", style: TextStyle(fontSize: 20, color: color.white)),); }); }}Copy the code

Running results:

The gridView.count and gridView.extent constructors can be used to achieve the same effect without setting the delegate

3.2 the GridView. Build

As with ListView, using constructors creates all the child widgets at once, which can cause performance problems, so you can use GridView.build to give the GridView itself the ability to manage the child widgets that need to be created.

Previously, with yz.json data, we now dynamically display a list using JSON data.

Consider whether the StatelessWidget can still be used at this time:

Answer: No, because currently our data is loaded asynchronously, the screen does not show the data at first (no data), and then shows the loaded data again after it is loaded from JSON (with data).

  • There’s a change of state, from no data to a change of data.

  • At this point, we need to use the StatefulWidget to manage the components.

When we talk about network requests – next

Slivers

Consider a layout in which a sliding view includes a HeaderView, a ListView, and a GridView. How can they achieve a uniform sliding effect? It’s hard to do with the previous scroll. There is a Widget in Flutter that can achieve this scrolling effect: CustomScrollView, which can manage multiple scrolling views in a unified manner. In CustomScrollView, each individual, scrollable Widget is called a Sliver. Add: Sliver can be translated as Sliver, and you can treat each individual scroll view as a Sliver.

4.1 Basic use of Slivers

Because you need to put a lot of slivers in a CustomScrollView, the CustomScrollView has a slivers property that puts the corresponding slivers in it:

  • SliverList: similar to the ListView we used before;

  • SliverFixedExtentList: similar to SliverList except that you can set the scrolling height;

  • SliverGrid: similar to the GridView we used before;

  • SliverPadding: Sets the inside margin of the Sliver, since it may be necessary to set the inside margin of the Sliver separately;

  • SliverAppBar: Add an AppBar, usually used as a HeaderView for CustomScrollView;

  • SliverSafeArea: sets the content to be displayed in a secure area (e.g. not to leave the content blocked by the sea)

Code rehearsal: SliverGrid+SliverPadding+SliverSafeArea combination

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Sliver problem "),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return CustomScrollView( slivers: <Widget>[ SliverSafeArea( sliver: SliverPadding( padding: EdgeInsets.all(8), sliver: SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8 ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Container( alignment: Alignment(0, 0), color: Colors.orange, child: Text("item$index"), ); }, childCount: 20 ), ), ) ), ], ); }}Copy the code

Running results:

4.2 Combined use of Slivers

Here we use the official sample program to add SliverAppBar+SliverGrid+SliverFixedExtentList

Walkthrough code:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Sliver "),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatelessWidget { @override Widget build(BuildContext context) { return showCustomScrollView(); } Widget showCustomScrollView() { return new CustomScrollView( slivers: <Widget>[ const SliverAppBar( expandedHeight: FlexibleSpace: FlexibleSpaceBar(Title: Text('ZXY list Demo'), background: Image(Image: NetworkImage( "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg", ), fit: BoxFit.cover, ), ), ), new SliverGrid( gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200.0, mainAxisSpacing: 10.0, crossAxisSpacing: 10.0, childAspectRatio: 4.0,), delegate: new SliverChildBuilderDelegate( (BuildContext context, int index) { return new Container( alignment: Alignment.center, color: Colors.teal[100 * (index % 9)], child: new Text('grid item $index'), ); }, childCount: 10,),), SliverFixedExtentList(itemExtent: 50.0, delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return new Container( alignment: Alignment.center, color: Colors.lightBlue[100 * (index % 9)], child: new Text('list item $index'), ); }, childCount: 20), (,],); }}Copy the code

Running results:

Listening for scroll events

For scrolling views, it is often necessary to listen for some scrolling events and do some corresponding things when listening. For example, when the view scrolls to the bottom, you might want to do more pull-up loading; For example, when scrolling to a certain position, a back to the top button is displayed, click the back to the top button, back to the top; For example, monitor when scrolling starts and ends; Listening for scrolling in a Flutter consists of two parts: the ScrollController and the ScrollNotification.

5.1 ScrollController

In a Flutter, the Widget is not the final element rendered to the screen (the RenderObject is actually rendered), so usually the listener event and related information cannot be obtained directly from the Widget, but must be implemented through the Controller of the corresponding Widget.

The component controller of the ListView and GridView is the ScrollController, which can be used to retrieve the view’s scroll information and update the view’s scroll position by calling its methods.

In addition, it is common to change the state information of some widgets based on the position of the scroll, so ScrollController is usually used in conjunction with the StatefulWidget and controls its initialization, listening, destruction, and other events.

Need: For an example, display a back to top button when scrolling to position 1000:

  • 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.

  • ScrollController indirectly inherits from Listenable and can listen for scroll events based on ScrollController.

Code walkthroughs:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return ZXYHomeBody(); } } class ZXYHomeBody extends StatefulWidget { @override _ZXYHomeBodyState createState() => _ZXYHomeBodyState(); } class _ZXYHomeBodyState extends State<ZXYHomeBody> { bool _isShowTop = false; ScrollController _controller; @override void initState() {// Initialize scrollController_controller = ScrollController(); _controller.addListener(() {var tempShowTop = _controller.offset >= 1000; if (tempShowTop ! = _isShowTop) { setState(() { _isShowTop = tempShowTop; }); }}); super.initState(); } @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text("ListView "),), body: ListView.builder( itemCount: 100, itemExtent: 60, controller: _controller, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("item$index")); } ), floatingActionButton: ! _isShowTop ? null : FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () { _controller.animateTo(0, duration: Duration(milliseconds: 1000), curve: Curves.ease); },),); }}Copy the code

The results

5.2 NotificationListener

If you want to listen for when scrolling starts and when scrolling ends, you can use a NotificationListener.

Basic knowledge of

  • A NotificationListener is a Widget where the template parameter T is the type of notification you want to listen for. If omitted, all types of notifications are listened for. If specified, only notifications of that type are listened for.

  • NotificationListener requires an onNotification callback function to implement the listening logic.

  • This callback can return a Boolean value that indicates whether the event is prevented from bubbling upwards. If true, the bubbling stops and the event stops propagating upwards, or if it does not return or if the return value is false, the bubbling continues

** Requirement: ** list scroll, and show scroll progress in the middle

Code walkthroughs:

import 'package:flutter/material.dart'; main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: ZXYHomePage(), ); } } class ZXYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(" listen to scroll "),), body: ZXYHomeBody(),); } } class ZXYHomeBody extends StatefulWidget { @override _ZXYHomeBodyState createState() => _ZXYHomeBodyState(); } class _ZXYHomeBodyState extends State<ZXYHomeBody> { int _progress = 0; @override Widget build(BuildContext context) { return NotificationListener( onNotification: (ScrollNotification notification) { // 1. If (notification is ScrollStartNotification) {print(" start scrolling....." ); } else if (notification is ScrollUpdateNotification) {/ / the current scroll position and total length of the final currentPixel = notification. Metrics. The pixels. final totalPixel = notification.metrics.maxScrollExtent; double progress = currentPixel / totalPixel; setState(() { _progress = (progress * 100).toInt(); }); Print (" is rolling: ${notification. The metrics. Pixels} - ${notification. The metrics. MaxScrollExtent} "); } else if (notification is ScrollEndNotification) {print(" end scrolling....") ); } return false; }, child: Stack( alignment: Alignment(.9, .9), children: <Widget>[ ListView.builder( itemCount: 100, itemExtent: 60, itemBuilder: (BuildContext context, int index) { return ListTile(title: Text("item$index")); } ), CircleAvatar( radius: 30, child: Text("$_progress%"), backgroundColor: Colors.black54, ) ], ), ); }}Copy the code

Running results:

conclusion

In this article today, I have introduced in detail some of the important features of the Flutter project, namely, the ways to implement scroll and listen for scroll. From the above, you can write list display pages and complex page combinations.

You can write the above Demo by hand. We believe that one or two related blog posts per week will deepen your understanding of the Flutter project.

The next blog post will cover the asynchronous implementation of Flutter and network requests, and finally start the project!! Thank you for your praise and attention to me, common progress, mutual encouragement!!