前面学习了 ListView 和 GridView,这一节我们介绍一下其他的滑动组件。这次就不学习具体的使用了,只是总体的介绍一下。
CustomScrollView
一个可以自定义滚动效果的滑动组件,可以更好的控制标题栏和滑动区域。
PageView
一个按页来滑动的组件,类似Android中的 ViewPager,也支持垂直方向上的滑动。
StaggeredGridView
说到滑动,少不了经典的瀑布流。
添加滑动监听
既然是滑动组件,那我们少不了需要监听一下滑动事件,那么如何添加滑动监听器呢? 以向ExploreScreen 添加监听为例:
- 将 ExploreScreen 改为继承 StatefulWidget
- 在 **initState()**方法里创建 ScrollController 对象
- 创建 **scrollListener()**方法监听滑动的位置
- 将一个scroll listener 添加到 ScrollController对象
- 将 ScrollController 对象 添加到 ListView
- 重写 dispose(),注销监听器
重写后的代码如下:
import 'package:flutter/material.dart';
import '../api/mock_fooderlich_service.dart';
import '../components/components.dart';
import '../models/explore_data.dart';
class ExploreScreen extends StatefulWidget {
const ExploreScreen({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _ExploreScreenState();
}
}
class _ExploreScreenState extends State<ExploreScreen> {
//MockFooderlichService 模拟服务端的响应数据
final mockService = MockFooderlichService();
late ScrollController _controller;
void _scrollListener() {
//当滑动到最底部时打印日志
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
print('i am at the bottom!');
}
//当滑动到最顶端时打印日志
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
print('i am at the top!');
}
}
@override
void initState() {
//初始化 scroll controller
_controller = ScrollController();
//添加监听器
_controller.addListener(_scrollListener);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
//使用getExploreData()返回的future作为FutureBuilder的参数
future: mockService.getExploreData(),
//使用snapshot来查看Future的状态
builder: (context, AsyncSnapshot<ExploreData> snapshot) {
//如果Future已经完成,可以获取数据来更新UI
if (snapshot.connectionState == ConnectionState.done) {
return ListView(
controller: _controller,
//指定滑动方向
scrollDirection: Axis.vertical,
//声明子Widget
children: [
//上半部分的菜谱列表
TodayRecipeListView(recipes: snapshot.data?.todayRecipes ?? []),
//添加一个间距
const SizedBox(height: 16),
//下半部分的帖子列表
FriendPostListView(
friendPosts: snapshot.data?.friendPosts ?? []),
],
);
} else {
//如果Future还没完成,则提示用户
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
@override
void dispose() {
_controller.removeListener(_scrollListener);
super.dispose();
}
}
另外,你可以尝试使用 SliverGridDelegateWithMaxCrossAxisExtent 来实现下面的效果:
学完了滑动组件后,下一章我们将会学习可交互的组件,关于滑动组件,我把原书中的重点列举一下:
- ListView 和 GridView 都支持水平和垂直方向上的滑动
- primary 属性让Flutter 知道那个滑动组件来处理滑动事件
- physics 在滑动组件中让你可以改变用户滑动交互,例如配合上面的 primary 使用,自身不再处理滑动事件
- 特别在 ListView 嵌套使用时, 记得将 shrinkWrap 设为 true,这样你就可以为组件中的item设定一个固定高度了,否则会出现错误
- 使用 FutureBuilder 来等待异步任务的完成
- 你可以嵌套使用滑动组件,例如在 ListView 中嵌套 GridView,发挥你的想象力吧
- 使用 ScrollController 和 ScrollNotification 来控制或者监听滑动事件
- 你可以把所有的组件导入到一个文件中,这样能使你导入文件更方便