flutter - 点击事件(一) - 自定义一个方便的点击控件
文章目录
点击事件
android 中,所有 View 都可以直接 setOnClickListener, RN 中也有 TouchableHightlight 这样的控件可以直接套在外面,ios 中也可以有 UIControl 这样的控件可以直接添加点击事件.
那么 flutter 中有吗? 答案自然是有. GestureDetector,InkResponse,InkWell, 包括一些琳琅满目的按钮,比如 FlatButton,MaterialButton,CupertinoButton,IconButton,ImageButton 这些组件都可以达到目的. 那么自定义的目的是什么呢?
自定义的优点
最重要的自然就是可控性强,复用性强. 一次修改终身受用. 来看下面的这段代码
1import 'package:flutter/material.dart';
2
3class MaterialTapWidget extends StatelessWidget {
4 final double radius;
5 final Function onTap;
6 final Widget child;
7 final double elevation;
8 final Color backgroundColor;
9 final Color splashColor;
10 final Function onLongTap;
11
12 const MaterialTapWidget({
13 Key key,
14 this.radius = 0.0,
15 this.onTap,
16 this.onLongTap,
17 @required this.child,
18 this.splashColor,
19 this.elevation = 0.0,
20 this.backgroundColor = Colors.transparent,
21 }) : super(key: key);
22
23 @override
24 Widget build(BuildContext context) {
25 Widget w = ClipRRect(
26 borderRadius: BorderRadius.circular(radius),
27 child: Material(
28 borderRadius: BorderRadius.circular(radius),
29 color: backgroundColor,
30 elevation: 0.0,
31 child: InkWell(
32 child: child,
33 onTap: onTap,
34 onLongPress: onLongTap,
35 ),
36 ),
37 );
38
39 if (this.splashColor != null) {
40 return Theme(
41 data: Theme.of(context).copyWith(splashColor: this.splashColor),
42 child: w,
43 );
44 }
45
46 return w;
47 }
48}
一共有下面几个属性
1 final double radius; //圆角
2 final Function onTap; //点击回调
3 final Widget child; // 内部的控件
4 final double elevation; //阴影"高度"
5 final Color backgroundColor; //背景颜色
6 final Color splashColor; // 点击的水波纹颜色
7 final Function onLongTap; //长按回调
这个在日常开发中可以满足我的需求了,但是有一天我还需要单独设置其他的呢 比如我需要添加双击事件,那么我只需要修改几处地方
1class MaterialTapWidget extends StatelessWidget {
2 final double radius;
3 final Function onTap;
4 final Widget child;
5 final double elevation;
6 final Color backgroundColor;
7 final Color splashColor;
8 final Function onLongTap;
9 final Function onDoubleTap; //添加字段
10
11 const MaterialTapWidget({
12 Key key,
13 this.radius = 0.0,
14 this.onTap,
15 this.onLongTap,
16 @required this.child,
17 this.splashColor,
18 this.elevation = 0.0,
19 this.backgroundColor = Colors.transparent,
20 this.onDoubleTap, //添加构造方法
21 }) : super(key: key);
22
23 @override
24 Widget build(BuildContext context) {
25 Widget w = ClipRRect(
26 borderRadius: BorderRadius.circular(radius),
27 child: Material(
28 borderRadius: BorderRadius.circular(radius),
29 color: backgroundColor,
30 elevation: 0.0,
31 child: InkWell(
32 child: child,
33 onTap: onTap,
34 onDoubleTap: onDoubleTap, //添加控件回调
35 onLongPress: onLongTap,
36 ),
37 ),
38 );
39
40 if (this.splashColor != null) {
41 return Theme(
42 data: Theme.of(context).copyWith(splashColor: this.splashColor),
43 child: w,
44 );
45 }
46
47 return w;
48 }
49}
这样就完成了双击的支持, 同样的,如果有别的需求也可以往这里放
比如我们有了特殊需求,希望如果设备是 ios 设备,则不使用 Material 风格,而使用一个点击背景变色的风格
在整体项目是使用 MaterialApp 的情况下,可以像下面这样写
1import 'package:flutter/material.dart';
2
3class PlatformTapWidget extends StatefulWidget {
4 final double radius;
5 final Function onTap;
6 final Widget child;
7 final double elevation;
8 final Color backgroundColor;
9 final Color splashColor;
10 final Function onLongTap;
11
12 const PlatformTapWidget({
13 Key key,
14 this.radius = 0.0,
15 this.onTap,
16 this.elevation,
17 this.backgroundColor = Colors.white,
18 this.splashColor,
19 this.onLongTap,
20 this.child,
21 }) : super(key: key);
22
23 @override
24 _PlatformTapWidgetState createState() => _PlatformTapWidgetState();
25}
26
27class _PlatformTapWidgetState extends State<PlatformTapWidget> {
28 bool isDown = false;
29
30 @override
31 Widget build(BuildContext context) {
32 Color splashColor = widget.splashColor ?? Colors.grey.withOpacity(0.3);
33
34 if (Theme.of(context).platform == TargetPlatform.iOS) {
35 Widget w;
36
37 w = ClipRRect(
38 borderRadius: BorderRadius.circular(widget.radius),
39 child: GestureDetector(
40 behavior: HitTestBehavior.translucent,
41 onTap: widget.onTap,
42 onTapDown: (d) => setState(() => this.isDown = true),
43 onTapUp: (d) => setState(() => this.isDown = false),
44 onTapCancel: () => setState(() => this.isDown = false),
45 child: AnimatedContainer(
46 duration: Duration(milliseconds: 600),
47 curve: Curves.easeIn,
48 color: isDown ? splashColor : widget.backgroundColor,
49 child: widget.child,
50 ),
51 ),
52 );
53
54 return w;
55 }
56
57 Widget w = ClipRRect(
58 borderRadius: BorderRadius.circular(widget.radius),
59 child: Material(
60 borderRadius: BorderRadius.circular(widget.radius),
61 color: widget.backgroundColor,
62 elevation: 0.0,
63 child: InkWell(
64 child: widget.child,
65 onTap: widget.onTap,
66 onLongPress: widget.onLongTap,
67 ),
68 ),
69 );
70
71 if (widget.splashColor != null) {
72 return Theme(
73 data: Theme.of(context).copyWith(splashColor: widget.splashColor),
74 child: w,
75 );
76 }
77
78 return w;
79 }
80}
这样就可以达到 ios 设备和 android 设备不同的方法
而这个也很符合 flutter 的设计理念, 组合优于继承 ,使用 flutter 自带的组件 通过组合的方式构建出自己的组件
flutter 中可以有很多这样的组合方式
比如我项目中有大量左图片,右文字的按钮,并且按钮的图片大小是固定的,字体大小也固定,并且附带圆角 那么这种情况下可以自己封装一个控件
1import 'package:flutter/material.dart';
2import 'package:platform_widget_demo/widgets/platform_tap_widget.dart';
3
4class IconTextButton extends StatelessWidget {
5 final IconData icon;
6 final String text;
7 final Function onTap;
8
9 const IconTextButton({
10 Key key,
11 this.icon,
12 this.text,
13 this.onTap,
14 }) : super(key: key);
15
16 @override
17 Widget build(BuildContext context) {
18 return PlatformTapWidget(
19 onTap: onTap,
20 child: Row(
21 children: <Widget>[
22 Icon(icon),
23 Text(text),
24 ],
25 ),
26 );
27 }
28}
1 IconTextButton(
2 icon: Icons.scanner,
3 text: "扫描",
4 ),