写过 Android 的同学都知道, RecyclerView 有个DividerItemDecoration 可以自定义富丽的切割线,那怎么在 Flutter 上也完成一个有用的切割线呢?

需求剖析

  • 厚度和颜色可灵敏定义
  • 左右缩进和边距可灵敏定义
  • 可刺进 Label 之类的标识

依据需求画出概念图,接着剖析布局:

Flutter 手把手教你封装实用又风骚的分割线
因为有marginpadding最外层一个Container比较合适,里面 Label 方位不固定,或许在线的上面,也或许在下面,还或许在中心。所以需求一个 columnrow

完成

依据咱们的剖析,写出伪代码:

    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方位

Flutter 手把手教你封装实用又风骚的分割线

先完成这三种方位:

  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 一直在切割线上方:

Flutter 手把手教你封装实用又风骚的分割线

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 在切割线下面的这种状况:

Flutter 手把手教你封装实用又风骚的分割线

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),
),

Flutter 手把手教你封装实用又风骚的分割线
再加上各个方位的切割线:

Flutter 手把手教你封装实用又风骚的分割线