likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 2

写过 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 手把手教你封装实用又风骚的分割线