Flutter动画调试插件animation_debugger的使用

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

Flutter动画调试插件 animation_debugger 的使用

animation_debugger 是一个强大的工具,可以帮助开发者在Flutter应用中调试动画。通过这个插件,你可以暂停、快进、倒放或反向播放动画。本文将详细介绍如何使用这个插件,并提供一个完整的示例Demo。

功能简介

Animation Debugger 允许你通过以下方式控制动画:

  • 暂停
  • 快进
  • 倒放
  • 反向播放

只需简单地将现有的 AnimationController 包装在一个调用中即可实现这些功能:

//                                                   ⬇︎ Your old controller ⬇︎
final controller = AnimationDebugger.of(context).watch(AnimationController());

使用步骤

第一步:在应用根部使用 builder 包装器

你需要在应用的根部(通常是 MaterialAppCupertinoApp)使用内置的 builder 包装器。

示例1:直接使用内置 builder

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: Colors.blueAccent,
      ),
      title: 'Animation Debugger',
      home: const MyHomePage(),
      builder: AnimationDebugger.builder, // ⬅︎ Use built-in builder
    );
  }
}

示例2:如果你已经在使用其他 builder,可以使用 builderWrapper

例如,这里我们使用了 bot_toast 库中的 builder

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: Colors.blueAccent,
      ),
      title: 'Animation Debugger',
      home: const MyHomePage(),
      builder: AnimationDebugger.builderWrapper(BotToastInit()), // ⬅︎ Use built-in builder-factory
    );
  }
}

第二步:包装你的 AnimationController

为了让 Animation Debugger 能够识别并控制你的动画控制器,你需要将它们包裹在 watch 方法中:

final AnimationController controller = AnimationDebugger.of(context).watch(AnimationController(debugLabel: 'some name'));
// 或者
final AnimationController controller2 = AnimationDebugger.of(context).watch(AnimationController(), label: 'just name');

为了便于区分不同的控制器,建议为每个控制器设置一个唯一的 debugLabel

调试

安装并配置好 animation_debugger 后,你会看到一个浮动小部件出现在应用屏幕顶部。点击它会弹出一个列表,显示所有可管理的控制器。

Mobile Desktop

注意:此插件仅在调试模式下生效,在发布模式下不会嵌入到应用中。

限制

当前版本仅支持具有明确时间范围的“有界”控制器。加速/延迟播放以及动态更改播放时间的功能尚未实现。

完整示例 Demo

下面是一个完整的示例代码,展示了如何在实际项目中使用 animation_debugger

import 'dart:async';
import 'dart:math';

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

const backgroundEndColor = Color.fromRGBO(1, 103, 213, 1);
const backgroundStartColor = Color.fromRGBO(43, 88, 153, 1);
const inkColor = Colors.white;
const borderWidth = 3.0;
const gap = 15.0;
const bottomGap = gap * 5;

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: Colors.blueAccent,
      ),
      title: 'Animation Debugger',
      home: const MyHomePage(),
      builder: AnimationDebugger.builder, // ⬅︎ Use built-in builder
    );
  }
}

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

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  Curve get curve => Curves.linear;
  Duration get duration => const Duration(seconds: 120);

  final List<String> spaceObjects = [
    'sun',
    'mercury',
    'venus',
    'earth',
    'mars',
    'jupiter',
    'saturn',
    'uranus',
    'neptune',
    'pluto',
  ];

  final List<int> daysInTheYear = [25, 88, 225, 365, 687, 4380, 10950, 30660, 60225, 90520];

  List<double> get distances => [1, 100, 170, 260, 380, 530, 700, 860, 1040, 1200];
  final List<AnimationController> controllers = [];

  void initSpaceObjects() {
    for (int i = 0; i < spaceObjects.length; i++) {
      final String objectName = spaceObjects[i];
      final int daysInTheYear = this.daysInTheYear[i];

      final AnimationController controller = AnimationDebugger.of(context).watch(
        AnimationController(
          vsync: this,
          debugLabel: objectName,
          duration: Duration(seconds: daysInTheYear),
        ),
      );
      controllers.add(controller);
      unawaited(controller.forward());
    }
  }

  Widget buildPaper() {
    return Positioned(
      left: gap,
      top: gap,
      right: gap,
      bottom: bottomGap,
      child: GridPaper(
        color: Colors.white.withOpacity(0.5),
        child: DecoratedBox(
          decoration: BoxDecoration(
            border: Border.all(color: inkColor, width: borderWidth),
          ),
        ),
      ),
    );
  }

  List<Widget> buildObjects() {
    final List<Widget> result = [];
    final Size size = MediaQuery.of(context).size;
    final double width = size.width - gap * 2 - borderWidth * 2;
    final double height = size.height - bottomGap - gap - borderWidth * 2;

    for (int i = 0; i < spaceObjects.length; i++) {
      final String name = spaceObjects[i];
      final AnimationController controller = controllers[i];
      final double distance = distances[i];
      final double fixMover = i == 6 || i == 7 ? -20 : -10;

      final Widget spaceObject = Image.asset('assets/$name.png', height: i == 0 ? 50 : 30);

      result.add(
        AnimatedBuilder(
          animation: controller,
          builder: (BuildContext context, Widget? child) {
            final double value = controller.value;
            final double rads = value * 360 / 2 / pi;
            const double multiplier = 0.65;
            final double x = cos(rads) * distance * multiplier;
            final double y = sin(rads) * distance * multiplier;

            if (i == 0) {
              return Positioned(
                left: width / 2,
                bottom: height / 2 * 1.2,
                child: Transform.translate(
                  offset: Offset(fixMover, -5),
                  child: Transform.rotate(angle: rads, child: child!),
                ),
              );
            }

            return Positioned(
              left: (width / 2) + x,
              bottom: (height / 2 * 1.2) + y,
              child: Transform.translate(
                offset: Offset(fixMover, -5),
                child: child!,
              ),
            );
          },
          child: spaceObject,
        ),
      );
    }
    return result;
  }

  [@override](/user/override)
  void dispose() {
    for (final AnimationController controller in controllers) {
      controller.dispose();
    }
    super.dispose();
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    initSpaceObjects();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          colors: [backgroundStartColor, backgroundEndColor],
          begin: Alignment.bottomLeft,
          end: Alignment.topRight,
          stops: [0, 1],
        ),
      ),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: Stack(
          fit: StackFit.expand,
          children: [
            /// ? GRID
            buildPaper(),

            ...buildObjects(),
          ],
        ),
      ),
    );
  }
}

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

1 回复

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


当然,animation_debugger 是一个非常有用的 Flutter 插件,它可以帮助开发者在开发过程中调试和可视化动画。以下是如何在 Flutter 项目中使用 animation_debugger 的一个简单示例代码案例。

步骤 1: 添加依赖

首先,在你的 pubspec.yaml 文件中添加 animation_debugger 依赖:

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

然后运行 flutter pub get 来获取依赖。

步骤 2: 导入插件

在你的 Dart 文件中导入 animation_debugger

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

步骤 3: 使用 AnimationController 和 AnimationBuilder

创建一个简单的动画并使用 AnimationDebugger 来包裹你的动画组件。以下是一个完整的示例:

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Animation Debugger Example'),
        ),
        body: AnimationDebugger(
          // 启用调试模式
          enabled: true,
          child: AnimatedWidgetExample(),
        ),
      ),
    );
  }
}

class AnimatedWidgetExample extends StatefulWidget {
  @override
  _AnimatedWidgetExampleState createState() => _AnimatedWidgetExampleState();
}

class _AnimatedWidgetExampleState extends State<AnimatedWidgetExample> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..repeat(reverse: true);

    _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
  }

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: _animation,
        child: Container(
          width: 100,
          height: 100,
          color: Colors.blue,
        ),
        builder: (context, child) {
          return Transform.scale(
            scale: _animation.value,
            child: child,
          );
        },
      ),
    );
  }
}

解释

  1. 添加依赖:在 pubspec.yaml 文件中添加 animation_debugger 依赖。
  2. 导入插件:在你的 Dart 文件中导入 animation_debugger
  3. 使用 AnimationController:创建一个 AnimationController 并配置动画时长和反向重复。
  4. 使用 AnimationBuilder:使用 AnimatedBuilder 来构建动画组件。
  5. 包裹 AnimationDebugger:用 AnimationDebugger 包裹你的动画组件,并设置 enabled: true 来启用调试模式。

调试

运行你的 Flutter 应用,你将看到动画在运行,并且 AnimationDebugger 会在动画运行时提供可视化调试信息。

注意:animation_debugger 插件的具体使用方法和界面可能会随着版本更新而变化,请参考官方文档以获取最新的使用指南。

回到顶部