You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现CustomScrollView底部区块自适应布局(无需LayoutBuilder)

实现自适应底部对齐的CustomScrollView布局

需求说明

  • 基于CustomScrollView构建布局,底部区块需满足两种自适应状态:
    1. 屏幕空间不足时,底部区块与其他Sliver处于同一滚动流,整体可滚动
    2. 屏幕空间充足时,底部区块固定对齐到屏幕底部,整体无需滚动
  • 限制:禁止使用LayoutBuilder,因底部区块内容可能随字体大小、显示内容动态变化
  • 额外场景:CustomScrollView可能嵌套在可滚动容器(如模态底部表单)内,且可获取该容器的控制器

布局效果预览

布局预览图

解决方案

核心思路是通过SliverFillRemaining结合动态调整hasScrollBody属性实现自适应,针对模态底部表单场景,利用滚动控制器监听状态来适配布局逻辑。

完整代码实现

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('自适应底部Sliver')),
        body: const AdaptiveScrollView(),
        floatingActionButton: FloatingActionButton(
          onPressed: () => showModalBottomSheet(
            context: context,
            isScrollControlled: true,
            builder: (_) => const ModalBottomSheetContent(),
          ),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

class AdaptiveScrollView extends StatelessWidget {
  const AdaptiveScrollView({super.key});

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverToBoxAdapter(
          child: Container(height: 100, color: Colors.blue),
        ),
        SliverToBoxAdapter(
          child: Container(height: 300, color: Colors.green),
        ),
        // 核心:用SliverFillRemaining实现自适应底部对齐
        SliverFillRemaining(
          hasScrollBody: false,
          child: Align(
            alignment: Alignment.bottomCenter,
            child: Container(
              height: 300,
              color: Colors.orange,
              child: const Center(
                child: Text('保持底部对齐或随内容滚动'),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class ModalBottomSheetContent extends StatefulWidget {
  const ModalBottomSheetContent({super.key});

  @override
  State<ModalBottomSheetContent> createState() => _ModalBottomSheetContentState();
}

class _ModalBottomSheetContentState extends State<ModalBottomSheetContent> {
  final ScrollController _scrollController = ScrollController();
  bool _hasScrollBody = false;

  @override
  void initState() {
    super.initState();
    // 监听模态表单滚动状态,动态调整滚动体属性
    _scrollController.addListener(() {
      setState(() {
        _hasScrollBody = _scrollController.position.maxScrollExtent > 0;
      });
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      controller: _scrollController,
      slivers: [
        SliverToBoxAdapter(
          child: Container(height: 100, color: Colors.blue),
        ),
        SliverToBoxAdapter(
          child: Container(height: 300, color: Colors.green),
        ),
        SliverFillRemaining(
          hasScrollBody: _hasScrollBody,
          child: Align(
            alignment: Alignment.bottomCenter,
            child: Container(
              height: 300,
              color: Colors.orange,
              child: const Center(
                child: Text('保持底部对齐或随内容滚动'),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

代码说明

  1. 基础场景处理:SliverFillRemaining设置hasScrollBody: false时,会自动填充屏幕剩余空间,将子组件对齐到底部;当内容总高度超出屏幕时,CustomScrollView会自动激活滚动,底部区块会跟随其他Sliver一起滚动。
  2. 模态底部表单场景:通过监听滚动控制器的maxScrollExtent值,判断内容是否超出容器范围,动态切换hasScrollBody属性,实现滚动状态下的自适应布局。

内容的提问来源于stack exchange,提问作者Dominik Roszkowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:02:31