Flutter-详解布局(滚动和Sliver系列布局控件)

DEMO

滚动布局相关控件

1. SingleChildScrollView

  • 说明: 为单个子控件添加滚动功能,支持垂直或水平滚动,类似于 Android 中的 ScrollView 或 iOS 中的 UIScrollView

  • 规则:

  1. scrollDirection: 滚动方向,默认为垂直方向(Axis.vertical)。
  2. reverse: 是否反转滚动方向,默认为 false。
  3. padding: 子组件的内边距。
  4. primary: 是否为主滚动视图,用于判断是否自动生成控制器。
  5. physics: 控制滚动行为,如滑动速度和边缘效应。
  6. controller: 用于控制滚动位置的对象。
  7. child: 要包含在 SingleChildScrollView 中的子组件。
  8. dragStartBehavior: 拖动开始行为,默认为 DragStartBehavior.start。
  9. clipBehavior: 裁剪行为,默认为 Clip.hardEdge。
  10. restorationId: 恢复标识。
  11. keyboardDismissBehavior: 键盘关闭行为,默认为
    ScrollViewKeyboardDismissBehavior.manual。
  • 注意: 不支持 Sliver 延迟加载,内容过多时性能较差,建议改用 ListView,适用于内容不会超过屏幕尺寸太多的情况,因为当内容超出屏幕尺寸较多时,性能会受到影响

  • 推荐: 单个子组件的滚动(如表单、文章详情页),内容量较小,无需动态加载的场景

Scrollbar(
  child: SingleChildScrollView(
    scrollDirection: Axis.vertical,
    child: Center(
      child: Column(
        children: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
            .split('')
            .map((c) => Container(
                  margin: const EdgeInsets.all(8.0),
                  padding: const EdgeInsets.all(16.0),
                  decoration: BoxDecoration(
                    color: Colors.red,
                    borderRadius: BorderRadius.circular(8.0),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withOpacity(0.5),
                        blurRadius: 4.0,
                        offset: const Offset(2.0, 2.0),
                      ),
                    ],
                  ),
                  child: Text(
                    c,
                    style: const TextStyle(
                        color: Colors.white, fontSize: 24.0),
                  ),
                ))
            .toList(),
      ),
    ),
  ),
)
SingleChildScrollView

2. PageView

  • 说明: 是一个分页滚动视图,用于实现可滑动的页面切换(水平/垂直)
  1. PageView():静态页面列表
  2. PageView.builder() :动态懒加载(大数据量优化)
  3. PageView.custom() :高度自定义子组件
  • 规则:
  1. scrollDirection: 视图滚动方向,默认为水平方向(Axis.horizontal)。
  2. reverse: 是否反转方向,默认为 false。
  3. controller: 用于监听视图滚动情况的控制器。
  4. onPageChanged: 索引改变时的回调函数。
  5. physics: 滚动逻辑,可以设置为不滚动、滚动或滚动到边缘是否反弹等。
  6. pageSnapping: 是否启用页面对齐,默认为 true。
  7. allowImplicitScrolling: 是否允许隐式滚动,默认为 false。
  • 注意: 使用 PageView.builder 避免一次性构建所有页面,搭配 AutomaticKeepAliveClientMixin 缓存页面状态,嵌套 ListView 时设置 physics: NeverScrollableScrollPhysics() 禁用父级滑动,避免垂直滑动冲突

  • 推荐: 适用于引导页、轮播图、全屏滑动视图等场景

PageView(
  children: <Widget>[
    Container(
      alignment: Alignment.center,
      decoration: const BoxDecoration(color: Colors.green),
      child: const Text("页面 0",
          style: TextStyle(fontSize: 20, color: Colors.black)),
    ),
    Container(
      alignment: Alignment.center,
      decoration: const BoxDecoration(color: Colors.red),
      child: const Text("页面 1",
          style: TextStyle(fontSize: 20, color: Colors.white)),
    ),
    Container(
      alignment: Alignment.center,
      decoration: const BoxDecoration(color: Colors.blue),
      child: const Text("页面 2",
          style: TextStyle(fontSize: 20, color: Colors.white)),
    ),
  ],
)
PageView

3. Scrollbar

  • 说明: 一个 Material 风格的滚动指示器,Flutter 的滚动视图(如 ListView、GridView 等)没有滚动条,但可以通过将滚动视图包裹在 Scrollbar 组件中来添加滚动条

  • 规则:

  1. child: 必需属性,用于指定需要滚动的内容。
  2. controller: 可选属性,用于控制滚动行为。
  3. isAlwaysShown: 布尔值,表示是否始终显示滚动条。默认为 false,需要设置 controller 才能显示。
  4. thickness: 滚动条的宽度。
  5. radius: 滚动条的圆角半径。
  6. showTrackOnHover: 布尔值,表示当鼠标悬停在滚动条上时是否显示轨道。
  7. hoverThickness: 鼠标悬停时滚动条的宽度。
  8. notificationPredicate: 用于过滤滚动通知的谓词。
  9. interactive: 布尔值,表示滚动条是否可以交互。
  10. scrollbarOrientation: 滚动条的方向。
  • 注意: Scrollbar 需要包裹可滚动组件(如 ListView),但不要求直接作为父级,只要在组件树中存在即可

  • 推荐: 自定义滚动条样式 、显示滚动条

Scrollbar(
  child: ListView.builder(
    itemCount: 40,
    itemBuilder: (context, index) {
      return Card(
        child: Container(
          height: 45,
          alignment: Alignment.center,
          child: Text('$index'),
        ),
      );
    },
  ),
)
Scrollbar

4. RefreshIndicator

  • 说明: 实现 Material 风格下拉刷新功能的组件,通常包裹在可滚动的子组件(如 ListView、CustomScrollView)外层。当用户下拉超过一定距离时,会触发回调函数执行刷新操作,并显示加载动画

  • 规则:

  1. child: 必需参数,通常为 ListView 或 CustomScrollView。
  2. displacement: 可选参数,从子组件的顶部或底部边缘到刷新指示符所在位置的距离。
  3. onRefresh: 必需参数,当用户将刷新指示器拖到足够远以表明他们希望应用刷新时调用的函数。
  4. color: 可选参数,进度指示器前景色。
  5. backgroundColor: 可选参数,进度指示器背景色。
  6. notificationPredicate: 可选参数,用于过滤滚动通知。
  7. semanticsLabel: 可选参数,语义标签。
  8. semanticsValue: 可选参数,语义值。
  • 注意: 上拉加载:需结合 ScrollController 监听滚动位置,实现 LoadMore 功能,onRefresh 必须返回 Future,且需在数据加载完成后完成该 Future,否则刷新动画不会停止

  • 推荐: 列表数据刷新

List<String> _items = List.generate(20, (index) => "Item $index");

Future<void> _refresh() async {
    await Future.delayed(Duration(seconds: 2));
    setState(() {
      _items = List.generate(20, (index) => "Refreshed Item $index");
    });
}
  
 RefreshIndicator(
  onRefresh: _refresh,
  child: ListView.builder(
    itemCount: _items.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(_items[index]),
      );
    },
  ),
) 

RefreshIndicator

5. DraggableScrollableSheet

  • 说明: 允许用户通过手势操作来拖动一个可滚动的区域。它常用于创建底部弹出式面板或可拖动的控件,类似于高德地图首页从底部滑动向上的效果。该组件继承自 StatefulWidget,通过调整滚动区域的大小来响应拖动手势,直到达到限制,然后开始滚动

  • 规则:

1. initialChildSize: 设置初始高度占屏幕的比例,范围为 0 到 1,默认为 0.5。

2. minChildSize: 指定最小高度占屏幕的比例,默认为 0.25。

3. maxChildSize: 指定最大高度占屏幕的比例,默认为 1.0。

4. expand: 控制是否允许在内容小于屏幕高度时扩展以填充屏幕,默认为 true。

5. snap: 布尔值,用于控制滚动停止时是否自动捕捉到接近的最小或最大值,默认为 false。

6. builder: 构建函数,用于构建 DraggableScrollableSheet 的内容,接受两个参数:
BuildContext 和 ScrollController,返回一个 Widget,通常是 SingleChildScrollView 或 ListView。

  • 注意: 确保 maxChildSize >= initialChildSize >= minChildSize,否则会抛出异常

  • 推荐: 底部弹出式面板评论区、详细信息展示

Stack(
    children: [
      Positioned(
        top: 0,
        bottom: 0,
        left: 0,
        right: 0,
        child: Container(
          color: Colors.grey[300],
          child: Center(
            child: Text('背景内容'),
          ),
        ),
      ),
      DraggableScrollableSheet(
        initialChildSize: 0.5,
        minChildSize: 0.3,
        maxChildSize: 1.0,
        builder: (_, controller) {
          return Container(
            color: Theme.of(context).cardColor,
            child: Scrollbar(
              child: ListView.builder(
                controller: controller,
                itemCount: 20,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text('Item $index'),
                  );
                },
              ),
            ),
          );
        },
      ),
    ],
)
DraggableScrollableSheet

6. NestedScrollView

  • 说明: 用于协调多个独立滚动区域的容器组件。它通过 NestedScrollViewCoordinator 协调两个滚动控制器:外层滚动(Header)和内层滚动(Body)。外层滚动由 headerSliverBuilder 定义的 Sliver 组件组成(如 SliverAppBar),受 PrimaryScrollController 控制;内层滚动由 body 定义的普通滚动组件(如 ListView),拥有独立的 ScrollController

  • 规则:

1.外层滚动优先: 当用户向下滑动时,外层滚动先消耗滚动事件,直到外层完全折叠后,内层滚动接管。
2.内层反向优先: 当内层滚动到达顶部且用户继续上滑时,外层滚动会展开

  • 注意: 确保内部可滚动组件(body)的 physics 属性设置正确,以避免滚动冲突

  • 推荐: 折叠头部图片效果

NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[
      SliverAppBar(
        title: const Text('NestedScrollView Example'),
        pinned: true,
        floating: true,
        forceElevated: innerBoxIsScrolled,
        expandedHeight: 160.0,
        flexibleSpace: FlexibleSpaceBar(
          background: Image.network(
            "https://miaobi-lite.bj.bcebos.com/miaobi/5mao/b%275aS05YOPYXBwXzE3MzM3NjcxODMuODQyNDgz%27/0.png",
            fit: BoxFit.cover,
          ),
        ),
      ),
    ];
  },
  body: ListView.builder(
    itemCount: 50,
    itemBuilder: (BuildContext context, int index) {
      return ListTile(
        title: Text('List Item $index'),
      );
    },
  ),
)

NestedScrollView

7. ScrollConfiguration

  • 说明: 用于统一控制子组件树的滚动行为(如滚动条样式、拖拽反馈等),尤其适用于定制不同平台的滚动体验

  • 规则: ScrollConfiguration 应该直接放置在列表组件的父组件下,以避免与其他列表组件发生冲突,若需要全局设置整个应用程序的所有列表默认样式,则可将 ScrollConfiguration 组件插入接近组件树根部的位置

  • 注意: 由于 ScrollConfiguration 的子组件可能为 null,因此在使用时需要进行 null 检查,以避免潜在的错误

  • 推荐: 自定义滚动条的样式,如颜色、厚度

ScrollConfiguration(
  behavior: MyCustomBehavior(),
  child: ListView.separated(
    itemCount: 20,
    separatorBuilder: (_, __) => Divider(),
    itemBuilder: (_, index) {
      return Container(
        height: 56,
        alignment: Alignment.center,
        child: Text("这是第 ${index + 1} 项"),
      );
    },
  ),
)

class MyCustomBehavior extends ScrollBehavior {
  @override
  Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
    // 自定义滚动条样式
    return GlowingOverscrollIndicator(
      axisDirection: axisDirection,
      color: Colors.grey, // 设置滚动条颜色为灰色
      child: child,
    );
  }

  @override
  ScrollPhysics getScrollPhysics(BuildContext context) {
    // 使用 ClampingScrollPhysics 防止过度滚动
    return ClampingScrollPhysics();
  }
}
ScrollConfiguration

Sliver 系列控件 (用于 CustomScrollView)

1. SliverList

  • 说明: 创建线性可滚动列表的组件,它属于 Sliver 家族的一部分,SliverList 需要与 CustomScrollView 结合使用,以实现复杂的滚动效果

  • 规则: 使用 SliverChildDelegate 来构建子组件。常用的实现是 SliverChildBuilderDelegate 和 SliverChildListDelegate。SliverChildListDelegate: (静态列表)适用于已知数量的子组件,一次性全部渲染。
    SliverChildBuilderDelegate: (动态构建列表项)适用于未知数量的子组件,按需加载和销毁列表项,提升性能。

  • 注意: 必须通过代理生成子项,其核心特点是支持统一滚动、避免滑动冲突

  • 推荐: 滚动时两个列表无缝衔接,无滑动冲突,实现吸顶 AppBar 和列表联动

CustomScrollView(
  slivers: <Widget>[
    const SliverAppBar(
      expandedHeight: 250,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('SliverList 示例'),
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return ListTile(
            title: Text('Item #$index'),
          );
        },
        childCount: 20, // 列表项数目
      ),
    ),
    SliverFixedExtentList(
      itemExtent: 50.0,
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return Container(
            alignment: Alignment.center,
            color: Colors.blue[100 * (index % 9)],
            child: Text("list item $index"),
          );
        },
        childCount: 50,
      ),
    ),
  ],
)
SliverList

2. SliverGrid

  • 说明: 创建网格布局的 Sliver 组件,它非常适合与其他 Sliver 组件结合使用,以构建复杂的可滚动布局,

  • 规则: SliverGrid 需要指定 gridDelegate 和 delegate 属性。gridDelegate 控制网格布局,而 delegate 用于构建每个网格单元的内容,SliverGrid 提供了三种主要的构造方法:1.SliverGrid:适用于动态显示内容(从数据库或 API 获取)。2.SliverGrid.extent:允许指定子项的最大跨轴范围。3.SliverGrid.count:用于指定跨轴上固定数量的子项

  1. delegate: 控制子项生成,常用:
  • SliverChildListDelegate: 静态子项列表
  • SliverChildBuilderDelegate: 动态懒加载子项
  1. gridDelegate: 控制网格布局,两种实现:
  • SliverGridDelegateWithFixedCrossAxisCount: 固定列数
  • SliverGridDelegateWithMaxCrossAxisExtent: 限制子项最大宽度
  • 注意: 按需加载子项,性能优于一次性构建所有子项的 GridView,与普通的网格布局组件(如 GridView)不同,SliverGrid 不会提前渲染所有网格项,而是在滚动时动态渲染当前可见的部分,从而节省内存和渲染时间

  • 推荐: 下拉刷新、嵌套滚动

CustomScrollView(
  slivers: [
    SliverAppBar(
      // 可折叠标题栏
      expandedHeight: 200,
      flexibleSpace: FlexibleSpaceBar(
          background: Image.network(
              'https://miaobi-lite.bj.bcebos.com/miaobi/5mao/b%275aS05YOPYXBwXzE3MzM3NjcxODMuODQyNDgz%27/0.png',
              fit: BoxFit.cover)),
    ),
    SliverGrid(
      // 网格区域
      delegate: SliverChildBuilderDelegate((ctx, i) => Container(
            color: [
              Colors.red,
              Colors.blue,
              Colors.black,
              Colors.yellow,
              Colors.green
            ][i % 5],
          )),
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 150, // 自适应宽度
        mainAxisSpacing: 10,
        crossAxisSpacing: 10,
      ),
    ),
  ],
)
SliverGrid

3. SliverAppBar

  • 说明: 创建可滚动应用栏的组件,它可以在滚动过程中动态变化,如展开、收缩或固定在顶部。SliverAppBar 通常与 CustomScrollView 搭配使用,可以实现复杂的滚动效果,类似于 Android 中的 CollapsingToolbarLayout

  • 规则:

  1. expandedHeight: 指定 SliverAppBar 展开时的高度。
  2. pinned: 设置为 true 时,AppBar 将在滚动到顶部时固定在屏幕顶部。
  3. floating: 设置为 true 时,当向下滚动时,AppBar 会重新出现。
  4. snap: 配合 floating 属性使用,当设置为 true 时,SliverAppBar 会在滑动过程中自动“吸附”到顶部或消失。
  5. flexibleSpace: 用于定义可伸缩的空间,通常使用 FlexibleSpaceBar。
  6. backgroundColor: 设置 AppBar 的背景色。
  7. title: 设置 AppBar 的标题。
  8. leading: 设置 AppBar 前面显示的一个控件。
  9. actions: 设置 AppBar 的操作按钮。
  10. bottom: 设置 AppBar 的底部区域,通常用于添加 TabBar。
  • 注意: pinned 和 floating 不可同时为 true,否则引发布局冲突,必须嵌套在 Sliver 容器,避免在 flexibleSpace 中使用复杂动画,可能导致滚动卡顿,如果需要实现滚动折叠效果,flexibleSpace 必须包含 FlexibleSpaceBar

  • 推荐: 折叠式图片标题、快速展开的搜索栏、带悬浮 Tab 的分页布局

DefaultTabController(
  length: 2,
  child: Scaffold(
    body: NestedScrollView(
      headerSliverBuilder:
          (BuildContext context, bool innerBoxIsScrolled) {
        return <Widget>[
          SliverAppBar(
            expandedHeight: 200.0,
            floating: false,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('SliverAppBar Example'),
              background: Image.network(
                'https://img.win3000.com/m00/76/6a/3fb7a5729f51fedf4261cb02addbd133.jpg',
                fit: BoxFit.cover,
              ),
            ),
            bottom: const TabBar(
              tabs: [
                Tab(icon: Icon(Icons.home), text: 'Home'),
                Tab(icon: Icon(Icons.settings), text: 'Settings'),
              ],
            ),
          ),
        ];
      },
      body: TabBarView(
        children: [
          CustomScrollView(
            slivers: [
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    return Container(
                      height: 85,
                      alignment: Alignment.center,
                      color: Colors
                          .primaries[index % Colors.primaries.length],
                      child: Text(
                        '$index',
                        style: const TextStyle(
                            color: Colors.white, fontSize: 20.0),
                      ),
                    );
                  },
                  childCount: 25,
                ),
              ),
            ],
          ),
          CustomScrollView(
            slivers: [
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                    return Container(
                      height: 85,
                      alignment: Alignment.center,
                      color: Colors
                          .primaries[index % Colors.primaries.length],
                      child: Text(
                        '$index',
                        style: const TextStyle(
                            color: Colors.white, fontSize: 20.0),
                      ),
                    );
                  },
                  childCount: 25,
                ),
              ),
            ],
          ),
        ],
      ),
    ),
  ),
)
SliverAppBar

4. SliverPadding

  • 说明: 专门用于在 滚动布局(如 CustomScrollView)中为子 Sliver 组件添加内边距的组件。它属于 Sliver 家族,适用于需要精细控制滚动区域间距的场景,例如在 SliverList、SliverGrid 等组件外部包裹边距

  • 规则: 仅能作为 CustomScrollView 的 slivers 数组的直接子组件使用,不可单独用于普通布局。SliverPadding 的 sliver 属性必须是一个 Sliver 类型的组件,否则会导致运行时错误

核心参数:
padding(必填):通过 EdgeInsets 设置边距(上、下、左、右或对称边距)。
sliver(必填):接受任意 Sliver 组件(如 SliverList、SliverGrid)

  • 注意: 普通 Padding 用于静态布局,而 SliverPadding 专为滚动视图优化,能正确计算滚动区域和视口边界,避免在 SliverPadding 内嵌套多层 Sliver 组件,推荐直接包裹目标组件,如需避开刘海屏或底部导航栏,优先使用 SliverSafeArea 而非 SliverPadding

  • 推荐: 列表/网格的全局边距、复杂滚动布局的间隔控制

CustomScrollView(
  slivers: [
    const SliverAppBar(title: Text('SliverPadding 示例')),
    SliverPadding(
      padding: const EdgeInsets.all(20), // 全局边距
      sliver: SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) => ListTile(
            title: Text("列表项 $index"),
          ),
          childCount: 20,
        ),
      ),
    ),
    SliverPadding(
      padding: const EdgeInsets.only(top: 30, bottom: 50), // 仅上下边距
      sliver: SliverGrid(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 10,
          mainAxisSpacing: 10,
        ),
        delegate: SliverChildBuilderDelegate(
          (context, index) => Container(
            color: Colors.blue,
            child: Center(child: Text("网格 $index")),
          ),
          childCount: 6,
        ),
      ),
    ),
  ],
)
SliverPadding

5. SliverToBoxAdapter

  • 说明: 将普通 盒模型布局组件(如 Container、Text 等)嵌入到 Sliver 滚动布局(如 CustomScrollView)中的适配器。它允许非 Sliver 组件(即非滚动专用组件)作为 Sliver 列表的一部分参与滚动,在 CustomScrollView 的 slivers 列表中混合使用常规 Widget 与 Sliver 组件(如 SliverList、SliverGrid)

  • 规则: 只能用于 CustomScrollView.slivers 或嵌套的 Sliver 容器(如 SliverPadding),非 Sliver 组件(如 Container)必须指定高度或宽度,否则会因无限空间约束导致布局错误,若子组件包含 ListView 等自身可滚动的 Widget,需禁用其滚动行为(如 physics: NeverScrollableScrollPhysics())

  • 注意: 尽量避免在 SliverToBoxAdapter 中嵌套过多的复杂布局,这可能会导致布局计算变慢,虽然 SliverToBoxAdapter 提供了便利性,但在大量使用时需要注意性能问题,尽量减少不必要的包裹,如果 child 的高度没有明确设置,可能会导致布局问题或渲染错误

  • 推荐: 列表头部/尾部添加独立 Widget(如 Banner、标题、按钮)

CustomScrollView(
  slivers: [
    // 1. 顶部 Banner
    SliverToBoxAdapter(
      child: Container(
        height: 200,
        decoration: const BoxDecoration(
          gradient: LinearGradient(colors: [Colors.blue, Colors.green]),
        ),
        child: const Center(
            child: Text('欢迎页', style: TextStyle(fontSize: 24))),
      ),
    ),

    // 2. 分隔标题
    SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Text('产品列表',
            style: Theme.of(context).textTheme.headlineSmall),
      ),
    ),

    // 3. 网格布局
    SliverGrid(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        childAspectRatio: 1.5,
      ),
      delegate: SliverChildBuilderDelegate(
        (context, index) =>
            Card(child: Center(child: Text('产品 $index'))),
        childCount: 6,
      ),
    ),

    // 4. 底部按钮
    SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 20),
        child: ElevatedButton(
          onPressed: () {},
          child: const Text('加载更多'),
        ),
      ),
    ),
  ],
)
SliverToBoxAdapter

6. SliverFillRemaining

  • 说明: 用于 填充滚动视图剩余空间 的 Sliver 布局组件。它会根据父容器的剩余高度或宽度自动调整自身大小,确保内容区域占满屏幕可视区域。适用于需要动态适配剩余空间的场景

  • 规则: 作为 CustomScrollView 的 slivers 列表中的最后一个组件,用于填充剩余空间,允许组件自适应地填充剩余空间,无论剩余空间如何变化

关键参数
1. child: 必填,用于填充剩余空间的子组件。
2. hasScrollBody:

  • 默认 true:子组件可滚动(如 ListView),适用于内容超出剩余空间的情况。
  • false:子组件不可滚动(如 Column),内容高度不超过剩余空间 710。
    3. fillOverscroll:
  • true:在过度滚动时继续填充(如 iOS 回弹效果)。
  • false:仅填充未滚动区域(默认值)
  • 注意: 当 hasScrollBody: true 时,子组件必须是可滚动的(如 ListView),否则会抛出布局错误,SliverFillRemaining 在滚动视图中动态填充剩余空间,而 SingleChildScrollView 会尝试包裹整个内容,可能导致布局溢出,在长列表底部使用 SliverFillRemaining 时,避免在 child 中嵌套复杂布局,防止重复构建

  • 推荐: 表单底部固定按钮、分页加载占位、全屏内容适配

CustomScrollView(
  slivers: <Widget>[
    const SliverAppBar(
      expandedHeight: 200.0,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('Flexible Space'),
      ),
    ),
    SliverList(
      delegate: SliverChildListDelegate(
        [
          Container(
            height: 500,
            color: Colors.amber,
            alignment: Alignment.center,
            child: const Text('List Item'),
          ),
        ],
      ),
    ),
    SliverFillRemaining(
      hasScrollBody: false,
      child: Container(
        color: Colors.blue,
        alignment: Alignment.center,
        child: const Text('Fill Remaining Space',
            style: TextStyle(color: Colors.white)),
      ),
    ),
  ],
)
SliverFillRemaining

7. SliverPersistentHeader

  • 说明: 创建可折叠/悬浮的头部的Sliver组件,在滚动视图中创建一个持久化的头部组件,该头部组件可以根据滚动位置自动调整大小、透明度或其他属性。可以固定在页面顶部或底部,也可以随着滚动而动态变化,从而实现更加灵活的布局效果

  • 规则:

1. delegate: 必需属性,用于定义头部组件的构建逻辑。
2. pinned: 布尔值,默认为 false,当设置为 true 时,头部组件会在滚动到顶部时固定在视图中。
3. floating: 布尔值,默认为 false,当设置为 true 时,头部组件会在滚动到顶部时短暂显示,然后隐藏。

  • 注意: pinned 和 floating 属性不能同时为 true,避免在build()方法中执行耗时操作,推荐使用const组件或StatelessWidget。若内容复杂,用AutoDispose混合Riverpod管理状态,不可直接嵌套ListView等滚动组件,需通过SliverToBoxAdapter,确保minExtent和maxExtent精确计算,避免小数精度误差。必须显式指定头部组件的高度范围(minExtent 和 maxExtent)。如果依赖 overlapsContent 参数构建子组件,则必须保证之前至少还有一个 SliverPersistentHeader 或 SliverAppBar

  • 推荐: 电商分类导航、渐变收缩的Banner图

CustomScrollView(
  slivers: <Widget>[
    // 创建一个固定在页面顶部的 SliverPersistentHeader
    SliverPersistentHeader(
      pinned: true, // 固定在页面顶部
      delegate: _MyHeaderDelegate(),
    ),
    // 添加一个普通的 SliverList
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return ListTile(
            title: Text('Item $index'),
            leading: CircleAvatar(child: Text('$index')),
          );
        },
        childCount: 50,
      ),
    ),
  ],
)

class _MyHeaderDelegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    // 获取状态栏高度
    final double statusBarHeight = MediaQuery.of(context).padding.top;
    // 构建头部组件
    return Container(
      padding: EdgeInsets.only(top: statusBarHeight),
      color: Colors.blue,
      alignment: Alignment.center,
      child: const Text('Header'),
    );
  }

  @override
  double get maxExtent => 120; // 最大高度

  @override
  double get minExtent => 40; // 最小高度

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
      false;
}
SliverPersistentHeader

还有其他的一些Sliver布局控件

SliverFillViewport:填充视口

SliverAnimatedList:动画列表

SliverAnimatedOpacity:动画透明度

SliverIgnorePointer:忽略指针事件

SliverOpacity:透明度效果

SliverSafeArea:安全区域

SliverOverlapInjector:处理重叠

SliverOverlapAbsorber:吸收重叠

因为网站字数限制,只能分系列了,需要一次性看完请去这里
Flutter-详解布局(核心布局控件)
Flutter-详解布局(单子组件布局控件)
Flutter-详解布局(弹性和层叠布局辅助控件)
Flutter-详解布局(响应式和平台适配及特殊布局控件)
需要代码去这里DEMO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容