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;
}
}
一共有下面几个属性
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设备不同的方法
而这个也很符合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: "扫描",
),