flutter - 點選事件(一) - 自定義一個方便的點選控制元件
阿新 • • 發佈:2018-11-14
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: "掃描",
),