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

Flutter中实现元素消失与收缩的Sliver动画方案咨询

Flutter实现滚动时顶部Logo/底部图标渐隐+中间文本收缩的Sliver方案

要实现你需要的滚动动画效果,核心是利用Flutter的Slivers组件监听滚动偏移量,动态控制元素的透明度、尺寸和位置。以下是完整的可运行实现方案:

核心思路

  • 用CustomScrollView作为滚动容器,搭配SliverPersistentHeader实现可滚动的头部区域
  • 通过shrinkOffset计算滚动进度,以此驱动Logo的渐隐、标题的收缩,以及底部操作栏的渐隐
  • 利用Opacity、动态样式赋值和Transform实现平滑过渡效果

完整代码实现

import 'package:flutter/material.dart';
import 'dart:math' as math;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sliver Scroll Animation Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ScrollAnimationPage(),
    );
  }
}

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

  @override
  State<ScrollAnimationPage> createState() => _ScrollAnimationPageState();
}

class _ScrollAnimationPageState extends State<ScrollAnimationPage> {
  final ScrollController _scrollController = ScrollController();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          // 顶部可收缩Header(包含Logo和标题)
          SliverPersistentHeader(
            pinned: true,
            delegate: _SliverHeaderDelegate(
              maxHeight: 220,
              minHeight: 60,
            ),
          ),
          // 主内容列表
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(
                title: Text('列表项 ${index + 1}'),
                subtitle: Text('这是第${index + 1}条滚动内容'),
              ),
              childCount: 30,
            ),
          ),
          // 底部操作栏
          SliverToBoxAdapter(
            child: AnimatedBuilder(
              animation: _scrollController,
              builder: (context, child) {
                double progress = 0.0;
                if (_scrollController.hasClients) {
                  final offset = _scrollController.offset;
                  // 滚动超过180时开始触发底部栏渐隐
                  progress = math.max(0, (offset - 180) / 100);
                  progress = math.min(progress, 1.0);
                }
                return Opacity(
                  opacity: 1 - progress,
                  child: Transform.translate(
                    offset: Offset(0, progress * 50),
                    child: Container(
                      height: 60,
                      color: Colors.white,
                      padding: const EdgeInsets.symmetric(horizontal: 20),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                          IconButton(icon: const Icon(Icons.home), onPressed: () {}),
                          IconButton(icon: const Icon(Icons.favorite), onPressed: () {}),
                          IconButton(icon: const Icon(Icons.settings), onPressed: () {}),
                        ],
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

// 自定义SliverHeader代理类
class _SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
  final double maxHeight;
  final double minHeight;

  _SliverHeaderDelegate({
    required this.maxHeight,
    required this.minHeight,
  });

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    // 计算收缩进度:0=完全展开,1=完全收缩
    final progress = shrinkOffset / maxHeight;
    return Container(
      color: Colors.white,
      child: Stack(
        alignment: Alignment.center,
        children: [
          // Logo区域:渐隐+上移
          Opacity(
            opacity: 1 - progress,
            child: Transform.translate(
              offset: Offset(0, progress * -80),
              child: const FlutterLogo(size: 120),
            ),
          ),
          // 标题区域:收缩+上移
          Positioned(
            top: 20 + progress * 20,
            left: 20,
            right: 20,
            child: Text(
              '我的页面',
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 28 - progress * 12, // 从28px收缩到16px
                fontWeight: FontWeight.bold,
                color: Colors.blue,
              ),
            ),
          ),
        ],
      ),
    );
  }

  @override
  double get maxExtent => maxHeight;

  @override
  double get minExtent => minHeight;

  @override
  bool shouldRebuild(covariant _SliverHeaderDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight;
  }
}

关键细节说明

  • 滚动进度统一控制:通过shrinkOffset / maxExtent得到0-1的进度值,让所有动画保持同步节奏
  • Logo动画:结合Opacity和Transform.translate,让Logo在上移过程中逐渐消失,过渡更自然
  • 标题动画:动态修改字体大小和位置,实现从居中大标题到顶部小标题的平滑转换
  • 底部操作栏:监听滚动偏移量,当滚动到指定位置后触发渐隐+上移动画,避免遮挡内容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:45:36