我们已经实现了微信的发现界面的效果,但是此时还有一个问题,cell的点击事件如何处理,以及cell点击时状态的变化,比如背景色等;这些问题在本篇文章中,我们来一一讲解;

cell的点击事件

我们的cell是用Container来构建的,但是Container并不能支持手势操作,在Flutter中,手势的操作通过GestureDetector来实现。我们将ContainerGestureDetector包起来,这样我们就可以定义点击事件了:
image.png

GestureDetector小知识

GestureDetectorFlutter中一个用于手势识别的功能性组件,通过它,我们可以识别各种手势;在其内部封装了Listener用来识别语义化的手势;

我们通过将Container包含在GestureDetector中来实现手势操作,常见的手势操作如下:

  • onTap:点击;
  • onTapDown:按下;
  • onTapCancel:取消;
  • onDoubleTap:双击;
  • onLongPress:长安;

Navigator介绍

它是一个路由管理的组件,提供打开退出路由页的方法。Navigator通过一个来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。
最常用的两个方法为:

  • Future push(Route route)
    • 将给定的路由入栈,也就是打开新的界面,返回值是一个Future对象,用来接收新路由出栈(关闭页面)时的返回数据;
  • bool pop([result])
    • 将栈顶路由出栈result为页面关闭时返回给上一个页面的数据。

除此之外,Navigator还有很多其他方法,如Navigator.replaceNavigator.popUntil等,我们后续用到再讲解;

cell点击跳转界面

cell的点击事件已经完成了,怎么少得了进行push操作呢?接下来我们演示如果使用Navigator进行push操作;通过push方法的定义,我们知道需要给push方法传递一个Route,在我们Flutter中有一个MaterialPageRoute这样一个现成的Route,我们此处使用它进行操作,其定义如下:

  1. MaterialPageRoute({
  2. required this.builder,
  3. RouteSettings? settings,
  4. this.maintainState = true,
  5. bool fullscreenDialog = false,
  6. })
  • builder是一个WidgetBuilder类型的回调函数,作用是构建路由页面的内容,返回值是一个Widget。一般我们实现此回调,用来返回路由的实例;
  • settings包含路由的配置信息,比如名称,是否初始路由(首页);
  • maintainState默认情况,当入栈一个新的路由时,原来的路由仍然会被保存在内存中,如果想要在路由没用的时候释放其所占用的所有资源,则可以设置maintainStatefalse
  • fullscreenDialog表示新的路由页面是否是一个全屏的模态对话框。iOS中,如果fullscreenDialogtrue,新页面将会从屏幕底部滑入(而不是水平方向);

我们比如给MaterialPageRoute的构造方法传递一个builder的参数,通常我们的理解builder是用来渲染的,其类型定义为:

  1. typedef WidgetBuilder = Widget Function(BuildContext context);

简单来说,就是一个带BuildContext context参数的方法,其返回值为Widget,我就是说,我们需要在builder中定义一个返回新页面(需要打来的界面)的方法,方法带参数BuildContext context
image.png

DetailPage是我们要进行push打开的新界面,在新界面中,我们定义了其构造方法,传递一个title,用来显示导航栏标题:

  1. class DetailPage extends StatelessWidget {
  2. final String? title;
  3. DetailPage({this.title});
  4. @override
  5. Widget build(BuildContext context) {
  6. return Scaffold(
  7. appBar: AppBar(
  8. title: Text(title ?? ''),
  9. ),
  10. body: Center(
  11. child: Text(title ?? ''),
  12. ),
  13. );
  14. }
  15. }

效果如下:
image.png

有状态的cell

我们注意到,在cell点击的时候,微信的条目是会变灰然后恢复白色的,此时很明显,微信的cell是有状态的,那么我们首先需要将我们的cellStatelessWidget改为StatefulWidget;

改为有状态StatefulWidget之后,我们需要创建FoundCellState继承自State

  1. class _FoundCellState extends State<FoundCell> {
  2. }

然后,将原来的build方法,转移到_FoundCellState中来,转移之后会发现,原来的属性不能直接访问了,此时我们需要使用widget.title来访问title属性:
image.png

此时的widget指向我们定义的StatefulWidget,也就是FoundCell

此时,我们需要处理手势的三个状态:点击,点下去,取消;

  1. return GestureDetector(
  2. onTap: () {
  3. print('1');
  4. Navigator.of(context).push(
  5. MaterialPageRoute(builder: (BuildContext context) {
  6. return DetailPage(title: widget.title,);
  7. })
  8. );
  9. },
  10. onTapDown: (TapDownDetails details) {
  11. print('2');
  12. },
  13. onTapCancel: () {
  14. print('3');
  15. },
  16. );

我们分别在这三个手势事件中打印数字,结果发现:

  • 进行push操作,输出2 1
  • 点击拖动操作,输出2 3

那么,也就是说,我们需要在onTapDown方法中,将cell背景变灰色,onTaponTapCancel两个方法中将cell背景变白色:
image.png
效果如下:
Flutter(十四)实战-cell的点击处理 - 图6

需要注意的是,如果cell非常复杂,我们把cell整个改为有状态的将会影响性能,在cell比较复杂的情况下,我们只需要将需要改变状态的那部分Widget改为StatefulWidget即可; 在将整个cell都改为有状态时,cell也并非整体渲染;Widget是界面的描述,并非真正的界面;重新构建时,构建的是描述,而非界面本身;真正消耗性能的是其他东西,我们后续再讲;