Flutter滚动动画插件flutter_scroll_animation的使用

Flutter滚动动画插件flutter_scroll_animation的使用

flutter_scroll_animation 是一个为 Flutter 提供滚动驱动动画的包。当小部件进入视口时,它们会变得可见并以自定义滑动和淡入效果进行动画展示,从而实现流畅且动态的用户界面过渡。

特性

  • 检测小部件何时进入视口,并使用可定制的滑动和淡入效果对其进行动画处理。
  • 新增了缩放、旋转和循环动画功能。
  • 可设置触发动画的可见性阈值。
  • 支持延迟动画,用于创建交错效果。
  • 高度灵活,易于集成到任何 Flutter 应用程序中。
  • 可自定义动画持续时间、方向、曲线以及退出行为。

开始使用

要使用此包,请将其添加到您的 pubspec.yaml 文件中:

dependencies:
  flutter_scroll_animation: ^0.0.1

然后运行以下命令来获取依赖项:

flutter pub get

使用示例

以下是一个简单的示例,演示如何在应用程序中使用 ScrollAnimationWidget

import 'package:flutter/material.dart';
import 'package:flutter_scroll_animation/flutter_scroll_animation.dart';

void main() {
  runApp(const MyApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Scroll Animation Example',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ExampleScreen(),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Scroll Animation Example')),
      body: ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.all(16.0),
            child: ScrollAnimationWidget(
              index: 'item_$index',
              alignment: index.isEven ? Alignment.centerRight : Alignment.centerLeft,
              child: Card(
                child: ListTile(
                  title: Text('Item $index'),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

说明

  • ScrollAnimationWidget 是该包的核心组件,用于实现滚动驱动动画。
  • 参数 index 用于唯一标识每个动画小部件。
  • alignment 控制小部件的对齐方式(如左对齐或右对齐)。
  • child 是需要动画的小部件。

自定义动画

可以通过传递参数来自定义动画的持续时间、曲线等属性。以下是一些自定义示例:

自定义持续时间和曲线

ScrollAnimationWidget(
  index: 'item_1',
  alignment: Alignment.centerLeft,
  duration: Duration(milliseconds: 700),
  curve: Curves.easeInOut,
  child: YourWidget(),
)

启用缩放和旋转动画

ScrollAnimationWidget(
  index: 'scale_icon',
  alignment: Alignment.center,
  enableScale: true, // 启用缩放动画
  enableRotation: true, // 启用旋转动画
  duration: Duration(milliseconds: 800),
  child: Icon(Icons.star, size: 50, color: Colors.amber),
)

循环动画

ScrollAnimationWidget(
  index: 'looping_text',
  alignment: Alignment.centerLeft,
  loopAnimation: true, // 循环动画
  alternate: true, // 前后交替
  duration: Duration(seconds: 2),
  child: Text('Looping Animation'),
)

延迟动画

ScrollAnimationWidget(
  index: 'delayed_button',
  alignment: Alignment.centerLeft,
  delay: Duration(milliseconds: 500), // 延迟 500 毫秒
  child: ElevatedButton(
    onPressed: () {},
    child: Text('Delayed Animation'),
  ),
)

自定义可见性阈值

ScrollAnimationWidget(
  index: 'custom_visibility',
  alignment: Alignment.centerLeft,
  visibilityThreshold: 0.5, // 当 50% 可见时触发动画
  duration: Duration(milliseconds: 600),
  child: Container(
    width: 150,
    height: 150,
    color: Colors.blue,
    child: Center(child: Text('50% Visible')),
  ),
)

高级自定义选项

该包提供了许多高级选项:

退出时反转动画

ScrollAnimationWidget(
  index: 'advanced_example',
  alignment: Alignment.center,
  reverseOnExit: true, // 动画退出时反转
  enableFade: true, // 启用淡入淡出
  resetOnScroll: true, // 滚动时重置动画
  onAnimationStart: () => print('Animation started!'), // 动画开始回调
  onAnimationEnd: () => print('Animation ended!'), // 动画结束回调
  child: Text('Advanced Scroll Animation'),
)

示例完整代码

以下是一个完整的示例代码,展示了上述所有功能:

import 'package:flutter/material.dart';
import 'package:flutter_scroll_animation/flutter_scroll_animation.dart';

void main() {
  runApp(const MyApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Scroll Animation Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ExampleScreen(),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Scroll Animation Demo')),
      body: ListView(
        physics: const AlwaysScrollableScrollPhysics(),
        children: [
          const SectionTitle(title: "Enhanced Card Animations"),
          buildEnhancedAnimatedCards(),

          const SectionTitle(title: "Scale & Rotation Animations"),
          buildScaleAndRotationIcons(),

          const SectionTitle(title: "Looping Text Animations"),
          buildLoopingText(),

          const SectionTitle(title: "Delayed Animations"),
          buildDelayedButtons(),

          const SectionTitle(title: "Custom Visibility Thresholds with Containers"),
          buildCustomVisibilityContainers(),

          // 添加底部空间以确保完全滚动
          const SizedBox(height: 400),
        ],
      ),
    );
  }

  // 展示增强的卡片动画
  Widget buildEnhancedAnimatedCards() {
    return Column(
      children: List.generate(4, (index) {
        return Padding(
          padding: const EdgeInsets.all(16.0),
          child: ScrollAnimationWidget(
            index: 'enhanced_card_$index',
            alignment: index.isEven ? Alignment.centerRight : Alignment.centerLeft,
            duration: const Duration(milliseconds: 800),
            curve: Curves.easeInOutCubic,
            reverseOnExit: true, // 退出时反转动画
            enableScale: true, // 启用缩放
            child: Card(
              elevation: 4,
              child: ListTile(
                title: Text('Enhanced Card $index'),
                subtitle: const Text('This card has enhanced animations.'),
              ),
            ),
          ),
        );
      }),
    );
  }

  // 展示缩放和旋转动画
  Widget buildScaleAndRotationIcons() {
    return Column(
      children: [
        ScrollAnimationWidget(
          index: 'scale_icon',
          alignment: Alignment.center,
          enableScale: true, // 启用缩放动画
          duration: const Duration(milliseconds: 800),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Icon(Icons.stars, size: 50, color: Colors.purple),
          ),
        ),
        ScrollAnimationWidget(
          index: 'rotation_icon',
          alignment: Alignment.center,
          enableRotation: true, // 启用旋转动画
          duration: const Duration(milliseconds: 800),
          curve: Curves.elasticInOut,
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Icon(Icons.rotate_right, size: 50, color: Colors.orange),
          ),
        ),
      ],
    );
  }

  // 展示循环动画
  Widget buildLoopingText() {
    return Column(
      children: [
        ScrollAnimationWidget(
          index: 'looping_text_1',
          alignment: Alignment.centerLeft,
          loopAnimation: true, // 循环动画
          duration: const Duration(seconds: 2),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: const Text(
              'This text loops continuously.',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
          ),
        ),
        ScrollAnimationWidget(
          index: 'looping_text_2',
          alignment: Alignment.centerRight,
          loopAnimation: true,
          alternate: true, // 前后交替
          duration: const Duration(seconds: 2),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: const Text(
              'This text alternates between forward and reverse.',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
          ),
        ),
      ],
    );
  }

  // 展示延迟动画
  Widget buildDelayedButtons() {
    return Column(
      children: [
        ScrollAnimationWidget(
          index: 'delayed_button_1',
          alignment: Alignment.centerLeft,
          duration: const Duration(milliseconds: 600),
          delay: const Duration(milliseconds: 200), // 延迟 200 毫秒
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: ElevatedButton(
              onPressed: () {},
              child: const Text('Button with 200ms delay'),
            ),
          ),
        ),
        ScrollAnimationWidget(
          index: 'delayed_button_2',
          alignment: Alignment.centerRight,
          duration: const Duration(milliseconds: 600),
          delay: const Duration(milliseconds: 500), // 延迟 500 毫秒
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: ElevatedButton(
              onPressed: () {},
              child: const Text('Button with 500ms delay'),
            ),
          ),
        ),
      ],
    );
  }

  // 展示自定义可见性阈值
  Widget buildCustomVisibilityContainers() {
    return Column(
      children: [
        ScrollAnimationWidget(
          index: 'visibility_custom_container_1',
          alignment: Alignment.centerLeft,
          visibilityThreshold: 0.5, // 当 50% 可见时触发
          duration: const Duration(milliseconds: 600),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Container(
              width: 150,
              height: 150,
              color: Colors.blue,
              child: Stack(
                children: [
                  Positioned(
                    top: 75, // 50% 高度
                    left: 0,
                    right: 0,
                    child: CustomPaint(
                      painter: DashedLinePainter(),
                      child: SizedBox(height: 1),
                    ),
                  ),
                  const Center(
                    child: Text(
                      '50% Visible',
                      style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
        ScrollAnimationWidget(
          index: 'visibility_custom_container_2',
          alignment: Alignment.centerRight,
          visibilityThreshold: 0.1, // 当 10% 可见时触发
          duration: const Duration(milliseconds: 600),
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Container(
              width: 150,
              height: 150,
              color: Colors.red,
              child: Stack(
                children: [
                  Positioned(
                    top: 15, // 10% 高度
                    left: 0,
                    right: 0,
                    child: CustomPaint(
                      painter: DashedLinePainter(),
                      child: SizedBox(height: 1),
                    ),
                  ),
                  const Center(
                    child: Text(
                      '10% Visible',
                      style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class SectionTitle extends StatelessWidget {
  final String title;

  const SectionTitle({required this.title, super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 20.0),
      child: Text(
        title,
        style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        textAlign: TextAlign.center,
      ),
    );
  }
}

class DashedLinePainter extends CustomPainter {
  final Color color;
  final double dashWidth;
  final double dashSpace;
  final Axis direction;

  DashedLinePainter({
    this.color = Colors.black,
    this.dashWidth = 5.0,
    this.dashSpace = 3.0,
    this.direction = Axis.horizontal,
  });

  [@override](/user/override)
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..strokeWidth = 2.0;

    double startX = 0.0;
    double startY = 0.0;

    if (direction == Axis.horizontal) {
      while (startX < size.width) {
        canvas.drawLine(Offset(startX, 0.0), Offset(startX + dashWidth, 0.0), paint);
        startX += dashWidth + dashSpace;
      }
    } else {
      while (startY < size.height) {
        canvas.drawLine(Offset(0.0, startY), Offset(0.0, startY + dashWidth), paint);
        startY += dashWidth + dashSpace;
      }
    }
  }

  [@override](/user/override)
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

更多关于Flutter滚动动画插件flutter_scroll_animation的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter滚动动画插件flutter_scroll_animation的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_scroll_animation 是一个用于在 Flutter 中实现滚动动画的插件。它可以帮助你在用户滚动页面时,为某些元素添加动画效果。虽然 flutter_scroll_animation 并不是 Flutter 官方提供的插件,但它提供了一种简单的方式来创建基于滚动的动画。

安装插件

首先,你需要在 pubspec.yaml 文件中添加 flutter_scroll_animation 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_scroll_animation: ^1.0.0 # 请使用最新版本

然后运行 flutter pub get 来安装插件。

使用 flutter_scroll_animation

以下是一个简单的示例,展示如何使用 flutter_scroll_animation 插件来实现基于滚动的动画。

import 'package:flutter/material.dart';
import 'package:flutter_scroll_animation/flutter_scroll_animation.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Scroll Animation Example'),
        ),
        body: ScrollAnimationExample(),
      ),
    );
  }
}

class ScrollAnimationExample extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        children: [
          Container(
            height: 200,
            color: Colors.blue,
            child: Center(
              child: ScrollAnimation(
                child: Text(
                  'Scroll Me!',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 24,
                  ),
                ),
                startAnimationOffset: 100.0,
                endAnimationOffset: 300.0,
                animationCurve: Curves.easeInOut,
                animationDuration: Duration(seconds: 1),
                builder: (context, child, animation) {
                  return Opacity(
                    opacity: animation.value,
                    child: Transform.translate(
                      offset: Offset(0.0, 50.0 * (1 - animation.value)),
                      child: child,
                    ),
                  );
                },
              ),
            ),
          ),
          Container(
            height: 1000,
            color: Colors.grey[300],
          ),
        ],
      ),
    );
  }
}
回到顶部