点击事件

android 中,所有 View 都可以直接 setOnClickListener, RN 中也有 TouchableHightlight 这样的控件可以直接套在外面,ios 中也可以有 UIControl 这样的控件可以直接添加点击事件.

那么 flutter 中有吗? 答案自然是有. GestureDetector,InkResponse,InkWell, 包括一些琳琅满目的按钮,比如 FlatButton,MaterialButton,CupertinoButton,IconButton,ImageButton 这些组件都可以达到目的. 那么自定义的目的是什么呢?

自定义的优点

最重要的自然就是可控性强,复用性强. 一次修改终身受用. 来看下面的这段代码

import 'package:flutter/material.dart';

class MaterialTapWidget extends StatelessWidget {
  final double radius;
  final Function onTap;
  final Widget child;
  final double elevation;
  final Color backgroundColor;
  final Color splashColor;
  final Function onLongTap;

  const MaterialTapWidget({
    Key key,
    this.radius = 0.0,
    this.onTap,
    this.onLongTap,
    @required this.child,
    this.splashColor,
    this.elevation = 0.0,
    this.backgroundColor = Colors.transparent,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Widget w = ClipRRect(
      borderRadius: BorderRadius.circular(radius),
      child: Material(
        borderRadius: BorderRadius.circular(radius),
        color: backgroundColor,
        elevation: 0.0,
        child: InkWell(
          child: child,
          onTap: onTap,
          onLongPress: onLongTap,
        ),
      ),
    );

    if (this.splashColor != null) {
      return Theme(
        data: Theme.of(context).copyWith(splashColor: this.splashColor),
        child: w,
      );
    }

    return w;
  }
}

img

一共有下面几个属性

  final double radius; //圆角
  final Function onTap; //点击回调
  final Widget child; // 内部的控件
  final double elevation; //阴影"高度"
  final Color backgroundColor; //背景颜色
  final Color splashColor; // 点击的水波纹颜色
  final Function onLongTap;  //长按回调

这个在日常开发中可以满足我的需求了,但是有一天我还需要单独设置其他的呢 比如我需要添加双击事件,那么我只需要修改几处地方

class MaterialTapWidget extends StatelessWidget {
  final double radius;
  final Function onTap;
  final Widget child;
  final double elevation;
  final Color backgroundColor;
  final Color splashColor;
  final Function onLongTap;
  final Function onDoubleTap;  //添加字段

  const MaterialTapWidget({
    Key key,
    this.radius = 0.0,
    this.onTap,
    this.onLongTap,
    @required this.child,
    this.splashColor,
    this.elevation = 0.0,
    this.backgroundColor = Colors.transparent,
    this.onDoubleTap, //添加构造方法
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Widget w = ClipRRect(
      borderRadius: BorderRadius.circular(radius),
      child: Material(
        borderRadius: BorderRadius.circular(radius),
        color: backgroundColor,
        elevation: 0.0,
        child: InkWell(
          child: child,
          onTap: onTap,
          onDoubleTap: onDoubleTap, //添加控件回调
          onLongPress: onLongTap,
        ),
      ),
    );

    if (this.splashColor != null) {
      return Theme(
        data: Theme.of(context).copyWith(splashColor: this.splashColor),
        child: w,
      );
    }

    return w;
  }
}

这样就完成了双击的支持, 同样的,如果有别的需求也可以往这里放

比如我们有了特殊需求,希望如果设备是 ios 设备,则不使用 Material 风格,而使用一个点击背景变色的风格

在整体项目是使用 MaterialApp 的情况下,可以像下面这样写

import 'package:flutter/material.dart';

class PlatformTapWidget extends StatefulWidget {
  final double radius;
  final Function onTap;
  final Widget child;
  final double elevation;
  final Color backgroundColor;
  final Color splashColor;
  final Function onLongTap;

  const PlatformTapWidget({
    Key key,
    this.radius = 0.0,
    this.onTap,
    this.elevation,
    this.backgroundColor = Colors.white,
    this.splashColor,
    this.onLongTap,
    this.child,
  }) : super(key: key);

  @override
  _PlatformTapWidgetState createState() => _PlatformTapWidgetState();
}

class _PlatformTapWidgetState extends State<PlatformTapWidget> {
  bool isDown = false;

  @override
  Widget build(BuildContext context) {
    Color splashColor = widget.splashColor ?? Colors.grey.withOpacity(0.3);

    if (Theme.of(context).platform == TargetPlatform.iOS) {
      Widget w;

      w = ClipRRect(
        borderRadius: BorderRadius.circular(widget.radius),
        child: GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: widget.onTap,
          onTapDown: (d) => setState(() => this.isDown = true),
          onTapUp: (d) => setState(() => this.isDown = false),
          onTapCancel: () => setState(() => this.isDown = false),
          child: AnimatedContainer(
            duration: Duration(milliseconds: 600),
            curve: Curves.easeIn,
            color: isDown ? splashColor : widget.backgroundColor,
            child: widget.child,
          ),
        ),
      );

      return w;
    }

    Widget w = ClipRRect(
      borderRadius: BorderRadius.circular(widget.radius),
      child: Material(
        borderRadius: BorderRadius.circular(widget.radius),
        color: widget.backgroundColor,
        elevation: 0.0,
        child: InkWell(
          child: widget.child,
          onTap: widget.onTap,
          onLongPress: widget.onLongTap,
        ),
      ),
    );

    if (widget.splashColor != null) {
      return Theme(
        data: Theme.of(context).copyWith(splashColor: widget.splashColor),
        child: w,
      );
    }

    return w;
  }
}

这样就可以达到 ios 设备和 android 设备不同的方法 pic 而这个也很符合 flutter 的设计理念, 组合优于继承 ,使用 flutter 自带的组件 通过组合的方式构建出自己的组件


flutter 中可以有很多这样的组合方式

比如我项目中有大量左图片,右文字的按钮,并且按钮的图片大小是固定的,字体大小也固定,并且附带圆角 那么这种情况下可以自己封装一个控件

import 'package:flutter/material.dart';
import 'package:platform_widget_demo/widgets/platform_tap_widget.dart';

class IconTextButton extends StatelessWidget {
  final IconData icon;
  final String text;
  final Function onTap;

  const IconTextButton({
    Key key,
    this.icon,
    this.text,
    this.onTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return PlatformTapWidget(
      onTap: onTap,
      child: Row(
        children: <Widget>[
          Icon(icon),
          Text(text),
        ],
      ),
    );
  }
}
 IconTextButton(
   icon: Icons.scanner,
   text: "扫描",
 ),

图片