我们已经实现了微信的发现界面的效果,但是此时还有一个问题,cell
的点击事件如何处理,以及cell
点击时状态的变化,比如背景色等;这些问题在本篇文章中,我们来一一讲解;
cell的点击事件
我们的cell
是用Container
来构建的,但是Container
并不能支持手势操作,在Flutter
中,手势的操作通过GestureDetector
来实现。我们将Container
用GestureDetector
包起来,这样我们就可以定义点击事件了:
GestureDetector小知识
GestureDetector
是Flutter
中一个用于手势识别的功能性组件,通过它,我们可以识别各种手势;在其内部封装了Listener
用来识别语义化的手势;
我们通过将Container
包含在GestureDetector
中来实现手势操作,常见的手势操作如下:
onTap
:点击;onTapDown
:按下;onTapCancel
:取消;onDoubleTap
:双击;onLongPress
:长安;
Navigator介绍
它是一个路由管理的组件,提供打开
和退出
路由页的方法。Navigator
通过一个栈
来管理活动路由集合。通常当前屏幕显示的页面就是栈顶的路由。
最常用的两个方法为:
- Future push(Route route)
- 将给定的路由
入栈
,也就是打开新的界面,返回值是一个Future
对象,用来接收新路由出栈
(关闭页面)时的返回数据;
- 将给定的路由
- bool pop([result])
- 将栈顶路由
出栈
,result
为页面关闭时返回给上一个页面的数据。
- 将栈顶路由
除此之外,Navigator
还有很多其他方法,如Navigator.replace
、Navigator.popUntil
等,我们后续用到再讲解;
cell点击跳转界面
cell
的点击事件已经完成了,怎么少得了进行push
操作呢?接下来我们演示如果使用Navigator
进行push
操作;通过push
方法的定义,我们知道需要给push
方法传递一个Route
,在我们Flutter
中有一个MaterialPageRoute
这样一个现成的Route
,我们此处使用它进行操作,其定义如下:
MaterialPageRoute({
required this.builder,
RouteSettings? settings,
this.maintainState = true,
bool fullscreenDialog = false,
})
builder
是一个WidgetBuilder
类型的回调函数,作用是构建路由页面的内容,返回值是一个Widget
。一般我们实现此回调,用来返回路由的实例;settings
包含路由的配置信息,比如名称,是否初始路由(首页);maintainState
默认情况,当入栈一个新的路由时,原来的路由仍然会被保存在内存中,如果想要在路由没用的时候释放其所占用的所有资源,则可以设置maintainState
为false
;fullscreenDialog
表示新的路由页面是否是一个全屏的模态对话框。iOS
中,如果fullscreenDialog
为true
,新页面将会从屏幕底部滑入(而不是水平方向);
我们比如给MaterialPageRoute
的构造方法传递一个builder
的参数,通常我们的理解builder
是用来渲染的,其类型定义为:
typedef WidgetBuilder = Widget Function(BuildContext context);
简单来说,就是一个带BuildContext context
参数的方法,其返回值为Widget
,我就是说,我们需要在builder
中定义一个返回新页面
(需要打来的界面)的方法,方法带参数BuildContext context
:
DetailPage
是我们要进行push
打开的新界面,在新界面中,我们定义了其构造方法,传递一个title
,用来显示导航栏标题:
class DetailPage extends StatelessWidget {
final String? title;
DetailPage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title ?? ''),
),
body: Center(
child: Text(title ?? ''),
),
);
}
}
效果如下:
有状态的cell
我们注意到,在cell
点击的时候,微信的条目是会变灰然后恢复白色的,此时很明显,微信的cell
是有状态的,那么我们首先需要将我们的cell
从StatelessWidget
改为StatefulWidget
;
改为有状态
的StatefulWidget
之后,我们需要创建FoundCellState
继承自State
:
class _FoundCellState extends State<FoundCell> {
}
然后,将原来的build
方法,转移到_FoundCellState
中来,转移之后会发现,原来的属性不能直接访问了,此时我们需要使用widget.title
来访问title
属性:
此时的
widget
指向我们定义的StatefulWidget
,也就是FoundCell
;
此时,我们需要处理手势的三个状态:点击,点下去,取消;
return GestureDetector(
onTap: () {
print('1');
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) {
return DetailPage(title: widget.title,);
})
);
},
onTapDown: (TapDownDetails details) {
print('2');
},
onTapCancel: () {
print('3');
},
);
我们分别在这三个手势事件中打印数字,结果发现:
- 进行
push操作
,输出2 1
; - 点击拖动操作,输出
2 3
;
那么,也就是说,我们需要在onTapDown
方法中,将cell
背景变灰色,onTap
和onTapCancel
两个方法中将cell
背景变白色:
效果如下:
需要注意的是,如果
cell
非常复杂,我们把cell
整个改为有状态
的将会影响性能,在cell
比较复杂的情况下,我们只需要将需要改变状态的那部分Widget
改为StatefulWidget
即可; 在将整个cell
都改为有状态
时,cell
也并非整体渲染;Widget
是界面的描述
,并非真正的界面;重新构建时,构建的是描述,而非界面本身;真正消耗性能的是其他东西,我们后续再讲;