先看大致效果,因为转成GIF的原因有些失真
一、前言
首先,我们会用到几个Widget:
- ListView:item列表
- ListTitle:item标题
- CircularProgressIndicator:进度圈
- Divider:分割线
然后会用到一个自动生成单词的库:english_words
二、开始
一、引入单词库
在 pubspec.yaml 文件里依赖
二、构建page
1. 引入包
import 'package:english_words/english_words.dart';
2. 创建page
因为涉及到状态改变,所以继承 StatefulWidget 类
class FunctionPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _FunctionPageState();
}
3. 为page创建state
class _FunctionPageState extends State<FunctionPage>{
@override
Widget build(BuildContext context) {
return null;
}
至此,page就构建完成了。
三、用separated构建ListView
首先思考我们要的效果是什么?跟Android里一样的套路!
- 总共加载80条内个item
- 一次加载15条数据
- 15条加载完成是进度圈
- 80条加载完后显示Foot:没有更多数据了
1. 构建ListView
// 定义一个存放单词的数组
List<String> _list = <String>[''];
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.separated(
// 1、首先,需要传入一个item
itemBuilder: (BuildContext context, int index) { },
// 2、其次,传入分隔符
separatorBuilder: (BuildContext context, int index) { },
// 3、最后,是定义的item总个数
itemCount: null),
);
}
2. 构建分隔符,定义item总数
从最简单的入手,我们先搞定分隔符和itemCount
itemCount我们给数组的长度
itemCount: _list.length
分隔符随便给个高度,颜色值flutter统一封装在Colors类里,使用很方便,而且支持渐变扩展。如果要设置样式直接设置Style即可。
separatorBuilder:
(BuildContext context, int index) {
return Divider(
height: 1,
color: Colors.cyan,
);
}
3. 接下来就是item了
itemBuilder会在每一个条目构建时回调,跟recyclerview的onBindHolder一个道理。
如果要直接使用list,如下:
itemBuilder: (BuildContext context, int index) {
// 返回item数据
return ListTile(title: Text(_list[index]),
);
}
我们有几个目标:
- index等于list最后一个item时显示Loading对话框;反之显示item。
- count小于80时返回item;
- count大于80时返回Foot view;
合并一下,于是我们得到以下逻辑:
itemBuilder: (BuildContext context, int index) {
if (index == _list.length - 1) {
if(_list.length - 1 < 80){
// index等于list最后一个item,且count小于80时,返回Loading对话框
return 对话框;// 伪代码
}else{
// count大于80时,返回Foot view
return '没有更多数据了。。。‘;// 伪代码
}
}
// 返回item数据
return ListTile(title: Text(_list[index]),
);
}
4. 逻辑构建完成,就该show us the code了
itemBuilder: (BuildContext context, int index) {
if (index == _list.length - 1) {
// 当item count小于80个时返回一个
if (_list.length - 1 < 80) {
_loadWords(); // 在显示loading时,加载数据
// 最后一个显示加载对话框
return Container(
padding: EdgeInsets.all(15),// Padding值封装在EdgeInsets类里
alignment: Alignment.center,// 内部居中
child: CircularProgressIndicator(),// 使用圆形进度圈
);
} else {
return Container(
padding: EdgeInsets.all(15),
alignment: Alignment.center,
child: Text('没有更多数据了...',style: TextStyle(color: Colors.green),),
);
}
}
// 返回item数据
return ListTile(title: Text(_list[index]),
);
}
四、构建加载数据的逻辑
直接贴代码吧
/// 生成单词从列表最后一行插入
void _loadWords() {
// Future是个好东西
Future.delayed(Duration(milliseconds: 700)).then((e) {
_list.insertAll(_list.length - 1,
// 一次取出15个单词,转成String再转成list添加到集合
generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
setState(() {});// TODO 不要调用setState刷新状态
});
}
在page初始化时调用加载第一次数据
@override
void initState(){
super.initState();
_loadWords();// 加载数据
}
五、结束了,贴上全部代码吧
有错误的地方欢迎指出,向Flutter进军吧!!!
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
class FunctionPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _FunctionPageState();
}
class _FunctionPageState extends State<FunctionPage>{
List<String> _list = <String>[''];
/// 生成单词从列表最后一行插入
void _loadWords() {
Future.delayed(Duration(milliseconds: 700)).then((e) {
_list.insertAll(_list.length - 1,
generateWordPairs().take(15).map((e) => e.asPascalCase).toList());
setState(() {});// TODO 不要忘记这里更改状态
});
}
@override
void initState(){
super.initState();
_loadWords();// 加载数据
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.separated(
itemBuilder: (BuildContext context, int index) {
if (index == _list.length - 1) {
if (_list.length - 1 < 80) {
_loadWords(); // 在显示loading时,加载数据
// 最后一个显示加载对话框
return Container(
padding: EdgeInsets.all(15),
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
} else {
return Container(
padding: EdgeInsets.all(15),
alignment: Alignment.center,
child: Text('没有更多数据了...',style: TextStyle(color: Colors.green),),
);
}
}
// 返回item数据
return ListTile(
title: Text(_list[index]),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
height: 1,
color: Colors.cyan,
);
},
itemCount: _list.length),
);
}
}