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

Flutter实现滚动时文本从Body动画到AppBar并切换行数

问题描述

需要实现两个核心效果:

  • 页面滚动至顶部时,Body中子组件的两行文本(主文本+次要文本)滚动到顶部的AppBar区域
  • 滚动过程中,文本从两行平滑过渡为仅显示主文本的一行
    当前页面结构里,底部是Expanded嵌套SingleChildScrollView,希望滚动操作能带动文本的动画变化。

(初始状态:页面顶部显示两行文本,下方是可滚动的内容区域;滚动后状态:顶部AppBar仅显示单行主文本,原文本区域被滚动内容覆盖)

实现方案

核心是用CustomScrollView结合SliverAppBar的伸缩特性,配合动画控制器监听滚动偏移,动态调整文本的显示状态。

1. 初始化控制器与动画变量

先定义滚动控制器和动画相关变量,用于监听滚动进度并驱动文本过渡:

class _FirstScreenState extends State<FirstScreen> {
  final PageController _pageController = PageController();
  int currentIndex = 0;
  final ScrollController _scrollController = ScrollController();
  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,设置过渡时长
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 300),
      vsync: this,
    );
    _animation = Tween<double>(begin: 0, end: 1).animate(_animationController);
    
    // 监听滚动事件,根据偏移量计算动画进度
    _scrollController.addListener(() {
      // 这里的80是文本区域的初始高度,可根据实际UI调整
      double progress = _scrollController.offset / 80;
      _animationController.value = progress.clamp(0, 1);
    });
  }

  @override
  void dispose() {
    // 记得销毁控制器,避免内存泄漏
    _scrollController.dispose();
    _animationController.dispose();
    _pageController.dispose();
    super.dispose();
  }

2. 重构页面为CustomScrollView结构

把原来的Column替换成CustomScrollView,将文本整合到SliverAppBar的伸缩空间中,实现滚动联动:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverAppBar(
            backgroundColor: Colors.white,
            pinned: true, // 滚动到顶部时固定AppBar
            expandedHeight: 80, // 展开时的高度,对应初始文本区域高度
            flexibleSpace: FlexibleSpaceBar(
              titlePadding: const EdgeInsets.only(left: 16, bottom: 16),
              title: AnimatedBuilder(
                animation: _animation,
                builder: (context, child) {
                  return Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      const Text('Some Text...'),
                      // 通过动画值控制次要文本的透明度和高度,实现渐隐消失
                      Opacity(
                        opacity: 1 - _animation.value,
                        child: SizedBox(
                          height: (1 - _animation.value) * 20,
                          child: const Text('Some Secondary Text...'),
                        ),
                      ),
                    ],
                  );
                },
              ),
            ),
          ),
          // 把原来的PageView放到SliverToBoxAdapter中
          SliverToBoxAdapter(
            child: SizedBox(
              height: MediaQuery.of(context).size.height - kToolbarHeight,
              child: PageView(
                controller: _pageController,
                onPageChanged: (page) {
                  setState(() => currentIndex = page);
                },
                children: [
                  SingleChildScrollView(
                    // 这里替换成你的实际滚动内容
                    child: Column(
                      children: List.generate(50, (index) {
                        return Padding(
                          padding: const EdgeInsets.all(16),
                          child: Text('滚动内容 $index'),
                        );
                      }),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • SliverAppBar的pinned: true属性确保滚动到顶部时AppBar固定,expandedHeight设置初始展开状态的高度,对应原来的文本区域高度
  • 滚动监听中通过偏移量计算动画进度,控制次要文本的透明度和高度,实现两行到一行的平滑过渡
  • AnimatedBuilder避免整个页面重复build,只更新文本相关的UI

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 11:35:23