目录
1. 进度指示器(LinearProgressIndicator、CircularProgressIndicator)
2. 日期组件
3. 宽高比组件AspectRatio
4. 卡片组件Card
5. 剪裁
6. 标签 Chip、ActionChip、FilterChip、ChoiceChip
7. 表格 DataTable、PaginatedDataTable
8. 分割线Divider
9. ListTile
10. ButtonBarTheme
11. Material
Spacer
Visibility
IndexedStack
CircleAvatar
1. 进度指示器
LinearProgressIndicator、CircularProgressIndicator
精确进度指示(可计算/预估,如:文件下载)、模糊进度指示(如:下拉刷新、数据提交)。
没提供尺寸参数(以父容器尺寸作为绘制边界),可通过ConstrainedBox、SizedBox等尺寸限制类组件来指定尺寸。
内部是通过CustomPainter来实现外观绘制的。
自定义指示器(通过CustomPainter自定义绘制)
- LinearProgressIndicator(线性/条状进度条)
LinearProgressIndicator({
Key key,
double value, // 当前进度,取值范围为[0,1],如果为null则会执行一个循环动画(模糊进度)。
Color backgroundColor, // 背景色
Animation<Color> valueColor, // 进度条颜色,可以指定动画,也可以AlwaysStoppedAnimation指定固定颜色
String semanticsLabel,
String semanticsValue,
})
示例
// 模糊进度条(会执行一个循环动画:蓝色条一直在移动)
LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
// 进度条显示50%
LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
)
示例(一个进度条在3秒内从灰色变成蓝色的动画)
import 'package:flutter/material.dart';
class ProgressRoute extends StatefulWidget {
@override
_ProgressRouteState createState() => _ProgressRouteState();
}
class _ProgressRouteState extends State<ProgressRoute>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
@override
void initState() {
// 动画执行时间3秒
_animationController =
new AnimationController(vsync: this, duration: Duration(seconds: 3));
_animationController.forward();
_animationController.addListener(() => setState(() => {}));
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16),
child: LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: ColorTween(begin: Colors.grey, end: Colors.blue)
.animate(_animationController), // 从灰色变成蓝色
value: _animationController.value,
),
);
],
),
);
}
}
- CircularProgressIndicator(圆形进度条)
CircularProgressIndicator({
Key key,
double value,
Color backgroundColor,
Animation<Color> valueColor,
this.strokeWidth = 4.0, // 圆形进度条的粗细
String semanticsLabel,
String semanticsValue,
})
示例
// 模糊进度条(会执行一个旋转动画)
CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
// 进度条显示50%,会显示一个半圆
CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
),
示例(自定义尺寸)
// 线性进度条高度指定为3
SizedBox(
height: 3,
child: LinearProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .5,
),
),
// 圆形进度条直径指定为100。如果CircularProgressIndicator显示空间的宽高不同,则会显示为椭圆。
SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(Colors.blue),
value: .7,
),
),
- 自定义指示器(通过CustomPainter自定义绘制)
- flutter_spinkit三方库
示例(风车)
import 'dart:math';
import 'package:flutter/material.dart';
class WindmillIndicator extends StatefulWidget {
final double size; // 大小
final double speed; // 转速,默认2秒转一圈。
final bool isClockwise; // 是否顺时针
WindmillIndicator({
Key key,
this.size = 50.0,
this.speed = 0.5,
this.isClockwise = true,
}) : assert(speed > 0),
assert(size > 20),
super(key: key);
@override
_WindmillIndicatorState createState() => _WindmillIndicatorState();
}
class _WindmillIndicatorState extends State<WindmillIndicator>
with SingleTickerProviderStateMixin {
AnimationController controller;
@override
void initState() {
super.initState();
int milliseconds = 1000 ~/ widget.speed;
controller = AnimationController(
duration: Duration(milliseconds: milliseconds), vsync: this);
controller.repeat();
}
@override
Widget build(BuildContext context) {
return AnimatedWindmill(
animation: controller,
size: widget.size,
isClockwise: widget.isClockwise,
);
}
@override
void dispose() {
if (controller.status != AnimationStatus.completed &&
controller.status != AnimationStatus.dismissed) {
controller.stop();
}
controller.dispose();
super.dispose();
}
}
// 组装4个叶片
class AnimatedWindmill extends AnimatedWidget {
final double size;
final bool isClockwise;
AnimatedWindmill({
Key key,
@required Animation<double> animation,
this.isClockwise = true,
this.size = 50.0,
}) : super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
final rotationAngle = isClockwise
? 2 * pi * animation.value
: -2 * pi * animation.value;
return Stack(
alignment: Alignment.topCenter,
children: [
WindmillWing(
size: size,
color: Colors.blue,
angle: 0 + rotationAngle,
),
WindmillWing(
size: size,
color: Colors.yellow,
angle: pi / 2 + rotationAngle,
),
WindmillWing(
size: size,
color: Colors.green,
angle: pi + rotationAngle,
),
WindmillWing(
size: size,
color: Colors.red,
angle: -pi / 2 + rotationAngle,
),
],
);
}
}
// 单个叶片
class WindmillWing extends StatelessWidget {
final double size; // 叶片所占据的正方形区域的边长
final Color color; // 叶片颜色
final double angle; // 叶片旋转角度
const WindmillWing({
Key key,
@required this.size,
@required this.color,
@required this.angle,
});
@override
Widget build(BuildContext context) {
return Container(
transformAlignment: Alignment.bottomCenter,
// 旋转后风车会下移,这里向上补偿size / 2
transform: Matrix4.translationValues(0, -size / 2, 0)..rotateZ(angle),
// 将正方形剪裁成叶片
child: ClipPath(
child: Container(
width: size,
height: size,
alignment: Alignment.center,
color: color,
),
clipper: WindwillClipPath(),
),
);
}
}
class WindwillClipPath extends CustomClipper<Path> {
@override
Path getClip(Size size) {
// 2弧线闭合
var path = Path()
..moveTo(size.width / 3, size.height)
..arcToPoint(
Offset(0, size.height * 2 / 3),
radius: Radius.circular(size.width / 2),
)
..arcToPoint(
Offset(size.width, 0),
radius: Radius.circular(size.width),
)
..lineTo(size.width / 3, size.height);
return path;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return false;
}
}
示例(在圆环内滚动的小球)
// 动画控制设置
controller =
AnimationController(duration: const Duration(seconds: 3), vsync: this);
animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
parent: controller,
curve: Curves.slowMiddle,
))
..addListener(() {
setState(() {});
});
// 绘制和动画控制方法
_drawLoadingCircle(Canvas canvas, Size size) {
var paint = Paint()..style = PaintingStyle.stroke
..color = Colors.blue[400]!
..strokeWidth = 2.0;
var path = Path();
final radius = 40.0;
var center = Offset(size.width / 2, size.height / 2);
path.addOval(Rect.fromCircle(center: center, radius: radius));
canvas.drawPath(path, paint);
var innerPath = Path();
final ballRadius = 4.0;
innerPath.addOval(Rect.fromCircle(center: center, radius: radius - ballRadius));
var metrics = innerPath.computeMetrics();
paint.color = Colors.red;
paint.style = PaintingStyle.fill;
for (var pathMetric in metrics) {
var tangent = pathMetric.getTangentForOffset(pathMetric.length * animationValue);
canvas.drawCircle(tangent!.position, ballRadius, paint);
}
}
示例(两横纵向圆环上滚动的小球)
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
parent: controller,
curve: Curves.easeInOutSine,
))
..addListener(() {
setState(() {});
});
_drawTwinsCircle(Canvas canvas, Size size) {
var paint = Paint()
..style = PaintingStyle.stroke
..color = Colors.blue[400]!
..strokeWidth = 2.0;
final radius = 50.0;
final ballRadius = 6.0;
var center = Offset(size.width / 2, size.height / 2);
var circlePath = Path()
..addOval(Rect.fromCircle(center: center, radius: radius));
paint.style = PaintingStyle.stroke;
paint.color = Colors.blue[400]!;
canvas.drawPath(circlePath, paint);
var circleMetrics = circlePath.computeMetrics();
for (var pathMetric in circleMetrics) {
var tangent = pathMetric
.getTangentForOffset(pathMetric.length * animationValue);
paint.style = PaintingStyle.fill;
paint.color = Colors.blue;
canvas.drawCircle(tangent!.position, ballRadius, paint);
}
paint.style = PaintingStyle.stroke;
paint.color = Colors.green[600]!;
var ovalPath = Path()
..addOval(Rect.fromCenter(center: center, width: 3 * radius, height: 40));
canvas.drawPath(ovalPath, paint);
var ovalMetrics = ovalPath.computeMetrics();
for (var pathMetric in ovalMetrics) {
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * animationValue);
paint.style = PaintingStyle.fill;
canvas.drawCircle(tangent!.position, ballRadius, paint);
}
}
示例(钟摆运动)
1. 绘制顶部的横线,代表悬挂的顶点;
2. 绘制运动的圆弧路径,以便让球沿着圆弧运动;
3. 绘制实心圆代表球,并通过动画控制沿着一条圆弧运动;
4. 用一条顶端固定,末端指向球心的直线代表绳子;
5. 当球运动到弧线的终点后,通过动画反转(reverse)控制球 返回;到起点后再正向(forward) 运动就可以实现来回运动的效果了。
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation(
parent: controller,
curve: Curves.easeInOutQuart,
))
..addListener(() {
setState(() {});
}
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
_drawPendulum(Canvas canvas, Size size) {
var paint = Paint()
..style = PaintingStyle.stroke
..color = Colors.blue[400]!
..strokeWidth = 2.0;
final ceilWidth = 60.0;
final pendulumHeight = 200.0;
var ceilCenter =
Offset(size.width / 2, size.height / 2 - pendulumHeight / 2);
var ceilPath = Path()
..moveTo(ceilCenter.dx - ceilWidth / 2, ceilCenter.dy)
..lineTo(ceilCenter.dx + ceilWidth / 2, ceilCenter.dy);
canvas.drawPath(ceilPath, paint);
var pendulumArcPath = Path()
..addArc(Rect.fromCircle(center: ceilCenter, radius: pendulumHeight),
3 * pi / 4, -pi / 2);
paint.color = Colors.white70;
var metrics = pendulumArcPath.computeMetrics();
for (var pathMetric in metrics) {
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * animationValue);
canvas.drawLine(ceilCenter, tangent!.position, paint);
paint.style = PaintingStyle.fill;
paint.color = Colors.blue;
paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 4.0);
canvas.drawCircle(tangent.position, 16.0, paint);
}
}
示例(一个有趣的Loading组件)
参数
1. 前景色:绘制图形的前景色;
2. 背景色:绘制图形的背景色;
3. 图形尺寸:绘制图形的尺寸;
4. 加载文字:可选,有就显示,没有就不显示。
class LoadingAnimations extends StatefulWidget {
final Color bgColor;
final Color foregroundColor;
String? loadingText;
final double size;
LoadingAnimations(
{required this.foregroundColor,
required this.bgColor,
this.loadingText,
this.size = 100.0,
Key? key})
: super(key: key);
@override
_LoadingAnimationsState createState() => _LoadingAnimationsState();
}
圆形Loading
多个沿着大圆运动的实心圆,半径依次减小,实心圆的间距随着动画时间逐步拉大。
_drawCircleLoadingAnimaion(
Canvas canvas, Size size, Offset center, Paint paint) {
final radius = boxSize / 2;
final ballCount = 6;
final ballRadius = boxSize / 15;
var circlePath = Path()
..addOval(Rect.fromCircle(center: center, radius: radius));
var circleMetrics = circlePath.computeMetrics();
for (var pathMetric in circleMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
}
椭圆运动Loading(渐变效果)
final ballCount = 6;
final ballRadius = boxSize / 15;
var ovalPath = Path()
..addOval(Rect.fromCenter(
center: center, width: boxSize, height: boxSize / 1.5));
paint.shader = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [this.foregroundColor, this.bgColor],
).createShader(Offset.zero & size);
var ovalMetrics = ovalPath.computeMetrics();
for (var pathMetric in ovalMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
贝塞尔曲线Loading
首先是构建贝塞尔曲线Path
var bezierPath = Path()
..moveTo(size.width / 2 - boxSize / 2, center.dy)
..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy - boxSize / 4,
size.width / 2, center.dy)
..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy + boxSize / 4,
size.width / 2 + boxSize / 2, center.dy)
..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy - boxSize / 4,
size.width / 2, center.dy)
..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy + boxSize / 4,
size.width / 2 - boxSize / 2, center.dy);
实心圆
var ovalMetrics = bezierPath.computeMetrics();
for (var pathMetric in ovalMetrics) {
for (var i = 0; i < ballCount; ++i) {
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(
Offset(size.width - tangent.position.dx,
size.height - tangent.position.dy),
ballRadius / (1 + i),
paint);
}
}
/*
改变运动方向
var lengthRatio = animationValue * (1 - i / ballCount);
var tangent =
pathMetric.getTangentForOffset(pathMetric.length * lengthRatio);
var ballPosition = tangent!.position;
canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint);
canvas.drawCircle(Offset(tangent.position.dy, tangent.position.dx),
ballRadius / (1 + i), paint);
*/
使用
class _LoadingDemoState extends State<LoadingDemo> {
var loaded = false;
@override
void initState() {
super.initState();
Future.delayed(Duration(seconds: 5), () {
setState(() {
loaded = true;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Loading 使用'),
),
body: Center(
child: loaded
? Image.asset(
'images/beauty.jpeg',
width: 100.0,
)
: LoadingAnimations(
foregroundColor: Colors.blue,
bgColor: Colors.white,
size: 100.0,
),
),
);
}
2. 日期组件
var now=new DateTime.now(); // 当前时间。2050-01-01 00:00:01.001
var time=now.millisecondsSinceEpoch; // 13位时间戳,ms。日期转时间戳
var date=DateTime.fromMillisecondsSinceEpoch(time); // 时间戳转日期,2050-01-01 00:00:01.001
intl 国际化包
DateFormat.yMMMd().format(DateTime.now())
date_format 三方库(日期格式化)
// 2050年01月01
formatDate(DateTime.now(),[yyyy,'年',mm,'月',dd]);
日历组件、时间组件
日历组件
var _datetime=DateTime.now();
_showDatePicker() async{ // 返回Future<void>
var date=await showDatePicker(
context: context,
initialDate: _datetime, // 当前日期(初始日期)
firstDate: DateTime(1900), // 最早日期
lastDate: DateTime(2100), // 最晚日期。_datetime.add(Duration(days: 30),), // 未来30天可选
// locale: Locale('zh'), // 当前环境不是中文时,强制使用中文。需要首先国家化Material组件。
); // 选择完后才会继续向下执行
if(!date)return;
print(date);
setState(() {
_datetime=date;
});
}
时间组件
var _nowTIme=TimeOfDay(hour: 8,minute: 0);
_showTimePicker() async{
var time=await showTimePicker(
context: context,
initialTime: _nowTIme, // 当前日期(初始日期)
);
if(!time)return;
print(time);
setState(() {
_nowTIme=time;
});
}
// _nowTIme.format(context)
iOS风格的日历选择器需要使用showCupertinoModalPopup方法和CupertinoDatePicker组件来实现:
Future<DateTime> _showDatePicker2() {
var date = DateTime.now();
return showCupertinoModalPopup(
context: context,
builder: (ctx) {
return SizedBox(
height: 200,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.dateAndTime,
minimumDate: date,
maximumDate: date.add(
Duration(days: 30),
),
maximumYear: date.year + 1,
onDateTimeChanged: (DateTime value) {
print(value);
},
),
);
},
);
}
三方组件(flutter_cupertino_date_picker)
需要先依赖包
var _datetime=DateTime.now();
_showDatePicker(){
DatePicker.showDatePicker(
context,
picherTheme: DateTimePickerTheme(
showTitle: true, // 是否显示标题
confirm: Text('确定',style:TextStyle(color: Colors.red)),
cancel: Text('取消',style:TextStyle(color: Colors.blue)),
),
minDateTime: DateTime.parse('1900-01-01') , // 最早日期
maxDateTime: DateTime.parse('2100-01-01') , // 最晚日期
initialDateTime: _datetime, // 当前日期(初始日期)
dateFormat:"yyyy-MM-dd", // 年月日
// dateFormat:"yyyy-MM-dd EEE,H时:m分,", // 年月日周时分秒
// pickerMode: DateTimePickerMode.datetime, // 年月日周时分秒
locale:DateTimePickerLocale.zh_cn,
onCancel:(){
},
onChange:(dateTime,List<int> index){
setDate((){
_datetime=dateTime;
});
},
onConfirm:(dateTime,List<int> index){
setDate((){
_datetime=dateTime;
});
},
);
}
3. AspectRatio 宽高比组件
设置子元素宽高比(宽度尽可能扩展,高度由宽高比决定)。
AspectRatio(
aspectRatio: 2.0/1.0, // 宽高比
child: Container( // 子组件
color: Colors.blue,
),
)
4. Card 卡片组件(Meterial组件库)
内容不能滚动,需要在MeterialApp内使用。
Card({
Key? key,
this.color, // 背景色
this.shadowColor, // 阴影色
this.elevation, // 阴影
this.shape, // 形状
this.borderOnForeground = true,
this.margin, // 外边距
this.clipBehavior,
this.child,
this.semanticContainer = true,
})
示例
Card(
margin: EdgeInsets.all(10.0), // 外边距
child: Column( // 子组件
children: <Widget>[
ListTile(
title: Text("张三"),
subtitle: Text("男"),
)
],
),
// shape: , // 默认圆角阴影
)
5. 剪裁
用于对子组件进行剪裁
1. ClipOval
圆形(正方形时),椭圆(矩形时)
2. ClipRRect
圆角矩形
3. ClipRect
溢出部分剪裁(剪裁子组件到实际占用的矩形大小)
4.ClipPath
按照自定义路径剪裁
继承CustomClipper<Path>自定义剪裁类,重写getClip方法返回自定义Path
ClipOval({Key? key, this.clipper, this.clipBehavior = Clip.antiAlias, Widget? child})
ClipRRect({
Key? key,
this.borderRadius = BorderRadius.zero,
this.clipper,
this.clipBehavior = Clip.antiAlias,
Widget? child,
})
ClipRect({ Key? key, this.clipper, this.clipBehavior = Clip.hardEdge, Widget? child })
示例
import 'package:flutter/material.dart';
class ClipTestRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 头像
Widget avatar = Image.asset("imgs/avatar.png", width: 60.0);
return Center(
child: Column(
children: <Widget>[
avatar, // 不剪裁
ClipOval(child: avatar), // 剪裁为圆形
ClipRRect( // 剪裁为圆角矩形
borderRadius: BorderRadius.circular(5.0),
child: avatar,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.topLeft,
widthFactor: .5,// 宽度设为原来宽度一半,另一半会溢出
child: avatar,
),
Text("你好世界", style: TextStyle(color: Colors.green),)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ClipRect( // 将溢出部分剪裁
child: Align(
alignment: Alignment.topLeft,
widthFactor: .5, // 宽度设为原来宽度一半
child: avatar,
),
),
Text("你好世界",style: TextStyle(color: Colors.green))
],
),
],
),
);
}
}
示例(自定义剪裁区域)
截取图片中部40×30像素的范围
1. 首先,自定义一个继承自CustomClipper的子类:
class MyClipper extends CustomClipper<Rect> {
@override
// getClip()是用于获取剪裁区域的接口,由于图片大小是60×60,计算之后即图片中部40×30像素的范围。
Rect getClip(Size size) => Rect.fromLTWH(10.0, 15.0, 40.0, 30.0);
@override
// shouldReclip() 接口决定是否重新剪裁。
// 如果在应用中,剪裁区域始终不会发生变化时应该返回false,这样就不会触发重新剪裁,避免不必要的性能开销。如果剪裁区域会发生变化(比如在对剪裁区域执行一个动画),那么变化后应该返回true来重新执行剪裁。
bool shouldReclip(CustomClipper<Rect> oldClipper) => false;
}
2. 然后,通过ClipRect来执行剪裁,为了看清图片实际所占用的位置,设置一个红色背景:
DecoratedBox(
decoration: BoxDecoration(
color: Colors.red
),
child: ClipRect(
clipper: MyClipper(), // 使用自定义的clipper
child: avatar
),
)
剪裁成功了,但是图片所占用的空间大小仍然是60×60(红色区域),这是因为剪裁是在layout完成后的绘制阶段进行的,所以不会影响组件的大小,这和Transform原理是相似的。
6. 标签 Chip、ActionChip、FilterChip、ChoiceChip
标签
Chip({
Key? key,
this.avatar, // 在左侧显示
required this.label, // 文本
this.labelStyle, //
this.labelPadding, //
this.deleteIcon, // 右侧删除图标
this.onDeleted, // 点击右侧删除图标后回调
this.deleteIconColor, // 右侧删除图标颜色
this.useDeleteButtonTooltip = true, // 长按右侧删除图标是否提示
this.deleteButtonTooltipMessage, // 长按右侧删除图标的提示文本
this.side,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.backgroundColor, // 背景色
this.padding,
this.visualDensity,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
})
可点击的标签
ActionChip({
Key? key,
this.avatar,
required this.label,
this.labelStyle,
this.labelPadding,
required this.onPressed, // 点击后回调
this.pressElevation,
this.tooltip,
this.side,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.backgroundColor,
this.padding,
this.visualDensity,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
})
选中后左侧出现对勾
FilterChip({
Key? key,
this.avatar,
required this.label, // 文本
this.labelStyle, //
this.labelPadding, //
this.selected = false, // 是否选中
required this.onSelected, // 选中状态改变后回调
this.pressElevation,
this.disabledColor,
this.selectedColor, // 选中背景色
this.tooltip,
this.side,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.backgroundColor,
this.padding,
this.visualDensity,
this.materialTapTargetSize,
this.elevation,
this.shadowColor, // 阴影色
this.selectedShadowColor,// 选中后的阴影色
this.showCheckmark,
this.checkmarkColor,
this.avatarBorder = const CircleBorder(),
})
单选标签
const ChoiceChip({
Key? key,
this.avatar,
required this.label, // 文本
this.labelStyle, //
this.labelPadding, //
this.onSelected, // 选中状态改变后回调
this.pressElevation,
required this.selected, // 是否选中
this.selectedColor, // 选中背景色
this.disabledColor,
this.tooltip,
this.side,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.backgroundColor,
this.padding,
this.visualDensity,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
this.selectedShadowColor,
this.avatarBorder = const CircleBorder(),
})
7. 表格 DataTable、PaginatedDataTable
DataTable({
Key? key,
required this.columns, // 顶部的栏目内容
this.sortColumnIndex, // 排序栏的索引号(右侧出现箭头)
this.sortAscending = true, // 排序方式,true升序
this.onSelectAll,
this.decoration,
this.dataRowColor,
this.dataRowHeight,
this.dataTextStyle,
this.headingRowColor,
this.headingRowHeight,
this.headingTextStyle,
this.horizontalMargin,
this.columnSpacing,
this.showCheckboxColumn = true,
this.showBottomBorder = false,
this.dividerThickness,
required this.rows, // 每一行内容
})
栏目
DataColumn({
required this.label, // 文本
this.tooltip,
this.numeric = false,
this.onSort, // 点击后回调,进行排序,参数(index,isAscending)
})
一行
DataRow({
this.key,
this.selected = false, // 是否选中,左侧会有选择框
this.onSelectChanged, // 选中状态改变后回调
this.color, // 背景色
required this.cells, //
})
DataRow.byIndex({
int? index,
this.selected = false,
this.onSelectChanged,
this.color,
required this.cells,
})
单元格
DataCell(
this.child, {
this.placeholder = false,
this.showEditIcon = false,
this.onTap,
})
分页表格
PaginatedDataTable({
Key? key,
this.header, // 表格标题
this.actions,
required this.columns, // 顶部栏目
this.sortColumnIndex, //
this.sortAscending = true, //
this.onSelectAll,
this.dataRowHeight = kMinInteractiveDimension,
this.headingRowHeight = 56.0,
this.horizontalMargin = 24.0,
this.columnSpacing = 56.0,
this.showCheckboxColumn = true,
this.initialFirstRowIndex = 0,
this.onPageChanged, // 页面改变后回调
this.rowsPerPage = defaultRowsPerPage,
this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.start,
required this.source, // 创建类,继承DataTableSource,实现相关方法。
})
8. 分割线Divider
Divider({
Key? key,
this.height, // 高度
this.thickness,
this.indent, // 缩进
this.endIndent,
this.color, // 背景色
})
9. ListTile
1. leading
头部widget
2. trailing
尾部widget
3. minLeadingWidth
头部最小宽(默认40.0)
4. title
标题
5. subtitle
副标题
6. minVerticalPadding
最小的纵向间距(默认4.0)
7. horizontalTitleGap
标题距离头部、尾部的距离(默认16.0)
8. isThreeLine
9. tileColor
未选中的背景色
10. selectedTileColor
选中时的背景色
11. selected
是否选中(默认false)
12. hoverColor
指针悬停时的背景色
13. focusColor
获取焦点时的背景色
14. autofocus
是否自动获取焦点(默认false)
15. focusNode
焦点
16. mouseCursor
在内部或悬停时的鼠标样式
17. shape
形状
18. visualDensity
紧凑程度
19. dense
20. contentPadding
内部边距
21. onTap
点击回调
22. onLongPress
长按回调
23. enableFeedback
是否提供听觉/触觉反馈
24. enabled
是否可交互(默认true)
示例
ListTile(
leading: const Icon(Icons.add),
title: const Text('Add account'),
),
10. ButtonBarTheme
继承自InheritedWidget
ButtonBarTheme({
Key? key,
required this.data,
required Widget child,
})
const ButtonBar({
Key? key,
this.alignment,
this.mainAxisSize,
this.buttonTextTheme,
this.buttonMinWidth,
this.buttonHeight,
this.buttonPadding,
this.buttonAlignedDropdown,
this.layoutBehavior,
this.overflowDirection,
this.overflowButtonSpacing,
this.children = const <Widget>[],
})
11. Material
Material({
Key? key,
this.type = MaterialType.canvas,
this.elevation = 0.0, // 阴影
this.color, //
this.shadowColor, // 阴影色
this.textStyle, // 文本样式
this.borderRadius, // 圆角
this.shape, // 形状
this.borderOnForeground = true,
this.clipBehavior = Clip.none,
this.animationDuration = kThemeChangeDuration,
this.child,
})
Spacer
Visibility
IndexedStack
CircleAvatar(圆形头像)
CircleAvatar({
Key? key,
this.child,
this.backgroundColor, // 背景色
this.backgroundImage, // 背景图片
this.onBackgroundImageError,
this.foregroundColor,
this.radius,
this.minRadius,
this.maxRadius,
})