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}

img

一共有下面几个属性

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 设备不同的方法

pic

而这个也很符合 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 ),

图片