上期实现了一个网络轮播图的效果,自定义了一个轮播图组件,继承自StatefulWidget,我们知道Flutter中并没有像Android中activity的概念。页面见的跳转是通过路由从一个全屏组件跳转到另外的一个全屏组件,那如果我想在A组件中更新B组件的数据应该怎么实现呢?

今天我们来实现一个支持筛选的列表页面。前面我们已经实现来一个支持下拉刷新和上拉加载更多的列表组件,这里就不在做更多介绍来,效果图如下:



通过点击左滑菜单筛选列表的数据。由于列表在之前的一篇文章已经说明过Flutter学习四之实现一个支持刷新加载的列表,所以这里就直接上列表的代码:我们将列表定义程一个组件,方便,页面引用,列表的请求也放在组件内部。

class ArticleListBaseWidget extends StatefulWidget {
int cid = 0; //文章分类的id
@override
State<StatefulWidget> createState() {
return ArticleListBaseState();
} ArticleListBaseWidget({this.cid});
} class ArticleListBaseState extends State<ArticleListBaseWidget> {
RefreshController refreshController =
RefreshController(initialRefresh: true);
int pageNum = 0;
List<TopArticleBeanData> _articles = []; void onRefresh() {
pageNum = 0;
getArticle(true, getApiName());
} void loadMore() {
pageNum++;
getArticle(false, getApiName());
} //根据cid参数来处理apiName,如果没有传cid参数进来表示不支持分类筛选。
String getApiName() {
String apiName = "";
if (widget.cid > 0) {
apiName = "article/list/$pageNum/json?cid=${widget.cid}";
} else {
apiName = "article/list/$pageNum/json";
}
print("apiName=$apiName");
return apiName;
} ///置顶文章
void getArticle(bool isRefresh, String apiName) async {
///文章接口请求
dio.get(apiName).then((value) {
///文章实体解析
ArticleListEntityEntity articleBeanEntity =
ArticleListEntityEntity().fromJson(jsonDecode(value.toString()));
if (isRefresh) {
_articles = articleBeanEntity.data.datas;
} else {
_articles.addAll(articleBeanEntity.data.datas);
} ///接口调用成功后更新数据需要调用setState()方法
setState(() { }); if (articleBeanEntity.data.datas.length ==0) {
//如果接口没有数据返回
if (isRefresh) {
//如果是下拉刷新的时候并且接口没有返回数据,隐藏列表控件,展示空页面
refreshController.refreshFailed();
} else {
//如果是上啦加载更多并且没有返回数据,就展示列表控件,但是下拉提示没有更多数据了
refreshController.loadNoData();
}
} else {
//如果有数据返回,展示列表控件
if (isRefresh) {
//如果是下拉刷新,并且有数据返回正常展示页面数据
refreshController.refreshCompleted();
} else {
//如果是上啦加载,并且有数据返回正常展示页面数据
refreshController.loadComplete();
}
}
}).catchError((onError) {
if (isRefresh) {
//如果下拉刷新,并且接口出错,展示错误页面
refreshController.refreshFailed();
} else {
//如果上拉加载更多,并且接口出错,展示列表控件,底部下拉位置展示加载失败
refreshController.loadFailed();
}
});
} @override
Widget build(BuildContext context) {
return
SmartRefresher(
controller: refreshController,
enablePullUp: true,
onRefresh: onRefresh,
onLoading: loadMore,
header: WaterDropHeader(),
footer: ClassicFooter(),
child:
ListView.builder(
itemBuilder: (context, index) => ItemPage(_articles[index]),
itemCount: _articles.length)
);
}
}

因为要实现的列表支持筛选项,所以我们这里要在构造方法中接收一个cid参数,用来进行列表筛选的,代码很简单,主要是定义来一个下拉刷新和上拉加载更多要执行的方法,然后在接口返回中针对返回的数据处理来对应的刷新状态,以及对异常的处理,注释已经写的很清楚来。

接下来我们新建一个页面,

 @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("文章列表")),
endDrawer: drawerSystem(),
body:ArticleListBaseWidget(key:key,cid: cid));
}

这里endDrawer方法是用来添加一个左滑菜单栏的,这里我们用来显示需要筛选的分类数据组件drawerSystem,该组件是由两个联动的列表组成的,同时在构造方法中,传人两个列表的点击时间,方便调用该组件的页面进行数据更新。这里要注意的是获取状态栏的高度和appbar的高度,还有我们要在endDrawer中自定义一个标题栏,但是endDrawer页面默认会有一个距离顶部的空白高度,我们要利用MediaQuery.removePadding方法来去掉顶部留白的部分,然后设置自己定义的标题栏。这里的appbar高度用常量kToolbarHeight来获取,MediaQuery.of(context).padding.top就是状态栏的高度,看到往上很多是直接写死,这样在不同的机型上面展示的效果会不一样的。

具体代码:

 Widget drawerSystem() {
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: Container(
color: Colors.blue,
width: 320,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
child: Text("体系数据"),
height: kToolbarHeight + MediaQuery.of(context).padding.top,
//appbar高度+状态栏高度
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top),
alignment: Alignment.center,
color: Colors.grey[200]),
Expanded(
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Container(
child: ListView.separated(
separatorBuilder: (context, index) {
return Divider(
height: 1, color: Colors.grey[50]);
},
itemBuilder: (context, index) => ItemPageSystem(
screenList[index],
null,
index,
(index) => {onItemClick(index)}),
itemCount: screenList.length),
color: Colors.white)),
Expanded(
child: Container(
child: ListView.separated(
separatorBuilder: (context, index) {
return Divider(
height: 1, color: Colors.white);
},
itemBuilder: (context, index) => ItemPageSystem(
null,
screenChildList[index],
index,
(index) => onItemClickChild(index)),
itemCount: screenChildList.length),
color: Colors.grey[50]))
],
))
],
)));
}

ItemPageSystem是listView每该item的样式,可以自己定义,这里要注意的一下,就是listview item的点击事件,这里为来方便在父组件进行数据的处理,所以是从item组件的构造方法将方法传进去的,

 ItemPageSystem(
this.screenDataBean, this.screenChild, this.index, this.function);

然后就可以在点击事件处理筛选的联动效果了,

 //一级筛选点击事件处理
onItemClick(int index) {
setState(() {
//全局记住点击位置
this.index = index;
//设置二级菜单数据集合
screenChildList = screenList[index].children;
//遍历一级数据设置一级菜单标示,是否选中
updateListSelect(index, screenList);
});
} //二级筛选事件处理
onItemClickChild(int index) {
setState(() {
//全局记住二级菜单点击位置
indexChild = index;
//双层循环遍历清空二级菜单为非选中状态
for (int i = 0; i < screenList.length; i++) {
updateListSelect(-1, screenList[i].children);
}
//设置当前点击数据为选中状态
updateListSelect(index, screenChildList);
cid = screenChildList[index].id;
Navigator.pop(context);//关闭侧边菜单栏
key.currentState.refreshController.requestRefresh();
});
}

到这一步基本已经完成了,我们看下效果发现,点击了筛选后并不能更新列表数据,这是为什么呢,查找了半天不知道问题处在哪里,通过查阅文档发现,如果想在父组件里面更新继承自StatefulWidget的组件的数据,光设置setStat还是不行,因为组件的Key是相同的没有改变,所以要想在父组件更新StatefulWidget组件的数据,需要用到GlobalKey,GlobalKey 能够跨 Widget 访问状态,简单来说就是可以通过GlobalKey来调用子组件的方法或者属性。

具体用法:

//在子组件的构造方法中添加一个Key参数。并且调用super方法返回
ArticleListBaseWidget({Key key,this.cid}):super(key:key); //在父组件中初始化组件,ArticleListBaseState为你要更改状态的子组件
final GlobalKey<ArticleListBaseState> key = GlobalKey<ArticleListBaseState>(); //在父组件中初始化子组件的位置,将GlobalKey对象传回到子组件
ArticleListBaseWidget(key:key,cid: cid)); //父组件中,在你需要更新子组件的位置利用GlobalKey对象调用子组件的方法
key.currentState.refreshController.requestRefresh();

在此运行发现,可以通过筛选条件来更新列表了。

Flutter学习六之实现一个带筛选的列表页面的更多相关文章

  1. Flutter学习四之实现一个支持刷新加载的列表

    上一篇文章用Scaffold widget搭建了一个带底部导航栏的的项目架构,这篇文章就来介绍一下在flutter中怎么实现一个带下拉刷新和上拉加载更多的一个列表,这里用到了pull_to_refre ...

  2. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  3. 「六」创建一个带 weblogic 服务的基础镜像

    Weblogic Weblogic 简单介绍以及其在 Docker 环境下的特殊应用 WebLogic是美国Oracle公司出品的一个application server确切的说是一个基于JAVAEE ...

  4. Flutter学习三之搭建一个简单的项目框架

    上一篇文章介绍了Dart的语法的基本使用,从这篇文章开始,开发一个基于玩Android网站的app.使用的他们开放的api来获取网站数据. 根据网站的结构,我们app最外层框架需要添加一个底部导航栏, ...

  5. 学习ASP.NET Core Blazor编程系列五——列表页面

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  6. Flutter学习之路---------第一个Flutter项目

    Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的开发者和组织使用,并且 ...

  7. 学习ASP.NET MVC(七)——我的第一个ASP.NET MVC 查询页面

    在本篇文章中,我将添加一个新的查询页面(SearchIndex),可以按书籍的种类或名称来进行查询.这个新页面的网址是http://localhost:36878/Book/ SearchIndex. ...

  8. 【Flutter学习】页面布局之基础布局组件

    一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...

  9. 【Flutter学习】基本组件之图片组件Image

    一,概述 Image(图片组件)是显示图像的组件,一个显示图片的widget,支持图像格式:JPEG,PNG,GIF,动画GIF,WebP,动画WebP,BMP和WBMP. Image组件有多种构造函 ...

随机推荐

  1. win7蓝牙连接手机蓝牙

    今天有个需求,需要win7 PC连接手机蓝牙,并发送文件到手机端.在此记录下过程. 准备: win7 电脑主机. CSR 蓝牙dongle. 手机 1. 打开蓝牙服务 方法:打开控制面板,找到“管理工 ...

  2. Mybatis_day2

    二 mybatis配置详解 MyBatis最关键的组成部分是SqlSessionFactory,我们可以从中获取SqlSession, 并执行映射的SQL语句.SqlSessionFactory对象可 ...

  3. Spring Cloud Admin健康检查 邮件、钉钉群通知

    源码地址:https://github.com/muxiaonong/Spring-Cloud/tree/master/cloudadmin Admin 简介 官方文档:What is Spring ...

  4. undertow配置

    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 # 不要设置过大,如果过大,启动项目会报错:打开文件数过多 #server.undertow.io- ...

  5. Better Key Sizes (and Attacks) for LWE-Based Encryption

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文 Abstract 基于“learning with errors”(LWE)问题,分析了理 ...

  6. 牛客网PAT练习场-到底买不买

    题目地址:https://www.nowcoder.com/pat/6/problem/4065 题意:用数组统计好字符,最后进行相减,最后进行统计 /** * *作者:Ycute *时间:2019- ...

  7. 焦大:seo思维进化论(中)

    http://www.wocaoseo.com/thread-52-1-1.html 给你一个网站,你第一步做的是什么?我觉得这个是一个绝好的问题,但是却也是个难题,最常见的答案是莫过于做修改标题/查 ...

  8. Javascript循环和代码规范

    1 - 循环 1.1 for循环 语法结构 for(初始化变量; 条件表达式; 操作表达式 ){ //循环体 } 名称 作用 初始化变量 通常被用于初始化一个计数器,该表达式可以使用 var 关键字声 ...

  9. 跳转语句 break 和 continue

    break跳出循环体,结束本次循环. continue结束本次循环. for(var i=0; i<5; i++){ if(i == 3) break; document.write(" ...

  10. Nodejs模块:path

    当前版本:v 10.16.0 一,获取文件相关信息 1,path.basename(filepath[, ext]) 获取该文件的文件名,如果有扩展名,则一起显示扩展名: 如果不想展示扩展名,只想展示 ...