文章目录
1. 自带交互的控件
在 Flutter 中,自带如点击事件的控件有
RaisedButton、IconButton、OutlineButton、Checkbox、SnackBar、Switch
等,如下面给
OutlineButton
添加点击事件:
body:Center(
child: OutlineButton(
child: Text('点击我'),
onPressed: (){
Fluttertoast.showToast(
msg: '你点击了FlatButton',
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIos: 1,
);
}),
),
2. 不自带交互的控件
很多控件不像
RaisedButton、OutlineButton
等已经对
presses(taps)
或手势做出了响应。那么如果要监听这些控件的手势就需要用另一个控件
GestureDetector
,那看看源码
GestureDetector
支持哪些手势:
GestureDetector({
Key key,
this.child,
this.onTapDown,//按下,每次和屏幕交互都会调用
this.onTapUp,//抬起,停止触摸时调用
this.onTap,//点击,短暂触摸屏幕时调用
this.onTapCancel,//取消 触发了onTapDown,但没有完成onTap
this.onDoubleTap,//双击,短时间内触摸屏幕两次
this.onLongPress,//长按,触摸时间超过500ms触发
this.onLongPressUp,//长按松开
this.onVerticalDragDown,//触摸点开始和屏幕交互,同时竖直拖动按下
this.onVerticalDragStart,//触摸点开始在竖直方向拖动开始
this.onVerticalDragUpdate,//触摸点每次位置改变时,竖直拖动更新
this.onVerticalDragEnd,//竖直拖动结束
this.onVerticalDragCancel,//竖直拖动取消
this.onHorizontalDragDown,//触摸点开始跟屏幕交互,并水平拖动
this.onHorizontalDragStart,//水平拖动开始,触摸点开始在水平方向移动
this.onHorizontalDragUpdate,//水平拖动更新,触摸点更新
this.onHorizontalDragEnd,//水平拖动结束触发
this.onHorizontalDragCancel,//水平拖动取消 onHorizontalDragDown没有成功触发
//onPan可以取代onVerticalDrag或者onHorizontalDrag,三者不能并存
this.onPanDown,//触摸点开始跟屏幕交互时触发
this.onPanStart,//触摸点开始移动时触发
this.onPanUpdate,//屏幕上的触摸点位置每次改变时,都会触发这个回调
this.onPanEnd,//pan操作完成时触发
this.onPanCancel,//pan操作取消
//onScale可以取代onVerticalDrag或者onHorizontalDrag,三者不能并存,不能与onPan并存
this.onScaleStart,//触摸点开始跟屏幕交互时触发,同时会建立一个焦点为1.0
this.onScaleUpdate,//跟屏幕交互时触发,同时会标示一个新的焦点
this.onScaleEnd,//触摸点不再跟屏幕交互,标示这个scale手势完成
this.behavior,
this.excludeFromSemantics = false
})
2.1 onTapXXX
child: GestureDetector(
child: Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onTapDown: (d){
print("onTapDown");
},
onTapUp: (d){
print("onTapUp");
},
onTap:(){
print("onTap");
},
onTapCancel: (){
print("onTaoCancel");
},
)
输出结果是:
I/flutter (16304): onTapDown
I/flutter (16304): onTapUp
I/flutter(16304): onTap
先触发onTapDown 然后onTapUp 继续onTap
2.2 onLongXXX
//手势测试
Widget gestureTest = GestureDetector(
child: Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onDoubleTap: (){
print("双击onDoubleTap");
},
onLongPress: (){
print("长按onLongPress");
},
onLongPressUp: (){
print("长按抬起onLongPressUP");
},
);
输出结果:
I/flutter (16304): 长按onLongPress
I/flutter (16304): 长按抬起onLongPressUP
I/flutter (16304): 双击onDoubleTap
2.3 onVerticalXXX
//手势测试
Widget gestureTest = GestureDetector(
child: Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onVerticalDragDown: (_){
print("竖直方向拖动按下onVerticalDragDown:"+_.globalPosition.toString());
},
onVerticalDragStart: (_){
print("竖直方向拖动开始onVerticalDragStart"+_.globalPosition.toString());
},
onVerticalDragUpdate: (_){
print("竖直方向拖动更新onVerticalDragUpdate"+_.globalPosition.toString());
},
onVerticalDragCancel: (){
print("竖直方向拖动取消onVerticalDragCancel");
},
onVerticalDragEnd: (_){
print("竖直方向拖动结束onVerticalDragEnd");
},
);
输出结果:
I/flutter (16304): 竖直方向拖动按下onVerticalDragDown:Offset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动开始onVerticalDragStartOffset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.7, 289.3)I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.7, 289.3)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.3, 290.0)
I/flutter (16304): 竖直方向拖动更新onVerticalDragUpdateOffset(191.3, 291.3)
I/flutter (16304): 竖直方向拖动结束onVerticalDragEnd
2.4 onPanXXX
//手势测试
Widget gestureTest = GestureDetector(
child: Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onPanDown: (_){
print("onPanDown");
},
onPanStart: (_){
print("onPanStart");
},
onPanUpdate: (_){
print("onPanUpdate");
},
onPanCancel: (){
print("onPanCancel");
},
onPanEnd: (_){
print("onPanEnd");
},
);
输出结果:
I/flutter (16304): onPanDown
I/flutter (16304): onPanStart
I/flutter (16304): onPanUpdate
I/flutter (16304): onPanUpdate
I/flutter (16304): onPanEnd
2.5 onScaleXXX
//手势测试
Widget gestureTest = GestureDetector(
child: Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onScaleStart: (_){
print("onScaleStart");
},
onScaleUpdate: (_){
print("onScaleUpdate");
},
onScaleEnd: (_){
print("onScaleEnd");
);
输出结果:
I/flutter (16304): onScaleStart
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleUpdate
I/flutter (16304): onScaleEnd
3. 原始指针事件
除了 GestureDetector 能够监听触摸事件外,Pointer 代表用户与设备屏幕交互的原始数据,也就是也能监听手势:
-
PointerDownEvent
:指针接触到屏幕的特定位置 -
PointerMoveEvent
:指针从屏幕上的一个位置移动到另一个位置 -
PointMoveEvent
:指针停止接触屏幕 -
PointUpEvent
:指针停止接触屏幕 -
PointerCancelEvent
:指针的输入事件不再针对此应用
//Pointer
Widget TestContainer = Listener(
child:Container(
width: 300.0,
height: 300.0,
color:Colors.red,
),
onPointerDown: (event){
print("onPointerDown");
},
onPointerUp: (event){
print("onPointerUp");
},
onPointerMove: (event){
print("onPointerMove");
},
onPointerCancel: (event){
print("onPointerCancel");
},
);
输出结果:
I/flutter (16304): onPointerDown
I/flutter (16304): onPointerMovee
I/flutter (16304): onPointerMove
I/flutter (16304): onPointerMoves
I/flutter (16304): onPointerMove
I/flutter (16304): onPointerUp
4. 路由(页面)跳转
在 Android 原生中,页面跳转是通过
startActvity()
来跳转不同页面,而在Flutter就不一样。Flutter中,跳转页面有两种方式:静态路由方式和动态路由方式。在Flutter管理多个页面有两个核心概念和类:
Route
和
Navigator
。一个
route
是一个屏幕或者页面的抽象,
Navigator
是管理
route
的Widget。
Navigator
可以通过
route
入栈和出栈来实现页面之间的跳转。
4.1 静态路由
-
配置路由
在原页面配置路由跳转,就是在MaterialApp里设置每个route对应的页面,注意:一个app只能有一个材料设计(MaterialApp),不然返回上一个页面会黑屏。代码如下:
//入口页面
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
//静态路由方式 配置初始路由
initialRoute: '/',
routes: {
//默认走这个条件`/`
'/':(context){
return HomeStateful();
},
//新页面路由
'/mainnewroute':(context){
return new newRoute();
}
},
//主题色
theme: ThemeData(
//设置为红色
primarySwatch: Colors.red),
//配置了初始路由,下面就不需要了
//home: HomeStateful(),
);
}
}
-
点击跳转
//如果新页面不在同一个类中,记得把它导入
import 'mainnewroute.dart';
class HomeStateful extends StatefulWidget{
@override
State<StatefulWidget> createState(){
return new HomeWidget();
}
}
class HomeWidget extends State<HomeStateful> {
@override
Widget build(BuildContext context) {
...
//Pointer
Widget TestContainer = Listener(
child:Container(
width: 300.0,
height: 300.0,
color:Colors.red,
child: RaisedButton(
child: Text('点击我'),
onPressed: (){
//页面跳转方法
Navigator.of(context).pushNamed('/mainnewroute');
}),
),
);
return new Scaffold(
appBar: new AppBar(
title: new Text('Flutter Demo'),
),
body:Center(
child: TestContainer,
),
);
}
}
-
配置新页面
新页面,我在 lib 下建立一个新的文件(页面)
mainfourday.dart
,很简单:
import 'package:flutter/material.dart';
class newRoute extends StatelessWidget{
@override
Widget build(BuildContext context){
return HomeWidget();
//注意:不需要MaterialApp
// return MaterialApp(
// theme: ThemeData(
// //设置为hongse
// primarySwatch: Colors.red),
// home: HomeWidget(),
// );
}
}
class HomeWidget extends StatelessWidget{
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('new Route'),
),
body: Center(
child:RaisedButton(
child: Text('返回'),
onPressed: (){
//这是关闭页面
Navigator.pop(context);
}),
// child: Text('这是新的页面'),
),
);
}
}
-
效果图
4.2 动态路由
下面说一下跳转页面的第二种方式,动态路由方式:
child: RaisedButton(
child: Text('点击我'),
onPressed: (){
//Navigator.of(context).pushNamed('/mainnewroute');
//动态路由
Navigator.push(
context,
MaterialPageRoute(builder: (newPage){
return new newRoute();
}),
);
}),
效果和上面是一样的。
4.3 页面传递数据
两种方式都是传递参数的,直接上动态路由传递数据代码:
Navigator.push(
context,
MaterialPageRoute(builder: (newPage){
return new newRoute("这是一份数据到新页面");
}),
);
在新页面改为如下:
import 'package:flutter/material.dart';
class newRoute extends StatelessWidget{
//接收上一个页面传递的数据
String str;
//构造函数
newRoute(this.str);
@override
Widget build(BuildContext context){
return HomeWidget(str);
}
}
class HomeWidget extends StatelessWidget{
String newDate;
HomeWidget(this.newDate);
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('new Route'),
),
body: Center(
child:RaisedButton(
//显示上一个页面所传递的数据
child: Text(newDate),
onPressed: (){
Navigator.pop(context);
}),
// child: Text('这是新的页面'),
),
);
}
}
静态路由方式传递参数,也就是在newRoute()加上所要传递的参数就可以了
//新页面路由
'/mainnewroute':(context){
return new newRoute("sdsd");
}
4.4 页面返回数据
传递数据给新页面可以了,那么怎样将新页面数据返回上一个页面呢?也是很简单,在返回方法pop加上所要返回的数据即可:
body: Center(
child:RaisedButton(
//显示上一个页面所传递的数据
child: Text(newDate),
onPressed: (){
Navigator.pop(context,"这是新页面返回的数据");
}),
// child: Text('这是新的页面'),
),
因为打开页面是异步的,所以页面的结果需要通过一个Future来返回,静态路由方式:
child: RaisedButton(
child: Text('点击我'),
onPressed: () async {
var data = await Navigator.of(context).pushNamed('/mainnewroute');
//打印返回来的数据
print(data);
}),
动态路由方式:
child: RaisedButton(
child: Text('点击我'),
onPressed: () async {
var data = await Navigator.push(
context,
MaterialPageRoute(builder: (newPage){
return new newRoute("这是一份数据到新页面");
}),
);
//打印返回的值
print(data);
}),