正常项目中使用ListView一定会涉及到分页加载的问题,此时无法避免地需要用到下拉刷新和上拉加载更多的功能。
本文就当前知识面对这两个知识点做简单的实际demo介绍。
本文“下拉刷新,上拉加载”效果图:
1、上拉加载更多
完整代码:
代码语言:javascript复制import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
class RefreshListViewDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return RefreshListViewDemoState();
}
}
class RefreshListViewDemoState extends State<RefreshListViewDemo> {
static const loadingTag = "##loading##"; //表尾标记
var _words = <String>[loadingTag];
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("ListView下拉刷新,上拉加载更多"),
),
body: Stack(
children: <Widget>[
ListView.separated(
itemBuilder: (context, index) {
if (_words[index] == loadingTag) {
if (_words.length - 1 < 100) {
//获取数据
_retrieveData();
//加载时显示loading
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(strokeWidth: 2.0)),
);
} else {
//已经加载了100条数据,不再获取数据。
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
"没有更多了",
style: TextStyle(color: Colors.grey),
));
}
}
//显示单词列表项
return ListTile(title: Text(_words[index]));
},
separatorBuilder: (context, index) => Divider(height: .0),
itemCount: _words.length),
],
));
}
void _retrieveData() {
Future.delayed(Duration(seconds: 2)).then((e) {
_words.insertAll(
_words.length - 1,
//每次生成20个单词
generateWordPairs().take(20).map((e) => e.asPascalCase).toList());
setState(() {
//重新构建列表
});
});
}
}
void main() {
runApp(new MaterialApp(
title: "ListView下拉刷新,上拉加载更多",
theme: ThemeData(primaryColor: Colors.deepOrangeAccent),
home: RefreshListViewDemo(),
));
}
关键步骤分解:
- 先准备一个存放数据的String数组,设置一个结束标记到数组中。此标记始终在列表数据的末尾,是判断列表滑动是否到达尾部的标记。 static const loadingTag = "##loading##"; //表尾标记 var _words = <String>[loadingTag];
- 准备一个模拟网络请求下载数据的方法。 void _retrieveData() { Future.delayed(Duration(seconds: 2)).then((e) {//模拟2秒网络请求 _words.insertAll( //将数据插入到倒数第二个item的位置,因为最后一个是结束标记。 _words.length - 1, //每次生成20个单词 generateWordPairs().take(20).map((e) => e.asPascalCase).toList()); setState(() { //重新构建列表 }); }); }
- 根据条件展示
上拉加载更多
。 3.1. 当监测到最后一条数据,又满足在100条数据以下,显示loading动画布局,并去网络获取数据,获取到数据之后插入到结束标记之前。超过100条数据,显示没有更多了
。 3.2. 如果不是最后一条数据,就正常展示该项的内容(随机英文单词)。 if (_words[index] == loadingTag) { if (_words.length - 1 < 100) { //获取数据 _retrieveData(); //加载时显示loading return Container(……); } else { //已经加载了100条数据,不再获取数据。 return Container( …… child: Text( "没有更多了", style: TextStyle(color: Colors.grey), )); } } //显示单词列表项 return ListTile(title: Text(_words[index]));
2、下拉刷新(包含上拉加载)
下拉刷新可以有很多种实现,这里只介绍如何使用原生下拉刷新控件。
完整代码:
代码语言:javascript复制import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
class RefreshListViewDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return RefreshListViewDemoState();
}
}
class RefreshListViewDemoState extends State<RefreshListViewDemo> {
static const loadingTag = "##loading##"; //表尾标记
var _words = <String>[loadingTag];
bool showRefreshLoad = false; //控制何时展示下拉刷新loading
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("ListView下拉刷新,上拉加载更多"),
),
body: RefreshIndicator(
onRefresh: _toRefresh,
child: ListView.separated(
itemBuilder: (context, index) {
if (_words[index] == loadingTag) {
if (_words.length - 1 < 100) {
//获取数据
_retrieveData();
//加载时显示loading
return Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(strokeWidth: 2.0)),
);
} else {
//已经加载了100条数据,不再获取数据。
return Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
"没有更多了",
style: TextStyle(color: Colors.grey),
));
}
}
//显示单词列表项
return ListTile(title: Text(_words[index]));
},
separatorBuilder: (context, index) => Divider(height: .0),
itemCount: _words.length,
),
));
}
void _retrieveData() {
Future.delayed(Duration(seconds: 10)).then((e) {
_words.insertAll(
_words.length - 1,
//每次生成20个单词
generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
setState(() {
//重新构建列表
});
});
}
Future _toRefresh() async {
// 延迟3秒后添加新数据, 模拟网络加载
await Future.delayed(Duration(seconds: 2), () {
setState(() {
_words.clear();
_words.addAll(
//每次生成20个单词
generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
_words.add(loadingTag); //最后加上结束标记
});
});
}
}
void main() {
runApp(new MaterialApp(
title: "ListView下拉刷新,上拉加载更多",
theme: ThemeData(primaryColor: Colors.deepOrangeAccent),
home: RefreshListViewDemo(),
));
}
关键步骤分解:
- 使用
RefreshIndicator
,包裹ListView
。 body: RefreshIndicator( onRefresh: _toRefresh, child: ListView.separated( - 在
onRefresh
里传入_toRefresh
方法做网络请求(此处模拟网络)。
此处使用english_words
库进行内容生成。
Future _toRefresh() async {
// 延迟3秒后添加新数据, 模拟网络加载
await Future.delayed(Duration(seconds: 2), () {
setState(() {
_words.clear();
_words.addAll(
//每次生成20个单词
generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
_words.add(loadingTag); //最后加上结束标记
});
});
}
完成之后就实现了整个下拉刷新步骤。
效果图见本文开篇处。
注意:有些朋友在使用generateWordPairs()的时候可能会遇到找不到该方法的问题(我就遇到了)。事实上这是一个自动生成英文单词的第三方库。需要导入english_words库才能使用该方法。
具导入方法体在我Flutter系列文章中的《Flutter问题:import 'package:english_words/english_words.dart'失败》一文中有详细步骤。