Flutter动画关键帧插件keyframe的使用

Flutter动画关键帧插件keyframe的使用

通过将Tweens转换为带有关键帧的Timeline,可以改善Flutter中的动画体验,就像视频编辑器一样,但使用编程语言来实现。

示例代码

example/lib/main.dart

import 'dart:convert';
import 'dart:developer';

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

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

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

  // 这个小部件是你的应用的根。
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

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

    _controller.addListener(() {
      setState(() {});
    });
  }

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

  final bool _initialized = false;

  init() {
    if (_initialized) return;
    registerExtension('ext.keyframe.up', (method, parameters) async {
      return ServiceExtensionResponse.result(
        jsonEncode({
          'index': 1,
          'value': 2,
        }),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    final timeline = Timeline(
      properties: [
        KeyframeProperty<Color>(
          [
            Keyframe<Color>(0, Colors.red),
            Keyframe<Color>(0.5, Colors.green),
            Keyframe<Color>(0.7, Colors.blue),
            Keyframe<Color>(1, Colors.red),
          ],
        ),
        KeyframeProperty<Size>(
          [
            Keyframe(0, const Size(200, 200)),
            Keyframe(0.5, const Size(300, 200)),
            Keyframe(0.7, const Size(300, 300)),
            Keyframe(1.0, const Size(200, 200)),
          ],
        ),
      ],
    ).animate(_controller);

    final color = timeline.value<Color>();
    final size = timeline.value<Size>();

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              width: size.width,
              height: size.height,
              color: color,
            ),
            const Text(
              '你已经点击了按钮很多次:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.reset();
          _controller.forward();
          init();
          print(extensionStreamHasListener);
          postEvent('ext.keyframe', {
            'test': 'dsfdfd',
          });
        },
        tooltip: '增加',
        child: const Icon(Icons.add),
      ),
    );
  }
}

代码解释

  1. 导入必要的库

    import 'dart:convert';
    import 'dart:log';
    import 'package:flutter/material.dart';
    import 'package:keyframe/keyframe.dart';
    
  2. 创建主应用类

    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
  3. 创建主页状态管理类

    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key, required this.title});
    
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
  4. 初始化控制器

    class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
      late final AnimationController _controller;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          duration: const Duration(seconds: 2),
          vsync: this,
        );
    
        _controller.addListener(() {
          setState(() {});
        });
      }
    
  5. 定义关键帧属性

    final timeline = Timeline(
      properties: [
        KeyframeProperty<Color>(
          [
            Keyframe<Color>(0, Colors.red),
            Keyframe<Color>(0.5, Colors.green),
            Keyframe<Color>(0.7, Colors.blue),
            Keyframe<Color>(1, Colors.red),
          ],
        ),
        KeyframeProperty<Size>(
          [
            Keyframe(0, const Size(200, 200)),
            Keyframe(0.5, const Size(300, 200)),
            Keyframe(0.7, const Size(300, 300)),
            Keyframe(1.0, const Size(200, 200)),
          ],
        ),
      ],
    ).animate(_controller);
    
  6. 设置UI组件

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              width: size.width,
              height: size.height,
              color: color,
            ),
            const Text(
              '你已经点击了按钮很多次:',
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _controller.reset();
          _controller.forward();
          init();
          print(extensionStreamHasListener);
          postEvent('ext.keyframe', {
            'test': 'dsfdfd',
          });
        },
        tooltip: '增加',
        child: const Icon(Icons.add),
      ),
    );
    

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

1 回复

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


在 Flutter 中,keyframe 是一个用于创建复杂动画的插件,它允许你定义一系列关键帧(Keyframe),并在这些关键帧之间进行平滑的过渡。keyframe 插件可以帮助你更灵活地控制动画的细节,尤其是在需要实现复杂动画效果时。

安装 keyframe 插件

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

dependencies:
  flutter:
    sdk: flutter
  keyframe: ^1.0.0

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

使用 keyframe 插件

1. 导入 keyframe 插件

import 'package:keyframe/keyframe.dart';

2. 创建关键帧动画

你可以通过定义一系列关键帧来创建动画。每个关键帧都包含一个时间点和对应的属性值。

class MyAnimatedWidget extends StatefulWidget {
  @override
  _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}

class _MyAnimatedWidgetState extends State<MyAnimatedWidget>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  KeyframeAnimation<Color> _colorAnimation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),
    );

    _colorAnimation = KeyframeAnimation(
      controller: _controller,
      keyframes: [
        Keyframe(Color(0xFF0000FF), 0.0), // 蓝色, 开始时间 0.0
        Keyframe(Color(0xFF00FF00), 0.5), // 绿色, 开始时间 0.5
        Keyframe(Color(0xFFFF0000), 1.0), // 红色, 开始时间 1.0
      ],
    );

    _controller.repeat(reverse: true); // 循环播放动画
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (context, child) {
        return Container(
          width: 100,
          height: 100,
          color: _colorAnimation.value,
        );
      },
    );
  }
}

3. 解释代码

  • AnimationController: 用于控制动画的播放、暂停、停止等操作。
  • KeyframeAnimation: 创建一个关键帧动画,传入 AnimationController 和一系列关键帧。
  • Keyframe: 定义关键帧,包含一个值和一个时间点。时间点是相对于动画总时长的比例(0.0 到 1.0)。
  • AnimatedBuilder: 用于在动画值变化时重建 UI。

4. 运行效果

上面的代码会创建一个矩形,颜色会从蓝色变为绿色,再变为红色,然后反向循环播放。

其他用途

keyframe 插件不仅可以用颜色动画,还可以用于其他类型的动画,如位置、大小、透明度等。你只需要定义相应的关键帧和动画类型即可。

KeyframeAnimation<Offset> _positionAnimation = KeyframeAnimation(
  controller: _controller,
  keyframes: [
    Keyframe(Offset(0, 0), 0.0),
    Keyframe(Offset(100, 100), 0.5),
    Keyframe(Offset(200, 0), 1.0),
  ],
);
回到顶部