写过 Android 的同学都知道, RecyclerView 有个DividerItemDecoration 可以自定义富丽的切割线,那怎么在 Flutter 上也完成一个有用的切割线呢?
需求剖析
- 厚度和颜色可灵敏定义
- 左右缩进和边距可灵敏定义
- 可刺进 Label 之类的标识
依据需求画出概念图,接着剖析布局:
因为有margin
和padding
最外层一个Container
比较合适,里面 Label 方位不固定,或许在线的上面,也或许在下面,还或许在中心。所以需求一个 column
和row
。
完成
依据咱们的剖析,写出伪代码:
Container(
margin: marginInsets ?? EdgeInsets. zero ,
padding:EdgeInsets.only(left:leftIndent??0,right:rightIndent??0 ) ,
child:lineAndLabelWidget,
);
尽量养成与原型一致的编码习气,上面代码设计非常合适咱们的原型。lineAndLabelWidget
控制着切割线和 Label 的巨细、方位。
class HorizontalDivider extends StatelessWidget {
// 切割线高度
final double thickness;
// 切割线颜色
final Color color;
final double leftIndent;
final double rightIndent;
final EdgeInsets? marginInsets;
final Widget? label;
final Alignment labelAlignment;
final EdgeInsets? labelMarginInsets;
const HorizontalDivider({
this.thickness = 1,
this.color = Colors.black,
this.leftIndent = 0,
this.rightIndent = 0,
this.marginInsets,
this.label,
this.labelAlignment = Alignment.center,
this.labelMarginInsets,
});
@override
Widget build(BuildContext context) {
final Widget lineWidget = Container(
height: thickness < 0 ? 1.0 : thickness,
color: color,
);
Widget? dividerWidgetWrapper;
return Container(
margin: marginInsets ?? EdgeInsets.zero,
padding: EdgeInsets.only(left: leftIndent ?? 0, right: rightIndent ?? 0),
child: dividerWidgetWrapper ?? lineWidget,
);
}
}
class EmptyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox.shrink();
}
}
这段代码总共三个小部件:
-
lineWidget
便是切割线,可设置颜色和高度。 -
labelWidgetWrapper
是依据 Label 和间距设置的小部件,假如没有 Label ,便是一个空的小部件,否则依据间距包裹在Container
里。 -
dividerWidgetWrapper
是 Label 和切割线的组合小部件,将依据方位组合在一起。
Label方位
先完成这三种方位:
Widget? dividerWidgetWrapper;
if (labelAlignment == Alignment.center) {
dividerWidgetWrapper = Row(children: [
Expanded(child: lineWidget),
labelWidgetWrapper,
Expanded(child: lineWidget),
]);
} else if (labelAlignment == Alignment.centerLeft) {
dividerWidgetWrapper = Row(children: [
labelWidgetWrapper,
Expanded(child: lineWidget),
]);
} else if (labelAlignment == Alignment.centerRight) {
dividerWidgetWrapper = Row(children: [
Expanded(child: lineWidget),
labelWidgetWrapper,
]);
}
在看这种布局,Label 一直在切割线上方:
if (labelAlignment == Alignment.topCenter) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
labelWidgetWrapper,
lineWidget,
],
);
} else if (labelAlignment == Alignment.topLeft) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
labelWidgetWrapper,
lineWidget,
],
);
} else if (labelAlignment == Alignment.topRight) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
labelWidgetWrapper,
lineWidget,
],
);
}
最终排版 Label 在切割线下面的这种状况:
if (labelAlignment == Alignment.bottomCenter) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
lineWidget,
labelWidgetWrapper,
],
);
} else if (labelAlignment == Alignment.bottomLeft) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
lineWidget,
labelWidgetWrapper,
],
);
} else if (labelAlignment == Alignment.bottomRight) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
lineWidget,
labelWidgetWrapper,
],
);
}
各种状况剖析完成后,完好代码:
import 'package:flutter/material.dart';
class HorizontalDivider extends StatelessWidget {
// 切割线高度
final double thickness;
// 切割线颜色
final Color color;
final double leftIndent;
final double rightIndent;
final EdgeInsets? marginInsets;
final Widget? label;
final Alignment labelAlignment;
final EdgeInsets? labelMarginInsets;
const HorizontalDivider({
this.thickness = 1,
this.color = Colors.black,
this.leftIndent = 0,
this.rightIndent = 0,
this.marginInsets,
this.label,
this.labelAlignment = Alignment.center,
this.labelMarginInsets,
});
@override
Widget build(BuildContext context) {
final Widget lineWidget = Container(
height: thickness < 0 ? 1.0 : thickness,
color: color,
);
Widget? dividerWidgetWrapper;
if (label != null) {
final Widget labelWidgetWrapper = labelMarginInsets == null
? label!
: Container(margin: labelMarginInsets, child: label);
if (labelAlignment == Alignment.center) {
dividerWidgetWrapper = Row(children: [
Expanded(child: lineWidget),
labelWidgetWrapper,
Expanded(child: lineWidget),
]);
} else if (labelAlignment == Alignment.centerLeft) {
dividerWidgetWrapper = Row(children: [
labelWidgetWrapper,
Expanded(child: lineWidget),
]);
} else if (labelAlignment == Alignment.centerRight) {
dividerWidgetWrapper = Row(children: [
Expanded(child: lineWidget),
labelWidgetWrapper,
]);
} else if (labelAlignment == Alignment.topCenter) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
labelWidgetWrapper,
lineWidget,
],
);
} else if (labelAlignment == Alignment.topLeft) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
labelWidgetWrapper,
lineWidget,
],
);
} else if (labelAlignment == Alignment.topRight) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
labelWidgetWrapper,
lineWidget,
],
);
} else if (labelAlignment == Alignment.bottomCenter) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
lineWidget,
labelWidgetWrapper,
],
);
} else if (labelAlignment == Alignment.bottomLeft) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
lineWidget,
labelWidgetWrapper,
],
);
} else if (labelAlignment == Alignment.bottomRight) {
dividerWidgetWrapper = Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
lineWidget,
labelWidgetWrapper,
],
);
}
}
return Container(
alignment: Alignment.centerLeft,
margin: marginInsets ?? EdgeInsets.zero,
padding: EdgeInsets.only(left: leftIndent, right: rightIndent),
child: dividerWidgetWrapper ?? lineWidget,
);
}
}
class EmptyWidget extends StatelessWidget {
const EmptyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox.shrink();
}
}
作用
一条默许的切割线和一条自定义的切割线:
HorizontalDivider(),
SizedBox(
height: 20,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 48.0),
),
再加上各个方位的切割线: