Button 在日常中是必不可少的,和尚尝试过不同类型的 Button,也根据需求自定义过,今天和尚系统的学习一下最基本的 Button;
Flutter 中没有 Button Widget,但提供了很多不同类型的 Child Button Widget;和尚分析源码整体可分为 RawMaterialButton 和 IconButton 两类;
其中 RaisedButton / FlatButton / OutlineButton 继承自 MaterialButton 且 MaterialButton 是对 RawMaterialButton 的封装;而BackButton / CloseButton / PopupMenuButton 继承自 IconButton;最终 RawMaterialButton 和 IconButton 都是由 ConstrainedBox 填充绘制;
IconButton 系列属于图标按钮,使用相对简单;其核心是 InkResponse 水波纹效果;
const IconButton({
Key key,
this.iconSize = 24.0, // 图标大小
this.padding = const EdgeInsets.all(8.0), // 图标周围间距
this.alignment = Alignment.center, // 图标位置
@required this.icon, // 图标资源
this.color, // 图标颜色
this.highlightColor, // 点击高亮颜色
this.splashColor, // 水波纹颜色
this.disabledColor, // 不可点击时高亮颜色
@required this.onPressed,
this.tooltip // 长按提示
})
分析源码,其中 icon 和 onPressed 是必须要设置的,其余属性根据需求而适当调整;
IconButton(icon: Icon(Icons.android), tooltip: 'IconButton tootip1',
onPressed: () => Toast.show('IconButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));
IconButton(icon: Icon(Icons.android), tooltip: 'IconButton tootip2',
color: Colors.cyan,
highlightColor: Colors.deepPurple.withOpacity(0.4),
splashColor: Colors.redAccent,
onPressed: () => Toast.show('IconButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));
IconButton(icon: Icon(Icons.android), disabledColor: Colors.green, onPressed: null);
IconButton(icon: Image.asset('images/ic_launcher.png'), iconSize: 40.0, onPressed: null);
BackButton 作用非常明确,一般用作返回上一个页面;
const BackButton({ Key key, this.color })
分析源码,BackButton 继承自 IconButton,只允许设置图标颜色,图标样式 Android 与 iOS 不同且不可修改;点击时会优先判断 maybePop 是否可以返回上一页;
BackButton();
BackButton(color: Colors.green);
CloseButton 一般用作导航栏关闭按钮与 BackButton 类似;
const CloseButton({ Key key }) : super(key: key);
分析源码,CloseButton 继承自 IconButton,无需设置任何属性;点击时会优先判断 maybePop 是否可以返回上一页;
CloseButton();
RawMaterialButton 是 MaterialButton 的基础,核心是由 Material 和 InkWell 等组成;但不可用当前 Theme 或 ButtonTheme 来计算未指定参数的默认值;
const RawMaterialButton({
Key key,
@required this.onPressed,
this.onHighlightChanged, // 高亮变化的回调
this.textStyle, // 文字属性
this.fillColor, // 填充颜色
this.highlightColor, // 背景高亮颜色
this.splashColor, // 水波纹颜色
this.elevation = 2.0, // 阴影
this.highlightElevation = 8.0, // 高亮时阴影
this.disabledElevation = 0.0, // 不可点击时阴影
this.padding = EdgeInsets.zero, // 内容周围边距
this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0), // 默认按钮尺寸
this.shape = const RoundedRectangleBorder(), // 形状样式
this.animationDuration = kThemeChangeDuration, // 动画效果持续时长
this.clipBehavior = Clip.none, // 抗锯齿剪切效果
MaterialTapTargetSize materialTapTargetSize, // 点击目标的最小尺寸
this.child,
})
分析源码可知,RawMaterialButton 没有设置宽高的属性,可根据 padding 或外层依赖 Container 适当调整位置和大小;默认最小尺寸为 88px * 36px;
和尚定义了一个基本的按钮,并监听其高亮改变时状态,与我们常见的按钮基本一致;
RawMaterialButton(
padding: EdgeInsets.all(20.0),
child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
Padding(child: Icon(Icons.android), padding: EdgeInsets.only(right: 10.0)),
Text('RawMaterialButton', style: TextStyle(color: Colors.brown))
]),
textStyle: TextStyle(color: Colors.pink, fontSize: 18.0),
fillColor: Colors.greenAccent.withOpacity(0.4),
highlightColor: Colors.cyan,
splashColor: Colors.deepPurple.withOpacity(0.4),
onPressed: () => Toast.show('RawMaterialButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM),
onHighlightChanged: (state) => Toast.show('onHighlightChanged -> $state', context, duration: Toast.LENGTH_SHORT, gravity: Toast.CENTER))
FloatingActionButton 是 RawMaterialButton 的封装,主要用于浮动在屏幕内容之上,一般是位于底部左右角或中间;一般一个页面只有一个;
const FloatingActionButton({
Key key,
this.child,
this.tooltip, // 长按提醒
this.foregroundColor, // 按钮上子元素颜色
this.backgroundColor, // 背景色
this.heroTag = const _DefaultHeroTag(), // Hero 动画标签
this.elevation = 6.0, // 阴影
this.highlightElevation = 12.0, // 高亮时阴影
@required this.onPressed,
this.mini = false, // 尺寸大小,分为 mini 和 default
this.shape = const CircleBorder(), // 样式形状
this.clipBehavior = Clip.none, // 抗锯齿剪切效果
this.materialTapTargetSize, // 点击目标的最小尺寸
this.isExtended = false, // 是否采用 .extended 方式
})
floatingActionButton: FloatingActionButton(child: Icon(Icons.android), tooltip: 'FloatingActionButton ToolTip',
onPressed: () => Toast.show('FloatingActionButton', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM));
foregroundColor: Colors.redAccent.withOpacity(0.7),
backgroundColor: Colors.green.withOpacity(0.4),
elevation: 0.0,
highlightElevation: 10.0,
mini: true,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),
clipBehavior: Clip.antiAlias,
floatingActionButtonAnimator: MyAnimation(),
heroTag: "aceTag",
class MyAnimation extends FloatingActionButtonAnimator {
double _x, _y;
@override
Offset getOffset({Offset begin, Offset end, double progress}) {
_x = begin.dx + (end.dx - begin.dx) * progress;
_y = begin.dy + (end.dy - begin.dy) * progress;
return Offset(_x * 0.5, _y * 0.9);
}
@override
Animation<double> getRotationAnimation({Animation<double> parent}) {
return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
}
@override
Animation<double> getScaleAnimation({Animation<double> parent}) {
return Tween<double>(begin: 1.0, end: 1.0).animate(parent);
}
}
floatingActionButton: FloatingActionButton.extended(
onPressed: () => Toast.show('FloatingActionButton.extended', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM),
icon: Icon(Icons.android),
label: Text('Android'));
// 方式一
floatingActionButton: Container(
width: 100.0, height: 100.0,
color: Colors.greenAccent.withOpacity(0.4),
child: RawMaterialButton(
shape: CircleBorder(),
elevation: 0.0,
child: Icon(Icons.android),
onPressed: () {}))
b. 借助 FittedBox 将按钮整体放大到 Container 约束范围内;
// 方式二
floatingActionButton: Container(
width: 100.0, height: 100.0,
child: FittedBox(
child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {})))
c. SizeBox 与 FittedBox 约束方式不同,只是整体范围变大,其内部按钮按 Material 建议样式展示;
// 方式三
floatingActionButton: SizedBox(
width: 100.0, height: 100.0,
child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {}))
d. scale 与 FittedBox 类似,按比例缩放;
// 方式四
floatingActionButton: Transform.scale(
scale: 1.5,
child: FloatingActionButton(child: Icon(Icons.android), onPressed: () {}))
Button 涉及的内容较多,扩展性很强,和尚分两节进行学习尝试;有些理解可能还不到位,有问题请多多指导!