This is the sixth day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

The last time we tried to load list data asynchronously with the ListView, we used the tripartite library Flutter_refresh, which is easy to use. But loading list data is by no means an exception, and this time I’m going to try it with native. Due to a variety of reasons, it takes a long time to finish the small dishes this time, so we should strengthen our self-control. The list this time does not deal with animation effects alone, but only deals with data refresh and load more normally, which needs further study and research.

ListView + NotificationListener

A NotificationListener is similar to an Android slide listener, with event handling added at the top and bottom. The integration is also very simple.

@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: New Text(' news list '), elevation: 0.0, // Shadow height), Body: new NotificationListener(onNotification: dataNotification, child: childWidget(), ), ); }Copy the code

The problem summary

One: How to distinguish the list sliding to the top or bottom?

NotificationListener can judge according to the following states and perform the required processing in the corresponding states:

  1. (notification. The metrics. ExtentAfter = = 0.0) to slide to the bottom;
  2. (notification. The metrics. ExtentBefore = = 0.0) to slide to the top.
Bool dataNotification(ScrollNotification notification) {if (notification is ScrollEndNotification) {// Drop to bottom if (notification. The metrics. ExtentAfter = = 0.0) {print (" = = = = = = fell to the bottom = = = = = = "); loadData(); } / / slide to the top if (notification. The metrics. ExtentBefore = = 0.0) {print (" = = = = = = slide to the top = = = = = = "); lastFileID = '0'; rowNumber = 0; dataItems.clear(); loadData(); } } return true; }Copy the code
Two: listen to the whole process of sliding multiple calls to the interface?

In the test process, every time the slide list will call an interface, because in the process of listening, if you do not do any processing as long as the slide list will be listening, there are two ways to solve the small dish;

  1. Listen to slide to the bottom and then call the interface for business operations, such as the judgment in question 1;
Bool dataNotification(ScrollNotification notification) {if (notification is ScrollEndNotification) {// Drop to bottom if (notification. The metrics. ExtentAfter = = 0.0) {print (" = = = = = = fell to the bottom = = = = = = "); loadData(); } } return true; }Copy the code
  1. Try listening for slides using TrackingScrollController, a class that can be used to synchronize the scroll offsets of two or more lazily created scroll views that share a single TrackingScrollController. It keeps track of the most recently updated scroll position and reports it as its initial scroll offset. And maxScrollExtent and offset are equal when not at the bottom. This type of listener is more flexible, and some operations are not processed at the bottom.
bool dataNotification(ScrollNotification notification) {
  if (notification is ScrollUpdateNotification) {
    if (_scrollController.mostRecentlyUpdatedPosition.maxScrollExtent >
            _scrollController.offset &&
        _scrollController.mostRecentlyUpdatedPosition.maxScrollExtent -
                _scrollController.offset <= 50) {
        loadData();
    }
  }
  return true;
}
Copy the code
Three: abnormal situation handling?

In the past, the Loading waiting page was only displayed when the list data was 0, and the data list was displayed when there was data. However, for other abnormal cases, the abnormal page was deliberately added this time, which is just an addition of business, without any new technical points.

The main source

class LoadMoreState extends State<LoadMorePage> { var lastFileID = '0'; var rowNumber = 0; var cid = '29338'; var newsListBean = null; List<ListBean> dataItems = <ListBean>[]; @override void initState() { super.initState(); loadData(); } Future<Null> loadData() {this.isloading = true; final Completer<Null> completer = new Completer<Null>(); getNewsData(); completer.complete(null); return completer.future; } getNewsData() async { await http .get( 'https://XXX..... &lastFileID=${lastFileID}&rowNumber=${rowNumber}') .then((response) { if (response.statusCode == 200) { var jsonRes = json.decode(response.body); newsListBean = NewsListBean(jsonRes); setState(() { if (newsListBean ! = null && newsListBean.list ! = null && newsListBean.list.length > 0) { for (int i = 0; i < newsListBean.list.length; i++) { dataItems.add(newsListBean.list[i]); } lastFileID = newsListBean.list[newsListBean.list.length - 1].fileID.toString(); rowNumber += newsListBean.list.length; } else {} }); }}); } bool dataNotification(ScrollNotification notification) {if (notification is ScrollEndNotification) {// Drop to bottom if (notification. The metrics. ExtentAfter = = 0.0) {print (" = = = = = = fell to the bottom = = = = = = "); loadData(); } / / slide to the top if (notification. The metrics. ExtentBefore = = 0.0) {print (" = = = = = = slide to the top = = = = = = "); lastFileID = '0'; rowNumber = 0; dataItems.clear(); loadData(); } } return true; } // Handle whether there is data in the list, and display the corresponding page. if (newsListBean ! = null && (newsListBean.success ! = null && ! newsListBean.success)) { childWidget = new Stack( children: <Widget>[ new Padding( padding: New EdgeInsets. FromLTRB (0.0, 0.0, 0.0, 100.0), child: new Center(child: image. asset('images/icon_wrong.jpg', width: New Padding(Padding: new EdgeInsets. FromLTRB (0.0, 100.0, 0.0, 0.0), child: New Center(child: new Text(' sorry, no content ', style: new TextStyle(fontSize: 18.0, color: color.blue),),),],); } else if (dataItems ! = null && dataItems.length ! = 0) {childWidget = new Padding(Padding: EdgeInsets. All (2.0), child: new Listview. builder(controller: controller) _scrollController, physics: const AlwaysScrollableScrollPhysics (), padding: const EdgeInsets. All (6.0), itemCount: dataItems == null ? 0 : dataItems.length, itemBuilder: (context, item) { return buildListData(context, dataItems[item]); })); } else { childWidget = new Center( child: new Card( child: new Stack( children: <Widget>[ new Padding( padding: New EdgeInsets. FromLTRB (0.0, 0.0, 0.0, 35.0), Child: new Center(Child: SpinKitFadingCircle) Colors. BlueAccent, size: 30.0,),),), new Padding(Padding: new EdgeInsets. FromLTRB (0.0, 35.0, 0.0, 0.0), child: The new Center (child: new Text (' being loaded, don't try so hard oh ~ '),),),]))); } return childWidget; }}Copy the code

Xiao CAI has not been in touch with Flutter for a long time, and there are still many unclear and ununderstood aspects. I hope to point out more if there are wrong aspects.

Source: Little Monk A Ce