Flutter封装:对 Flex.spacing 不满意,所以封装了NFlexSeparated

359 阅读2分钟

一、思路来源

Flutter 3.27 会出一个 Flex.spacing 可以添加子项 spacing间距,但是仅仅添加间距不支持 Widget 所以我封装了NFlexSeparated。

Flutter 3.27 的 Flex.spacing:

Flex.spacing.png


NFlexSeparated 效果图

Simulator Screenshot - iPhone 15 - 2024-12-29 at 13.08.15.png

Simulator Screenshot - iPhone 15 - 2024-12-29 at 13.08.28.png

二、示例展示

Widget buildSection3() {
  final children = List.generate(
      4,
      (index) => Container(
            decoration: BoxDecoration(
              color: Colors.green,
              // border: Border.all(color: Colors.blue),
            ),
            child: NText("选项_$index"),
          )).toList();

  Widget separated = Padding(
    padding: const EdgeInsets.symmetric(horizontal: 8.0),
    child: VerticalDivider(
      color: Colors.red,
      width: 1,
      indent: 0,
      endIndent: 0,
    ),
  );
  return Container(
    // color: Colors.green,
    child: Column(
      children: [
        NSectionBox(
          title: "ListView.separated",
          child: Container(
            height: 20,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
            ),
            child: ListView.separated(
              scrollDirection: Axis.horizontal,
              itemBuilder: (_, i) => children[i],
              separatorBuilder: (_, i) => separated,
              itemCount: children.length,
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - separatedBuilder",
          child: Container(
            height: 30,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
            ),
            child: NFlexSeparated(
              direction: Axis.horizontal,
              spacing: 60,
              separatedBuilder: (i) {
                final spacing = (i + 1) * 16.0;
                return Container(
                  width: spacing,
                  color: Colors.yellow,
                  alignment: Alignment.center,
                  child: NText(
                    spacing.toInt().toString(),
                    style: TextStyle(fontSize: 13),
                  ),
                );
                // return separated;
              },
              children: children,
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - spacing: 16",
          child: Container(
            height: 30,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
            ),
            alignment: Alignment.center,
            child: NFlexSeparated(
              direction: Axis.horizontal,
              spacing: 16,
              // separatedBuilder: (i) {
              //   return Container(color: Colors.cyan, width: 12);
              //   // return separated;
              // },
              children: children,
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - vertical - separatedBuilder",
          child: IntrinsicWidth(
            child: NFlexSeparated(
              direction: Axis.vertical,
              spacing: 60,
              separatedBuilder: (i) {
                final spacing = (i + 1) * 16.0;
                return Container(
                  height: spacing,
                  color: Colors.yellow,
                  alignment: Alignment.center,
                  child: NText(
                    spacing.toInt().toString(),
                    style: TextStyle(fontSize: 13),
                  ),
                );
                // return separated;
              },
              children: children,
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - vertical - spacing: 16",
          child: IntrinsicWidth(
            child: NFlexSeparated(
              direction: Axis.vertical,
              separatedBuilder: (i) {
                final spacing = 16.0;
                return Container(
                  height: spacing,
                  color: Colors.yellow,
                  alignment: Alignment.center,
                  child: NText(
                    spacing.toInt().toString(),
                    style: TextStyle(fontSize: 13),
                  ),
                );
                // return separated;
              },
              children: children,
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - horizontal",
          child: Container(
            height: 45,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
            ),
            child: IntrinsicHeight(
              child: buildFlexSeparated(direction: Axis.horizontal),
            ),
          ),
        ),
        NSectionBox(
          title: "NFlexSeparated - vertical",
          child: Container(
            height: 400,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
            ),
            child: IntrinsicWidth(
              child: buildFlexSeparated(direction: Axis.vertical),
            ),
          ),
        ),
      ],
    ),
  );
}

/// 带分隔的 Flex
Widget buildFlexSeparated({
  required Axis direction,
  Alignment? textAlignment = Alignment.center,
  double spacing = 0,
  Widget Function(int index)? separatedBuilder,
}) {
  return NFlexSeparated(
    direction: direction,
    // crossAxisAlignment: CrossAxisAlignment.start,
    spacing: 12,
    separatedBuilder: separatedBuilder ??
        (i) {
          final spacing = 16.0 * (i + 1);
          return Container(
            width: direction == Axis.horizontal ? spacing : null,
            height: direction == Axis.horizontal ? null : spacing,
            color: Colors.yellow,
            alignment: Alignment.center,
            child: NText(
              spacing.toInt().toString(),
              style: TextStyle(fontSize: 13),
            ),
          );
        },
    children: [
      Flexible(
        flex: 1,
        child: Container(
          color: Colors.red,
          alignment: textAlignment,
          child: Text(
            "flex: 1",
            style: TextStyle(fontSize: 13),
          ),
        ),
      ),
      Flexible(
        flex: 2,
        child: Container(
          color: Colors.green,
          alignment: textAlignment,
          child: Text(
            "flex: 2",
            style: TextStyle(fontSize: 13),
          ),
        ),
      ),
      Flexible(
        flex: 3,
        child: Container(
          color: Colors.blue,
          alignment: textAlignment,
          child: Text(
            "flex: 3",
            style: TextStyle(fontSize: 18),
          ),
        ),
      ),
      Flexible(
        flex: 1,
        child: Container(
          color: Colors.purple,
          alignment: textAlignment,
          child: Text(
            "flex: 1",
            style: TextStyle(fontSize: 13),
          ),
        ),
      ),
    ],
  );
}

三、源码 NFlexSeparated

//
//  FlexSpacing.dart
//  flutter_templet_project
//
//  Created by shang on 2024/9/5 16:19.
//  Copyright © 2024/9/5 shang. All rights reserved.
//

import 'package:flutter/material.dart';
import 'package:flutter_templet_project/extension/ddlog.dart';

/// 带分隔的 Flex 组件
class NFlexSeparated extends Flex {
  NFlexSeparated({
    super.key,
    required super.direction,
    super.mainAxisAlignment,
    super.mainAxisSize,
    super.crossAxisAlignment,
    super.textDirection,
    super.verticalDirection,
    super.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be
    super.clipBehavior,
    required List<Widget> children,
    double spacing = 0,
    Widget Function(int index)? separatedBuilder,
  }) : super(
          children: [
            ...children
                .map((e) {
                  final i = children.indexOf(e);
                  final hasSeparated = e != children.last;
                  // final separated = separatedBuilder?.call(i);

                  return <Widget>[
                    e,
                    if (hasSeparated)
                      separatedBuilder?.call(i) ??
                          SizedBox(
                            width: direction == Axis.horizontal ? spacing : 0,
                            height: direction == Axis.vertical ? spacing : 0,
                          ),
                  ];
                })
                .toList()
                .flatMap(),
          ],
        );
}

extension _ListNewExt<E> on List<List<Widget>> {
  /// 二维数组降维一维数组
  List<E> flatMap() {
    final items = this as List<List<E>>;
    var list = <E>[];
    for (final e in items) {
      list.addAll(e);
    }
    return list;
  }
}

总结

NFlexSeparated 通过二维数组的思路实现将分隔组件或者 SizeBox 插入到各个子项之间。同时暴露出索引参数可以提供更多的可能性。代码简单维护性和扩展性也很高。

github