在《APP 开发从 0 到 1(三)布局与 ListView》我们完成了 ListView,这篇文章将做 ListView 下拉加载和加载更多。

ListView 下拉加载

Flutter 提供了 RefreshIndicator 下拉刷新组件,可以轻松让我们实现 Material Design 风格的下拉刷新效果。

参数详解

 //下拉刷新组件
const RefreshIndicator
({
Key key,
@required this.child,
this.displacement: 40.0, //触发下拉刷新的距离
@required this.onRefresh, //下拉回调方法,方法需要有async和await关键字,没有await,刷新图标立马消失,没有async,刷新图标不会消失
this.color, //进度指示器前景色,默认为系统主题色
this.backgroundColor, //背景色
this.notificationPredicate: defaultScrollNotificationPredicate,
})

效果预览

完整代码

废话不多说,直接上完整代码,你可细品下哦。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; class ListViewPage extends StatefulWidget {
@override
ListViewPageState createState() => new ListViewPageState();
} class ListViewPageState extends State<ListViewPage> {
List list = new List(); //列表要展示的数据 @override
void initState() {
super.initState();
getData();
} @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AndBlog'),
),
body: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(list[index]),
);
},
)),
floatingActionButton: FloatingActionButton(
tooltip: 'Increment',
child: Icon(Icons.account_box),
onPressed: () {
print("FloatingActionButton");
},
elevation: 30,
), // This trailing comma makes auto-formatting nicer for build methods.
);
} Future<Null> _onRefresh() async {
await Future.delayed(Duration(seconds: 3), () {
print('refresh');
setState(() {
list = List.generate(20, (i) => '哈喽,我是下拉刷新的数据 $i');
});
});
} Future getData() async {
await Future.delayed(Duration(seconds: 2), () {
setState(() {
list = List.generate(30, (i) => '哈喽,我是原始数据 $i');
});
});
}
}

ListView 加载更多

Flutter 没有直接提供加载更多组件,但我们可以在 ListView 监听 ScrollController,判断是否滑到底,然后加载下一页。

效果预览

完整代码

import 'package:flutter/material.dart';
import 'package:flutter_andblog/andblog/common/color_common.dart';
import 'package:flutter_andblog/andblog/common/http_common.dart';
import 'package:flutter_andblog/andblog/detail/blog_detail_page.dart';
import 'package:http/http.dart' as http;
import 'blog.dart'; class BlogListPage extends StatefulWidget {
@override
BlogListPageState createState() => new BlogListPageState();
} class BlogListPageState extends State<BlogListPage> {
List<Blog> _blogList = [];
String loadMoreText = "正在加载中...";
TextStyle loadMoreTextStyle =
new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0);
ScrollController scrollController = new ScrollController();
var hasData = true;
var page = 0; @override
void initState() {
super.initState();
//一进页面就请求接口
_getBlogListData(); scrollController.addListener(() {
if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent) {
//已经滑到底了
if (hasData) {
//还有数据,加载下一页
setState(() {
loadMoreText = "正在加载中...";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0);
});
page++;
print("page=" + page.toString());
_getBlogListData();
} else {
setState(() {
loadMoreText = "没有更多数据";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF999999), fontSize: 14.0);
});
}
}
});
} @override
void dispose() {
scrollController.dispose();
super.dispose();
} //网络请求
Future _getBlogListData() async {
//一页加载8条数据,skip为跳过的数据,比如加载第二页(page=1),skip跳过前8条数据,即显示第9-16条数据
var skip = page * 8;
print("blog_list_url=" + HttpCommon.blog_list_url + skip.toString());
var response = await http.get(HttpCommon.blog_list_url + skip.toString(),
headers: HttpCommon.headers());
if (response.statusCode == 200) {
// setState 相当于 runOnUiThread
setState(() {
var data = Blog.decodeData(response.body);
if (data.length < 8) {
//某页数据小于8,表明没有下一页了
hasData = false;
} else {
hasData = true;
}
_blogList.addAll(data);
print("_blogList.length0=" + _blogList.length.toString());
});
}
} @override
Widget build(BuildContext context) {
var content; if (_blogList.length == 0) {
content = new Center(
// 可选参数 child:
child: new CircularProgressIndicator(),
);
} else {
content = _contentList();
} return Scaffold(
backgroundColor: ColorCommon.backgroundColor,
appBar: AppBar(
title: Text('AndBlog'),
),
body: content,
floatingActionButton: FloatingActionButton(
tooltip: 'Increment',
child: Icon(Icons.account_box),
onPressed: () {
print("FloatingActionButton");
},
elevation: 30,
), // This trailing comma makes auto-formatting nicer for build methods.
);
} Widget _contentList() {
print("_blogList.length=" + _blogList.length.toString());
return new RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
itemCount: _blogList.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == _blogList.length) {
return _buildProgressMoreIndicator();
} else {
return _blogItem(index);
}
},
controller: scrollController,
));
} Future<Null> _onRefresh() async {
await Future.delayed(Duration(seconds: 1), () {
print('refresh');
setState(() {
page = 0;
_blogList.clear();
_getBlogListData();
});
});
} Widget _buildProgressMoreIndicator() {
return new Padding(
padding: const EdgeInsets.all(15.0),
child: new Center(
child: new Text(loadMoreText, style: loadMoreTextStyle),
),
);
} Widget _blogItem(int index) {
Blog blog = _blogList[index];
var date = new Padding(
padding: const EdgeInsets.only(
top: 20.0,
left: 10.0,
right: 10.0,
),
child: new Text(
blog.date,
textAlign: TextAlign.center,
style: TextStyle(color: ColorCommon.dateColor, fontSize: 18),
)); var cover = new Padding(
padding: const EdgeInsets.only(
top: 10.0,
left: 10.0,
right: 10.0,
),
child: new ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0)),
child: new Image.network(
'http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg',
))); var title = new Text(
blog.title,
style: TextStyle(color: ColorCommon.titleColor, fontSize: 22),
); var summary = new Padding(
padding: const EdgeInsets.only(
top: 5.0,
),
child: new Text(blog.summary,
textAlign: TextAlign.left,
style: TextStyle(color: ColorCommon.summaryColor, fontSize: 18))); var titleSummary = new Container(
padding: const EdgeInsets.all(10.0),
alignment: Alignment.topLeft,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10.0),
bottomRight: Radius.circular(10.0)),
shape: BoxShape.rectangle,
),
margin: const EdgeInsets.only(left: 10, right: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[title, summary],
),
); var blogItem = new GestureDetector(
//点击事件
onTap: () => navigateToMovieDetailPage(blog.objectId, index), child: new Column(
children: <Widget>[
date,
cover,
titleSummary,
],
),
); return blogItem;
} // 跳转页面
navigateToMovieDetailPage(String blogId, Object imageTag) {
Navigator.of(context)
.push(new MaterialPageRoute(builder: (BuildContext context) {
return new BlogDetailPage(blogId, imageTag: imageTag);
}));
}
}

Flutter 开发从 0 到 1(四)ListView 下拉加载和加载更多的更多相关文章

  1. WP & Win10开发:实现ListView下拉加载的两种方法

    1.通过ListView控件的ContainerContentChanging方法.该方法在列表项被实例化时触发,在列表项最后一个项目实例化的时候触发刷新数据逻辑就可以实现下拉加载了. 代码如下:// ...

  2. Windows Phone 8.1开发:如何让ListView下拉加载更多?

    Windows Phone 8.1开发中使用ListView作为数据呈现载体时,经常需要一个下拉(拇指向上滑动)加载更多的交互操作.如何完成这一操作呢?下面为您阐述. 思路是这样的: 1.在ListV ...

  3. (UWP开发)更为合理的一种ListView下拉刷新(PullToRefresh)实现方法

    最近在做的一个项目需要用到下拉刷新,但是参考了现在网络上比较普遍的方法,觉得都不太好,因为要在外部套上一个SrollViewer,容易出现滚动错误.于是刚开始的时候就把思路定到了ListView内部的 ...

  4. ListView下拉加载一(分页)

    首先创建在主xml里放置一个listview列表,代码如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/r ...

  5. ListView下拉加载二(分页)

    这次在一的基础上做了数据通过HttpClient远程获取显示 并且分页,首先看下效果吧: 以上就是效果图了 下面看下具体代码实现吧 主要代码和上节差不多 主入口代码: package com.tp.s ...

  6. Flutter 开发从 0 到 1(五)源码

    Flutter 开发从 0 到 1 明天开始又要上班了,你的假期任务完成如何啊?由于平时加班太多了,实在挤不出更多时间,从开始想用 Flutter <Flutter 开发从 0 到 1(一)需求 ...

  7. Flutter学习笔记(25)--ListView实现上拉刷新下拉加载

    如需转载,请注明出处:Flutter学习笔记(25)--ListView实现上拉刷新下拉加载 前面我们有写过ListView的使用:Flutter学习笔记(12)--列表组件,当列表的数据非常多时,需 ...

  8. 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

    前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...

  9. MaterialRefreshLayout+ListView 下拉刷新 上拉加载

    效果图是这样的,有入侵式的,非入侵式的,带波浪效果的......就那几个属性,都给出来了,自己去试就行. 下拉刷新 上拉加载 关于下拉刷新-上拉加载的效果,有许许多多的实现方式,百度了一下竟然有几十种 ...

随机推荐

  1. Mybatis 枚举类处理

    目录 类型处理器(TypeHandler) 内置的枚举处理器 EnumTypeHandler源码 自定义枚举类处理 通用枚举处理器 Git 类型处理器(TypeHandler) 无论是 MyBatis ...

  2. HTML5 Drag & Drop

    一定要区分不同事件产生的对象 源元素 属性:draggable = "true" 事件: ondragstart:开始拖拽 ondragend:拖拽结束 目标元素 事件: ondr ...

  3. MySQL索引凭什么能让查询效率提高这么多?

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  4. 4.FFMPEG-AVFrame

    在ffmpeg中,解码前的数据结构体为AVPacket(参考:3.AVPacket使用),而解码后的数据为AVFrame(视频的YUV, RGB, 音频的PCM,数据量更大) 1.AVFrame介绍 ...

  5. Nice to meet you

    Who am i 详情可以参见我的这一篇博文 Why and how 其实之前就想在博客园开创自己的博客了,但是自己之前已经利用自己的GitHub搭建了一个 博客,然后的话自己写的文章即水又不多,说到 ...

  6. 如何入门Pytorch之四:搭建神经网络训练MNIST

    上一节我们学习了Pytorch优化网络的基本方法,本节我们将以MNIST数据集为例,通过搭建一个完整的神经网络,来加深对Pytorch的理解. 一.数据集 MNIST是一个非常经典的数据集,下载链接: ...

  7. Linux:less and Aix:more

    在运维工作中,经常要查询应用日志,有Linux和Aix系统,个人感觉,Linux查询日志用less命令比较方便,Aix查询日志用more命令比较方便,在此总结一下两个命令的使用方法 AIX more命 ...

  8. 现代C++教程:高速上手(四)-容器

    1.线性容器 std::array与std::vector不同的是,array对象的大小是固定的,如果容器大小是固定的,那么可以优先考虑使用std::array容器. 由于std::vector是自动 ...

  9. python-数据清洗与编码解码

    0x01 join str = 'hk$$yicunyiye$$hello world' print(str.split('$$')) #自己实现 result = '' for i in str.s ...

  10. php+html实现用户登录退出

    随着渗透学习,逐渐意识到了学会开发也是非常重要的,仅仅是看懂感觉还是差了一些,所以写一写php的开发,这套程序目前并未有较完整的功能,之后会不断进行完善 登录页面.html <!DOCTYPE ...