Flutter 手把手教你封装实用又风骚的分割线
写过 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),
),
再加上各个位置的分割线:
转载自:https://juejin.cn/post/7145636414825660429