Dart4Flutter - 01 – 变量、类型和函数markdown
Dart4Flutter - 拾遗01 - flutter-dart环境搭建异步
Dart4Flutter - 不可变性async
Flutter 入门 - Container 属性详解post
Flutter 入门-本地访问-MethodChannel动画
Flutter 实例 - 从本地到Flutter通讯 - Event Channels
本教程完成一个有加载更多的ListView,最终效果以下图所示:
首先咱们只在列表中展现10个整数。
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<int> 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<List<int>> 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<MyHomePage> {
List<int> 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<int> 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上:
@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,
),
);
}
复制代码
最终效果以下:
下面是附加内容,处理当请求返回的数据为空时的晴空。咱们经过ScrollController
给ListView
一些动画。
_getMoreData() async {
if (!isPerformingRequest) {
setState(() => isPerformingRequest = true);
List<int> 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<MyHomePage> {
List<int> 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<int> 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 - exclusive
Future<List<int>> fakeRequest(int from, int to) async {
return Future.delayed(Duration(seconds: 2), () {
return List.generate(to - from, (i) => i + from);
});
}
复制代码
有任何问题,欢迎你们提问