Flutter中使用ListView时如何更有效的设置Item项间距?

App开发中,列表的使用是比较常见的场景,Android原生开发时,ListView 是最常用的滑动组件,后来 Google 又推出了功能更加强大,使用更加灵活的 RecyclerView。同样,在 Flutter 开发中,系统也提供了 ListView 用于实现列表滑动的滑动组件,它可以沿一个方向线性排布所有子组件,并且支持基于 Sliver 的延迟构建模型。

Flutter 中使用 ListView 时,如何更有效的设置 Item 项间距呢?
看官方文档,可以找到,我们在构建 ListView 时有四种方式可以使用:

  1. ListView的默认构造函数,显示的构造 List< Widget>,该方式适合于具有少量子元素的列表视图,因为这种方式需要将所有需要显示的子Widget 都提前构建好,而不是等到子 Widget 真正显示的时候再构建,即通过默认的构造函数构建的 ListView 没有应用基于 Sliver 的懒加载模型。
  2. ListView.builder 利用 IndexedWidgetBuilder 按需构建子 Widget,适合列表项比较多( 或者无限 )的列表视图,因为构建器只有当子 Widget 真正需要显示的时候才会被构建,即使用 builder 方式构建的 ListView 支持基于 Sliver 的懒加载模型的。
  3. ListView.separated 利用两个 IndexedWidgetBuilder: itemBuilder 根据需要构建子 Widget 和 separatorBuilder 构建子 Widget 之间的分割符子 Widget。此构造函数适用于具有固定数量的子 Widget 的列表视图。
  4. ListView.custom 使用 SliverChildDelegate 构建,它提供了定制子模型的其它方面的能力,如 SliverChildDelegate 可以控制用于估算实际上不可见的子 Widget 的大小的算法。

关于上述四种方法的使用,这里不再具体讲述,后面我给出示例代码的地址;

下面就来回答一下,ListView 如果更有效的设置 Item 项间距?
首先滑动方向是竖直方向时如何设置 Item 项间距:

 		Expanded(
            child: RepaintBoundary(
              child: ListView.separated(
                  itemBuilder: (context, index) {
                    return _CommonItem(stringItem: _words[index]);
                  },
                  scrollDirection: Axis.vertical,
                  padding: _listVerticalPadding,
                  physics: BouncingScrollPhysics(),
                  separatorBuilder: (BuildContext context, int index) =>
                      Divider(
                        height: 16.0,
                        color: Color(0xFFFFFFFF),
                      ),
                  itemCount: _words.length),
            ),
          ),

接下来滑动方向是水平方向是如何设置 Item 项间距:

		Container(
              constraints: BoxConstraints(maxHeight: 100.0, minHeight: 0),
              child: ListView.separated(
                  itemBuilder: (context, index) {
                    return _CommonItem(stringItem: _words[index]);
                  },
                  scrollDirection: Axis.horizontal,
                  padding: _listHorizontalPadding,
                  physics: BouncingScrollPhysics(),
                  separatorBuilder: (BuildContext context, int index) =>
                      VerticalDivider(
                        width: 16.0,
                        color: Color(0xFFFFFFFF),
                      ),
                  itemCount: _words.length),
            ),

上面使用到的 Divider 和 VerticalDivider 都是系统提供的,当然我们也可以使用自定义的 分割符子项:

		Container(
              constraints: BoxConstraints(maxHeight: 100.0, minHeight: 0),
              child: ListView.separated(
                  itemBuilder: (context, index) {
                    return _CommonItem(stringItem: _words[index]);
                  },
                  scrollDirection: Axis.horizontal,
                  padding: _listHorizontalPadding,
                  physics: BouncingScrollPhysics(),
                  separatorBuilder: (BuildContext context, int index) =>
                       Container(
                        width: 16.0,
                        color: Color(0xFFFFFFFF),
                      ),
                  itemCount: _words.length),
            ),

是不是很简单啦,查看系统提供的 Divider 和 VerticalDivider 的源代码,会发现它们也只是封装组合了一下系统已有的 Widget 而已,下面看一下 Divider 的源码:

/// Creates a material design divider.
class MyDivider extends StatelessWidget {
  const MyDivider({
    Key key,
    this.height,
    this.thickness,
    this.indent,
    this.endIndent,
    this.color,
  }) : assert(height == null || height >= 0.0),
        assert(thickness == null || thickness >= 0.0),
        assert(indent == null || indent >= 0.0),
        assert(endIndent == null || endIndent >= 0.0),
        super(key: key);

  /// 水平分割器的高度值,都不设置,默认为:16.0
  final double height;

  /// 分割器内绘制的线的厚度
  final double thickness;

  /// 分割器内绘制的线距离前边缘的空间
  final double indent;

  /// 分割器内绘制的线距离后边缘的间距
  final double endIndent;

  /// 分割器内绘制的线的颜色值
  final Color color;

  /// 创建并计算分割器的边界
  static BorderSide createBorderSide(BuildContext context, { Color color, double width }) {
    final Color effectiveColor = color
        ?? (context != null ? (DividerTheme.of(context).color ?? Theme.of(context).dividerColor) : null);
    final double effectiveWidth =  width
        ?? (context != null ? DividerTheme.of(context).thickness : null)
        ?? 0.0;

    // Prevent assertion since it is possible that context is null and no color
    // is specified.
    if (effectiveColor == null) {
      return BorderSide(
        width: effectiveWidth,
      );
    }
    return BorderSide(
      color: effectiveColor,
      width: effectiveWidth,
    );
  }

  @override
  Widget build(BuildContext context) {
    final DividerThemeData dividerTheme = DividerTheme.of(context);
    final double height = this.height ?? dividerTheme.space ?? 16.0;
    final double thickness = this.thickness ?? dividerTheme.thickness ?? 0.0;
    final double indent = this.indent ?? dividerTheme.indent ?? 0.0;
    final double endIndent = this.endIndent ?? dividerTheme.endIndent ?? 0.0;

    /// 组合一些系统 Widget 来实现
    return SizedBox(
      // 分割器的高度
      height: height,
      child: Center(
        child: Container(
          // 分割器内绘制分割线的厚度
          height: thickness,
          // 分割器内绘制的分割线距离前后边缘的间距
          margin: EdgeInsetsDirectional.only(start: indent, end: endIndent),
          decoration: BoxDecoration(
            border: Border(
              bottom: createBorderSide(context, color: color, width: thickness),
            ),
          ),
        ),
      ),
    );
  }
}

完整实例代码可点击进入查看,读者可以试着运行一下,查看具体效果;

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值