设计给的效果如下:
拿到设计后,先把整体拆分成几个部分:
Stack
)组件布局的可点击区域。然后就可以开始进行编码了。
通过手势探测器(GestureDetector
)组件,你可以通过检测多种手势并设置对应的处理函数。通过大小框(SizedBox
)组件为“按钮”设置一个活动区域,并将堆栈(Stack
)组件作为子组件置于其中。同时,通过activation
、onTapCallback
两个变量来传递状态和回调函数。
import 'package:flutter/material.dart';
/// 自定义的涂鸦按钮组件。
class DoodleButton extends StatefulWidget {
// TODO: 第3步:实现“底部矩形”,成员变量。
// TODO: 第4步:实现“主要矩形”,成员变量。
/// 按钮的激活状态,默认为`false`。
final bool activation;
/// 发生点击时的回调函数。
final Function onTapCallback;
DoodleButton({
// TODO: 第3步:实现“底部矩形”,默认构造。
// TODO: 第4步:实现“主要矩形”,默认构造。
this.activation: false,
this.onTapCallback,
});
@override
_DoodleButtonState createState() => _DoodleButtonState();
}
/// 与自定义的涂鸦按钮组件关联的状态子类。
class _DoodleButtonState extends State<DoodleButton> {
// TODO: 第4步:实现“主要矩形”,控制按压。
@override
Widget build(BuildContext context) {
// 手势探测器(`GestureDetector`)组件,检测手势的组件。
// 尝试识别与其非空回调相对应的手势。如果此组件具有子项,
// 则它会根据其大小调整行为推迟该子项。
// 如果它没有子组件,它会变得适合父组件。
return GestureDetector(
// 在点击(`onTap`)属性,发生了点击。
onTap: widget.activation ? () {
setState(() {
// TODO: 第4步:实现“主要矩形”,“按下”按钮。
widget.onTapCallback();
// TODO: 第4步:实现“主要矩形”,“松开”按钮。
});
} : null,
child: SizedBox(
height: 48.0,
// 堆栈(`Stack`)组件,用于将其子级相对于其框的边缘定位。
child: Stack(
children: <Widget>[
// TODO: 第3步:实现“底部矩形”,实现容器。
// TODO: 第4步:实现“主要矩形”,实现容器。
]
),
),
);
}
}
通过backgroundColor
、silentBackgroundColor
变量来传递“按钮”可用、不可用时,“底部矩形”的颜色。
// TODO: 第3步:实现“底部矩形”,成员变量。
/// 按钮的底部颜色,默认为`#D95636`。
final Color backgroundColor;
/// 不可用按钮的底部颜色,默认为`#8A8A8A`。
final Color silentBackgroundColor;
// TODO: 第3步:实现“底部矩形”,默认构造。
this.backgroundColor: const Color(0xFFD95636),
this.silentBackgroundColor: const Color(0xFF8A8A8A),
使用对齐(Align
)组件,并通过Alignment.bottomCenter
将其设置为靠近下方对齐。再用边界半径(BorderRadius
)类,将矩形的4个边角设置为圆角。
// TODO: 第3步:实现“底部矩形”,实现容器。
// 对齐(`Align`)组件,用于将其子项与其自身对齐,并根据子级的大小自行调整大小。
Align(
// 对准属性,如何调整子组件。
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: widget.activation ? widget.backgroundColor : widget.silentBackgroundColor,
// 框装饰(`BoxDecoration`)类的边界(`border`)属性,在背景颜色、渐变或图像上方绘制的边框。
// 边界(`Border`)类,框的边框,由四个边组成:顶部、右侧、底部、左侧。
border: Border.all(
// 宽度参数,边框的宽度。
width: 2.0,
// 颜色参数,边框的颜色。
color: const Color(0xFF282828),
),
// 边界半径(`borderRadius`)属性,如果为非空值,则此属性将对此框的角进行舍入。
// 边界半径(`BorderRadius`)类,矩形每个角的一组不可变半径。
// 边界半径.所有(`BorderRadius.all`)构造函数,创建一个边界半径,设置所有的半径。
borderRadius: BorderRadius.all(
// 半径(`Radius`)类,圆形或椭圆形的半径。
// 半径.圆(`Radius.circular`)构造函数,构造一个圆形半径,x和y将具有相同的半径值。
Radius.circular(2.0),
),
),
height: 42.0,
),
),
通过mainColor
、silentMainColor
变量来传递“按钮”可用、不可用时,“底部矩形”的颜色,promptText
变量传递“按钮”的操作提示文本。
// TODO: 第4步:实现“主要矩形”,成员变量。
/// 按钮的操作提示文本。
final String promptText;
/// 按钮的主要颜色,默认为`#FF6B47`。
final Color mainColor;
/// 不可用按钮的主要颜色,默认为`#B2B2B2`。
final Color silentMainColor;
// TODO: 第4步:实现“主要矩形”,默认构造。
this.promptText,
this.mainColor: const Color(0xFFFF6B47),
this.silentMainColor: const Color(0xFFB2B2B2),
通过对准(Alignment
)类来控制“主要矩形”的按压效果:靠近上边时是松开按钮;靠近下边时是按下按钮。
// TODO: 第4步:实现“主要矩形”,控制按压。
// 对准(`Alignment`)类,矩形内的一个点。
// [0.0,0.0]表示矩形的中心,[-1.0到+1.0]的距离是从矩形的一边到矩形的另一边的距离。
// 因此,水平(或垂直)[2.0]单位等于矩形的宽度(或高度)。
/// 主要容器的对齐方式,控制按钮的按压状态。
Alignment mainContainerAlignment = Alignment.topCenter;
// TODO: 第4步:实现“主要矩形”,“按下”按钮。
mainContainerAlignment = Alignment.bottomCenter;
widget.onTapCallback();
// TODO: 第4步:实现“主要矩形”,“松开”按钮。
mainContainerAlignment = Alignment.topCenter;
最后实现“主要矩形”的布局代码。
// TODO: 第4步:实现“主要矩形”,实现容器。
Align(
alignment: mainContainerAlignment,
child: Container(
child: Center(
child: Text(
widget.promptText,
style: TextStyle(
color: const Color(0xFF282828),
fontSize: 18.0,
// 字体重量(`FontWeight`)类,用于绘制文本的字形的粗细。
fontWeight: FontWeight.bold,
),
),
),
decoration: BoxDecoration(
color: widget.activation ? widget.mainColor : widget.silentMainColor,
border: Border.all(
width: 2.0,
color: const Color(0xFF282828),
),
borderRadius: BorderRadius.all(
Radius.circular(2.0),
),
),
height: 42.0,
),
),