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

Flutter滚动组件淡入滑入动画咨询:求原理与示例

滚动触发淡入滑入动画的实现原理

这类动画的核心逻辑围绕滚动监听+可见性判断+动画过渡三个环节:

  • 滚动监听:持续捕获页面滚动位置(网页中用scrollTop,Flutter中用ScrollController)
  • 可见性检测:判断目标组件是否进入视口范围(网页通过getBoundingClientRect()计算元素位置,Flutter可通过组件位置与视口的交集判断)
  • 动画过渡:当组件进入视口一定比例时,启动从「透明→不透明」「偏移位置→原位置」的过渡动画,搭配缓动函数(如ease-out)让动画更自然流畅
Flutter 淡入滑入动画示例

下面是一个列表滚动触发动画的简单实现,结合ScrollController、AnimatedOpacity和AnimatedContainer完成效果:

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('滚动淡入滑入示例')),
        body: const AnimatedScrollList(),
      ),
    );
  }
}

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

  @override
  State<AnimatedScrollList> createState() => _AnimatedScrollListState();
}

class _AnimatedScrollListState extends State<AnimatedScrollList> {
  final ScrollController _scrollController = ScrollController();
  final List<bool> _animatedItems = List.generate(20, (_) => false);

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    setState(() {
      for (int i = 0; i < _animatedItems.length; i++) {
        if (!_animatedItems[i]) {
          final renderObject = context.findRenderObject();
          if (renderObject is RenderBox) {
            // 计算item的顶部位置
            final itemTop = renderObject.localToGlobal(Offset.zero).dy + (i * 100);
            // 视口底部位置 = 屏幕高度 + 滚动偏移量
            final viewportBottom = MediaQuery.of(context).size.height + _scrollController.offset;
            // 当item顶部进入视口下方200px时触发动画
            if (itemTop < viewportBottom - 200) {
              _animatedItems[i] = true;
            }
          }
        }
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      itemCount: 20,
      itemBuilder: (context, index) {
        return AnimatedItem(
          isAnimated: _animatedItems[index],
          index: index,
        );
      },
    );
  }
}

class AnimatedItem extends StatelessWidget {
  final bool isAnimated;
  final int index;

  const AnimatedItem({
    super.key,
    required this.isAnimated,
    required this.index,
  });

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 800),
      curve: Curves.easeOut,
      // 未动画时向下偏移50px,动画完成后回到原位置
      transform: isAnimated ? Matrix4.identity() : Matrix4.translationValues(0, 50, 0),
      child: AnimatedOpacity(
        opacity: isAnimated ? 1.0 : 0.0,
        duration: const Duration(milliseconds: 800),
        curve: Curves.easeOut,
        child: Container(
          height: 100,
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          decoration: BoxDecoration(
            color: Colors.blue[100],
            borderRadius: BorderRadius.circular(8),
          ),
          child: Center(
            child: Text(
              '列表项 ${index + 1}',
              style: const TextStyle(fontSize: 18),
            ),
          ),
        ),
      ),
    );
  }
}

示例说明:

  • 通过ScrollController监听滚动事件,在_onScroll方法中判断每个列表项是否进入触发区域
  • 用AnimatedOpacity实现淡入效果,AnimatedContainer结合Matrix4实现向下滑入的位移效果
  • 用_animatedItems数组记录每个项的动画状态,避免重复触发动画
Flutter 入门学习建议
  • 先吃透官方入门教程,掌握Widget、State管理、布局系统这些核心基础
  • 重点学习动画核心类:Animation、AnimationController、AnimatedWidget、AnimatedBuilder,这些是所有Flutter动画的基础
  • 从简单动画练手:比如按钮点击缩放、页面切换淡入,再过渡到滚动触发这类依赖事件的复杂动画
  • 多研究官方示例仓库里的动画Demo,理解不同场景下的动画实现思路
  • 遇到问题优先查阅官方文档的动画章节,或官方视频教程中的动画模块

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:15:52