flutter当中提供了无限加载的组件,再此基础上又做了一层封装。
这个可复用的无限加载列表包括以下特点:
- 上滑加载
- 数据不满足加载条件,提示到达底部
- 根据具体参数的变化,搜索并重新加载数据(可以轻松跟filter和search结合)
- 不同的列表只需要传入任意定义的RowItem即可
- 根据RESTFUL当中limit,offset分页机制加载
组件封装当中比较复杂一点的就是dart如何进行泛型实例化。
- RowItem父类
import 'package:flutter/widgets.dart';
abstract class ListItemWidget extends StatelessWidget {
const ListItemWidget(
{Key key, this.item}
):super(key:key);
final Map item;
call(Map item)=>this;
}
- 无线加载列表组件
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/material_header.dart';
import 'package:flutter_lim/common/http.dart';
import 'package:flutter_lim/widgets/listItem.dart';
typedef RequestCallback = Stream<dynamic> Function({Map paramObj});
typedef S ItemCreator<S extends ListItemWidget>(dynamic item); // new T()泛型实例化实现
// 无限加载列表
//使用InheritedWidget,每个列表管理自己的搜索参数
class ShareParams extends InheritedWidget {
ShareParams(
{@required this.specialParams, this.data, this.count, Widget child})
: super(child: child);
final Map<String, dynamic> specialParams;
final List<dynamic> data;
final int count;
static ShareParams of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareParams>();
}
@override
bool updateShouldNotify(ShareParams oldWidget) {
Map<String, dynamic> oldParams = oldWidget.specialParams;
bool isUpdate = false;
if (this.specialParams.keys.length != oldParams.length) {
isUpdate = true;
} else {
for (String key in this.specialParams.keys) {
if (oldParams[key] == null ||
oldParams[key] != this.specialParams[key]) {
isUpdate = true;
break;
}
}
}
return isUpdate;
}
}
@immutable
class InfiniteListViewWidget<T extends ListItemWidget> extends StatefulWidget {
final RequestCallback request; //数据请求路径
final List<dynamic> data; //初始化数据
final int count; //列表总个数
final ItemCreator<T> creator; //列表Item泛型实例化
final Map<String, dynamic> specialParam; //特殊请求参数,若无须共享参数,传值
final Function refresh;
InfiniteListViewWidget(this.request, this.data, this.count, this.creator,
{this.specialParam, this.refresh});
@override
_InfiniteListViewWidgetState<T> createState() =>
new _InfiniteListViewWidgetState<T>(
this.request, this.data, this.count, this.creator,
specialParam: this.specialParam, refresh: this.refresh);
}
@override
class _InfiniteListViewWidgetState<T extends ListItemWidget>
extends State<InfiniteListViewWidget> {
static const loadingTag = {"position": "bottom"}; //表尾标记
Http http = new Http();
InfiniteListParamObj infiParams;
RequestCallback request; //数据请求
int count; //列表数据总数量
List<dynamic> data;
ItemCreator<T> creator;
@optionalTypeArgs
Map<String, dynamic> specialParam; //特殊请求参数
Function refresh;
EasyRefreshController _refreshController = new EasyRefreshController();
_InfiniteListViewWidgetState(
this.request, this.data, this.count, this.creator,
{this.specialParam, this.refresh});
@override
void initState() {
super.initState();
}
void didChangeDependencies() {
super.didChangeDependencies();
if (ShareParams.of(context) != null) {
print(
'LISTEN PARAMS didChange=>${ShareParams.of(context).specialParams}');
print('LISTEN PARAMS didChange=>${ShareParams.of(context).data}');
this.specialParam = ShareParams.of(context).specialParams;
this.data = ShareParams.of(context).data;
this.count = ShareParams.of(context).count;
}
if (this.specialParam != null) {
this._setInfiniteParams(this.specialParam);
}
}
@override
Widget build(BuildContext context) {
//EasyRefresh是下拉刷新组件,不需要的话可以直接去掉
return EasyRefresh(
header: MaterialHeader(),
child: ListView.builder(
itemCount: this.data.length,
itemBuilder: (BuildContext context, int index) {
var word = this.data[index];
var nextWord;
if (index == this.data.length - 1) {
nextWord = null;
} else {
nextWord = this.data[index + 1];
}
if (nextWord == null && this.data.length == index + 1) {
// 根据返回的count
final curCount = this.data.length;
if (curCount < this.count) {
//offset + limit >= count 的时候不再下拉
this.infiParams.offset = this.data.length;
//获取数据
_retrieveData(this.resolveListData);
//加载时显示loading
return Column(
children: <Widget>[
creator(word),
Container(
padding: const EdgeInsets.all(16.0),
alignment: Alignment.center,
child: SizedBox(
width: 24.0,
height: 24.0,
child: CircularProgressIndicator(strokeWidth: 2.0)),
)
],
);
} else {
//加载数据总数已经超过count了不再加载
return Column(
children: <Widget>[
creator(word),
Container(
alignment: Alignment.center,
padding: EdgeInsets.all(16.0),
child: Text(
"没有更多了",
style: TextStyle(color: Colors.grey),
))
],
);
}
}
//由于build方法中返回的Widget必须实例化,所以要实现泛型实例化
T item = creator(word);
return item;
},
),
controller: _refreshController,
onRefresh: () async {
this.refresh == null
? _refreshController.finishRefresh(success: true)
: this.refresh();
},
);
}
void _setInfiniteParams(Map<String, dynamic> specialParam,
{int limit, int offset, String ordering}) {
if (this.infiParams == null) {
limit = limit != null ? limit : 10;
offset = offset != null ? offset : 0;
ordering = ordering != null ? ordering : '-create_time';
this.infiParams =
new InfiniteListParamObj(limit, offset, ordering, specialParam);
} else {
this.infiParams.paramsObj = specialParam;
}
}
void _retrieveData(Function resovle) {
Map<String, dynamic> paramObj = this.infiParams.getParams();
request(paramObj: paramObj).listen(resovle);
}
void resolveListData(dynamic data) {
this.count = data['count'];
setListData(data['results']);
}
void setListData(dynamic list) {
this.data.addAll(list);
setState(() {});
}
}
- 列表默认参数,可以根据具体的设计修改
@override
class InfiniteListParamObj {
int limit = 10;
int offset = 0;
String ordering = '';
Map<String, dynamic> paramsObj = {};
InfiniteListParamObj(this.limit, this.offset, this.ordering, this.paramsObj);
Map<String, dynamic> getParams() {
Map<String, dynamic> basic = {
"limit": this.limit,
"offset": this.offset,
"ordering": this.ordering
};
basic.addAll(this.paramsObj);
return basic;
}
void resetBasicParams() {
this.limit = 10;
this.offset = 0;
}
}
- 如何使用
Expanded(
child: ShareParams(
specialParams: this.paramsObj,
data: this.dataList,//数据列表
count: this.count,//数据总count
child: new InfiniteListViewWidget<ProductItem>(//ProductItem具体集成自ListItemWidget的列表项目
getUniqueProdList,//数据加载url
this.dataList,
this.count,
(item) => new ProductItem(item),
specialParam: this.paramsObj,//列表搜索参数
refresh: this.onRefresh,//自定义列表刷新的方法
),
));
网友评论