博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Flutter 实例 - 加载更多的ListView
阅读量:6985 次
发布时间:2019-06-27

本文共 6972 字,大约阅读时间需要 23 分钟。

本教程完成一个有加载更多的ListView,最终效果如下图所示:

开始

首先我们只在列表中展示10个整数。

class MyHomePage extends StatefulWidget {  @override  _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State
{ List
items = List.generate(10, (i) => i); // 产生数据 @override void initState() { super.initState(); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar( title: Text("Infinite ListView"), ), body: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile(title: new Text("Number $index")); }, ), ); }}复制代码

动态添加数据

首先我们模拟一个http请求,假设我们通过传递from和to参数,然后返回他们之间的数。我们将添加延迟时间,这样看起来更像是网络加载。具体代码如下所示:

/// from - 包括, to - 不包括/// 通过这个模拟http请求Future
> fakeRequest(int from, int to) async {// 如果对Future不熟悉,可以参考 https://juejin.im/post/5b2c67a351882574a756f2eb return Future.delayed(Duration(seconds: 2), () { return List.generate(to - from, (i) => i + from); });}复制代码

当用户将列表滚动到最底,我们将会调用上面的方法。为了监听列表是否已经滚动到最底,最简单的方式就是给列表添加一个ScrollController,当列表滚动的时候,就会发出一个请求。但是防止频繁的发送http请求,我们需要添加一个变量isPerformingRequest,表示是否有请求正在进行。只有当isPerformingRequest为false时,才能开始一个新的请求。

class _MyHomePageState extends State
{ List
items = List.generate(10, (i) => i); ScrollController _scrollController = new ScrollController(); bool isPerformingRequest = false; // 是否有请求正在进行 @override void initState() { super.initState(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _getMoreData(); } }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } _getMoreData() async { if (!isPerformingRequest) { // 判断是否有请求正在执行 setState(() => isPerformingRequest = true); List
newEntries = await fakeRequest(items.length, items.length + 10); setState(() { items.addAll(newEntries); isPerformingRequest = false;// 下一个请求可以开始了 }); } } @override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar( title: Text("Infinite ListView"), ), body: ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile(title: new Text("Number $index")); }, controller: _scrollController, ), ); }}复制代码

如果你现在运行代码,你可以看数据可以动态的加载,但是这个距离我们最终想要的效果还有差距。我们需要添加一些提示,告知用户数据正在加载中。

进度提示

相关的组件是CircularProgressIndicator,他被Center、Opacity、Padding所包裹。我们将用Opacity组件控制CircularProgressIndicator的显示,当请求正在进行中时显示。整个组件的代码如下所示:

Widget _buildProgressIndicator() {  return new Padding(    padding: const EdgeInsets.all(8.0),    child: new Center(      child: new Opacity(        opacity: isPerformingRequest ? 1.0 : 0.0,        child: new CircularProgressIndicator(),      ),    ),  );}复制代码

最后一件事是将这个组件添加到我们的ListView上:

@overrideWidget build(BuildContext context) {  return new Scaffold(    appBar: AppBar(      title: Text("Infinite ListView"),    ),    body: ListView.builder(      itemCount: items.length + 1,      itemBuilder: (context, index) {        if (index == items.length) {          return _buildProgressIndicator();        } else {          return ListTile(title: new Text("Number $index"));        }      },      controller: _scrollController,    ),  );}复制代码

最终效果如下:

处理空数据情况

下面是附加内容,处理当请求返回的数据为空时的晴空。我们通过ScrollControllerListView一些动画。

_getMoreData() async {  if (!isPerformingRequest) {    setState(() => isPerformingRequest = true);    List
newEntries = await fakeRequest(items.length, items.length); //returns empty list if (newEntries.isEmpty) { double edge = 50.0; double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels; if (offsetFromBottom < edge) { _scrollController.animateTo( _scrollController.offset - (edge -offsetFromBottom), duration: new Duration(milliseconds: 500), curve: Curves.easeOut); } } setState(() { items.addAll(newEntries); isPerformingRequest = false; }); }}复制代码

下面是完整的代码:

import 'dart:async';import 'package:flutter/material.dart';void main() => runApp(new MyApp());class MyApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new MaterialApp(      theme: new ThemeData(primarySwatch: Colors.blue),      home: new MyHomePage(),    );  }}class MyHomePage extends StatefulWidget {  @override  _MyHomePageState createState() => _MyHomePageState();}class _MyHomePageState extends State
{ List
items = List.generate(10, (i) => i); ScrollController _scrollController = new ScrollController(); bool isPerformingRequest = false; @override void initState() { super.initState(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _getMoreData(); } }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } _getMoreData() async { if (!isPerformingRequest) { setState(() => isPerformingRequest = true); List
newEntries = await fakeRequest( items.length, items.length + 10); //returns empty list if (newEntries.isEmpty) { double edge = 50.0; double offsetFromBottom = _scrollController.position.maxScrollExtent - _scrollController.position.pixels; if (offsetFromBottom < edge) { _scrollController.animateTo( _scrollController.offset - (edge - offsetFromBottom), duration: new Duration(milliseconds: 500), curve: Curves.easeOut); } } setState(() { items.addAll(newEntries); isPerformingRequest = false; }); } } Widget _buildProgressIndicator() { return new Padding( padding: const EdgeInsets.all(8.0), child: new Center( child: new Opacity( opacity: isPerformingRequest ? 1.0 : 0.0, child: new CircularProgressIndicator(), ), ), ); } @override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar( title: Text("Infinite ListView"), ), body: ListView.builder( itemCount: items.length + 1, itemBuilder: (context, index) { if (index == items.length) { return _buildProgressIndicator(); } else { return ListTile(title: new Text("Number $index")); } }, controller: _scrollController, ), ); }}/// from - inclusive, to - exclusiveFuture
> fakeRequest(int from, int to) async { return Future.delayed(Duration(seconds: 2), () { return List.generate(to - from, (i) => i + from); });}复制代码

有任何问题,欢迎大家提问

参考

转载地址:http://jiqpl.baihongyu.com/

你可能感兴趣的文章
本人对于netty框架的一些理解,怎么与网站上的websock建立连接
查看>>
SSM前后端分离及跨域
查看>>
json 和 table控件
查看>>
RabbitMQ基础概念详细介绍
查看>>
【es6】数组排重
查看>>
thinkphp字符截取函数msubstr()
查看>>
(42) Aeroo 模板实战
查看>>
NetSetMan IP地址切换工具
查看>>
20050708:我还是忍忍吧
查看>>
Android系统移植与驱动开发概述
查看>>
鼠标滑过,解决ul下 li下a的背景与父级Li不同宽的问题
查看>>
为何毕业即失业
查看>>
技术准备工作
查看>>
介绍Windows Azure移动服务:用于您连接的客户端应用程序的后端
查看>>
MPAndroidChart——饼图
查看>>
Android 学习笔记四:创建工具栏按钮
查看>>
ES6基础-字符串的新特性
查看>>
PHPUnit-附录 A. 断言 (assert)
查看>>
python网络编程(八)
查看>>
redis(一)
查看>>