Flutter页面过渡动画插件loop_transition的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter页面过渡动画插件loop_transition的使用

loop_transition 包提供了一个名为 LoopTransition 的多功能小部件,允许你为子部件应用可重复的动画过渡。这些过渡可以循环指定次数,从而在你的用户界面中创建动态效果。

特性

  • 应用于子部件的可重复动画过渡。
  • 提供多种内置过渡函数(如淡入淡出、旋转、滑动、缩放、闪烁)。
  • 允许使用 LoopTransitionBuilder 自定义过渡。
  • 支持通过 pause 属性暂停和恢复播放。
  • 通过以下属性控制动画行为:
    • repeat: 动画循环的次数(-1 表示无限循环)。
    • pause: 是否暂停动画。
    • continuity: 控制动画在暂停时是否保持连续性。
    • mirror: 动画是否向前播放然后向后播放,形成镜像效果。
    • reverse: 控制初始动画方向(向前或向后)。
    • transition: 定义动画行为的 LoopTransitionBuilder 函数。
    • curve: 控制动画缓动的动画曲线。
    • delay: 动画开始前的延迟时间。
    • duration: 每个方向(正向和反向,如果适用)的动画持续时间。
    • backwardDelay: 向后动画开始前的延迟时间(在镜像效果中适用)。
    • backwardDuration: 向后动画的持续时间(镜像效果中适用)。
  • 在各种动画生命周期阶段触发回调:
    • onStart: 在第一次动画播放开始时调用一次。
    • onPause: 动画每次暂停时调用。
    • onContinue: 动画每次恢复时调用。
    • onCycle: 每次动画完成一个循环迭代时调用(正向和反向,如果适用)。
    • onComplete: 当所有指定循环都完成时调用一次(如果 repeat 不设置为 -1 表示无限循环)。

使用方法

要了解更多关于 loop_transition 中使用的类和其他引用,请参阅 API 文档

导入包

import 'package:loop_transition/loop_transition.dart';

创建可重复的过渡小部件

LoopTransition(
  // 重复动画循环 3 次(除了最初的循环)
  repeat: 3,

  // 开始动画
  pause: false,

  // 当 `pause` 设置为 `true` 然后 `false` 时,重置动画以继续
  continuity: false,

  // 启用镜像效果
  mirror: true,

  // 初始动画逆向播放(可选)
  reverse: true,

  // 使用内置的淡入淡出过渡动画(你可以使用自定义的 LoopTransitionBuilder 来实现更复杂的动画)
  transition: LoopTransition.fade,

  // 使用曲线来使动画平滑(可选)
  curve: Curves.easeInOut,

  // 动画开始前延迟 1 秒
  delay: const Duration(seconds: 1),

  // 设置动画持续时间为 500 毫秒,每个方向(正向和反向,如果适用)
  duration: const Duration(milliseconds: 500),

  // 设置向后动画开始前的延迟时间(可选)
  backwardDelay: const Duration(milliseconds: 200),

  // 设置向后动画的持续时间(可选)
  backwardDuration: const Duration(milliseconds: 500),

  // 动画生命周期事件的回调(可选)
  onStart: () => debugPrint('Animation Started'),
  onPause: () => debugPrint('Animation Paused'),
  onContinue: () => debugPrint('Animation Continued'),
  onCycle: (cycle) => debugPrint('Animation Cycle: $cycle'),
  onComplete: () => debugPrint('Animation Completed'),

  // 允许链式效果
  wrapper: (child, status) {
    if (status.isCompleted) {
      return LoopTransition(
        delay: const Duration(milliseconds: 300),
        duration: const Duration(milliseconds: 700),
        transition: LoopTransition.shimmer(colors: [
          Colors.black87,
          Colors.blue,
          Colors.black87,
          Colors.black87,
        ]),
        child: child,
      );
    }
    return child;
  },

  // 动画子部件
  child: const MyWidget(
    text: 'This is the widget that will be animated',
  ),
)

内置过渡

该包提供了多种内置过渡,可以直接使用:

  • LoopTransition.fade: 子部件在动画周期中淡入淡出。
  • LoopTransition.spin: 子部件围绕中心点旋转。
  • LoopTransition.slide: 子部件滑动到指定位置。
  • LoopTransition.zoom: 子部件放大缩小。
  • LoopTransition.shimmer: 子部件上创建闪烁效果。
  • LoopTransition.shakeX: 子部件水平震动。
  • LoopTransition.shakeY: 子部件垂直震动。

自定义过渡

为了获得更多控制,可以使用 LoopTransitionBuilder 定义自己的过渡函数:

final myCustomTransition = (child, animation) {
  // 实现你的自定义动画逻辑
  return Container(child: child); // 包裹子部件
};

LoopTransition(
  transition: myCustomTransition,
  child: MyWidget(),
),

示例代码

以下是完整的示例代码:

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

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Loop Transition Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Wrap(
              spacing: 20,
              children: [
                const LoopTransition(
                  curve: Curves.bounceInOut,
                  duration: Duration(milliseconds: 1500),
                  reverse: true,
                  child: FlutterLogo(size: 64),
                ),
                LoopTransition(
                  mirror: true,
                  curve: Curves.easeInOut,
                  duration: const Duration(milliseconds: 1500),
                  backwardDuration: const Duration(milliseconds: 500),
                  transition: LoopTransition.zoom(.5, 1.2),
                  child: const Icon(
                    Icons.favorite,
                    size: 64,
                    color: Colors.red,
                    shadows: [
                      Shadow(
                        blurRadius: 5.0,
                        color: Colors.red,
                        offset: Offset(0, 0),
                      ),
                    ],
                  ),
                ),
              ],
            ),
            const SizedBox(height: 20),
            const Wrap(
              spacing: 20,
              children: [
                LoopTransition(
                  duration: Duration(milliseconds: 1500),
                  transition: LoopTransition.spin,
                  child: Icon(
                    Icons.settings,
                    size: 64,
                  ),
                ),
                PausableTransition(),
                LoopTransition(
                  duration: Duration(milliseconds: 1500),
                  reverse: true,
                  transition: LoopTransition.spin,
                  child: Icon(
                    Icons.settings,
                    size: 64,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 20),
            Wrap(
              spacing: 20,
              children: [
                const LoopTransition(
                  curve: Curves.bounceOut,
                  delay: Duration(milliseconds: 1000),
                  duration: Duration(milliseconds: 700),
                  transition: LoopTransition.shakeX,
                  child: Text('Shake Horizontally'),
                ),
                LoopTransition(
                  curve: Curves.bounceOut,
                  delay: const Duration(milliseconds: 1000),
                  duration: const Duration(milliseconds: 700),
                  transition: LoopTransition.shake(
                    direction: Axis.vertical,
                    distance: 7,
                  ),
                  child: const Text('Shake Vertically'),
                ),
              ],
            ),
            const SizedBox(height: 20),
            Wrap(
              crossAxisAlignment: WrapCrossAlignment.center,
              spacing: 20,
              children: [
                Container(
                  padding: const EdgeInsets.symmetric(
                    vertical: 10,
                    horizontal: 20,
                  ),
                  decoration: BoxDecoration(
                    color: Colors.black87,
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: LoopTransition(
                    curve: Curves.linear,
                    delay: const Duration(milliseconds: 1000),
                    duration: const Duration(milliseconds: 900),
                    transition: LoopTransition.shimmer(
                      colors: [
                        Colors.white,
                        Colors.amber,
                        Colors.green,
                        Colors.white,
                        Colors.white,
                      ],
                    ),
                    child: DefaultTextStyle.merge(
                      style:
                          Theme.of(context).textTheme.headlineSmall?.copyWith(
                        fontWeight: FontWeight.bold,
                        shadows: [
                          const Shadow(
                            blurRadius: 1.0,
                            color: Colors.white,
                            offset: Offset(0, 0),
                          ),
                        ],
                      ),
                      child: IconTheme.merge(
                        data: const IconThemeData(size: 34),
                        child: Wrap(
                          crossAxisAlignment: WrapCrossAlignment.center,
                          spacing: 10,
                          children: [
                            Transform.translate(
                              offset: const Offset(0, 1.5),
                              child: const ThreeArrows(),
                            ),
                            const Text('Slide to unlock'),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
                const InteractiveThreeArrows(),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  State<PausableTransition> createState() => _PausableTransitionState();
}

class _PausableTransitionState extends State<PausableTransition> {
  bool paused = false;

  void toggle([bool? value]) {
    setState(() {
      paused = value ?? !paused;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: toggle,
      child: MouseRegion(
        onEnter: (_) => toggle(true),
        onExit: (_) => toggle(false),
        child: LoopTransition(
          pause: paused,
          repeat: 10,
          mirror: true,
          reverse: true,
          onStart: () => debugPrint('Animation Started'),
          onPause: () => debugPrint('Animation Paused'),
          onContinue: () => debugPrint('Animation Continued'),
          onCycle: (cycle) => debugPrint('Animation Cycle: $cycle'),
          onComplete: () => debugPrint('Animation Completed'),
          duration: const Duration(milliseconds: 1000),
          transition: LoopTransition.spin,
          wrapper: (child, status) {
            if (status.isCompleted) {
              return LoopTransition(
                pause: paused,
                mirror: true,
                continuity: false,
                delay: const Duration(milliseconds: 300),
                duration: const Duration(milliseconds: 700),
                backwardDuration: const Duration(milliseconds: 500),
                transition: LoopTransition.shimmer(colors: [
                  Colors.black87,
                  Colors.blue,
                  Colors.black87,
                  Colors.black87,
                ]),
                child: const Icon(
                  Icons.check,
                  size: 64,
                ),
              );
            }
            return child;
          },
          child: const Icon(
            Icons.refresh,
            size: 64,
          ),
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return LoopTransition(
      mirror: true,
      curve: Curves.easeInCubic,
      delay: const Duration(milliseconds: 300),
      duration: const Duration(milliseconds: 900),
      backwardDelay: Duration.zero,
      transition: LoopTransition.slide(const Offset(0, -.3)),
      child: LoopTransition(
        curve: Curves.linear,
        delay: const Duration(milliseconds: 1000),
        duration: const Duration(milliseconds: 900),
        transition: LoopTransition.shimmer(
          colors: [
            Colors.blue,
            Colors.white,
            Colors.blue,
            Colors.blue,
          ],
          end: Alignment.topCenter,
          begin: Alignment.bottomCenter,
          direction: AxisDirection.up,
        ),
        child: const ThreeArrows(
          direction: AxisDirection.up,
          size: 32,
        ),
      ),
    );
  }
}

class ThreeArrows extends StatelessWidget {
  const ThreeArrows({
    super.key,
    this.direction = AxisDirection.right,
    this.size,
  });

  final AxisDirection direction;

  final double? size;

  int get turns {
    switch (direction) {
      case AxisDirection.down:
        return 1;
      case AxisDirection.left:
        return 2;
      case AxisDirection.up:
        return 3;
      default:
        return 0;
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return IconTheme.merge(
      data: IconThemeData(
        size: size,
        shadows: const [
          Shadow(
            blurRadius: 1.0,
            color: Colors.white,
            offset: Offset(0, 0),
          ),
        ],
      ),
      child: RotatedBox(
        quarterTurns: turns,
        child: const Wrap(
          children: [
            Align(
              widthFactor: .3,
              child: Icon(Icons.keyboard_arrow_right),
            ),
            Align(
              widthFactor: .3,
              child: Icon(Icons.keyboard_arrow_right),
            ),
            Align(
              widthFactor: .3,
              child: Icon(Icons.keyboard_arrow_right),
            ),
          ],
        ),
      ),
    );
  }
}

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

1 回复

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


当然,以下是如何在Flutter应用中使用loop_transition插件来实现页面过渡动画的一个示例。loop_transition插件提供了一种循环过渡动画效果,可以让页面切换看起来更加生动和吸引人。

首先,确保你已经在pubspec.yaml文件中添加了loop_transition依赖:

dependencies:
  flutter:
    sdk: flutter
  loop_transition: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,我们来看一个完整的示例代码,展示如何使用loop_transition进行页面过渡动画。

main.dart

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
      routes: {
        '/second': (context) => SecondScreen(),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              LoopPageRoute(
                builder: (context) => SecondScreen(),
                transitionDuration: Duration(seconds: 1),
                reverseTransitionDuration: Duration(seconds: 1),
                loopDuration: Duration(seconds: 2),
                pattern: const [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
              ),
            );
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go Back'),
        ),
      ),
    );
  }
}

解释

  1. 依赖引入:在pubspec.yaml中添加了loop_transition依赖。
  2. 主应用MyApp是一个简单的MaterialApp,设置了首页为HomeScreen,并定义了到SecondScreen的路由。
  3. 首页HomeScreen包含一个按钮,点击按钮时,使用Navigator.push方法通过LoopPageRoute导航到SecondScreen
  4. LoopPageRoute
    • builder:构建目标页面(这里是SecondScreen)。
    • transitionDuration:正向过渡动画的持续时间。
    • reverseTransitionDuration:反向过渡动画的持续时间。
    • loopDuration:循环动画的总持续时间。
    • pattern:定义了动画的关键帧,这些值决定了动画在不同时间点的进度。

通过上述代码,当你从HomeScreen导航到SecondScreen时,会看到一个循环过渡动画效果。当你点击返回按钮时,也会看到相应的反向过渡动画。

你可以根据需要调整transitionDurationreverseTransitionDurationloopDurationpattern的值,以达到你想要的动画效果。

回到顶部