Flutter动画路径插件simple_animated_path的使用

Flutter动画路径插件simple_animated_path的使用

simple_animated_path 是一个允许你使用自定义路径进行动画制作的 Flutter 包。

示例

如何使用

步骤 1: 创建路径

首先,创建一个 Path 对象并配置它。你可以使用 Dart 的 Path 类来定义路径。

final path = Path();
path.moveTo(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
path.close(); // 形成一个矩形

步骤 2: 创建画笔

接下来,创建一个 Paint 对象并配置它。Paint 对象用于定义路径的颜色、样式等属性。

final paint = Paint();
paint.color = Colors.blue; // 设置颜色为蓝色
paint.style = PaintingStyle.fill; // 填充模式

步骤 3: 创建动画

然后,创建一个动画控制器 (AnimationController) 和一个 Tween 对象来控制动画的进度。

final _animTimeMillis = 1000; // 动画时长为1秒
late AnimationController _animCtrl;
late Animation<double> _anim;

[@override](/user/override)
void initState() {
  super.initState();
  _animCtrl = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: _animTimeMillis),
  );
  _anim = Tween<double>(begin: 0, end: 1).animate(
    CurvedAnimation(
      parent: _animCtrl,
      curve: Curves.linear,
    ),
  );
}

步骤 4: 使用 SimpleAnimatedPathWidget

最后,将这些配置添加到 SimpleAnimatedPathWidget 中。

return Scaffold(
  appBar: AppBar(
    title: Text('Example'),
  ),
  body: Center(
    child: SimpleAnimatedPathWidget(
      animation: _anim,
      size: Size(200, 200),
      configurations: [
        PathConfiguration(
          paint: paint,
          path: path,
        ),
      ],
    ),
  ),
);

完整示例

以下是一个完整的示例,展示了如何在 Flutter 应用程序中使用 simple_animated_path 插件。

import 'dart:math';

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  final _animTimeMillis = 1000;
  late AnimationController _animCtrl;
  late Animation<double> _anim;

  [@override](/user/override)
  void initState() {
    super.initState();
    _animCtrl = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: _animTimeMillis),
    );
    _anim = Tween<double>(begin: 0, end: 1).animate(
      CurvedAnimation(
        parent: _animCtrl,
        curve: Curves.linear,
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    _animCtrl.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    const padding = 10;
    const defaultItemWidth = 200.0;
    final mqSize = MediaQuery.of(context).size;
    final itemSize = (min((mqSize.width / 2 - padding * 3), defaultItemWidth));

    final step = itemSize / 7;

    return Scaffold(
      appBar: AppBar(
        title: Text('Example'),
      ),
      body: Align(
        alignment: Alignment.topCenter,
        child: SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(
              maxWidth: itemSize * 2 + padding * 3,
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                const SizedBox(height: 10),
                Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    TextButton(
                      onPressed: () {
                        _animCtrl.forward();
                      },
                      child: Text('Forward'),
                    ),
                    const SizedBox(width: 10),
                    TextButton(
                      onPressed: () {
                        _animCtrl.reverse();
                      },
                      child: Text('Reverse'),
                    ),
                  ],
                ),
                const SizedBox(height: 10),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Container(
                      width: itemSize,
                      height: itemSize,
                      color: Colors.blue,
                      child: SimpleAnimatedPathWidget(
                        animation: _anim,
                        size: Size(itemSize, itemSize),
                        configurations: [
                          ...List.generate(
                            7,
                            (index) => PathConfiguration(
                              paint: Paint()
                                ..color = Colors.white
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 1
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: _circlePath(
                                Size(
                                  itemSize - (step * index),
                                  itemSize - (step * index),
                                ),
                              ),
                            ),
                          ),
                          ...[
                            PathConfiguration(
                              paint: Paint()
                                ..color = Colors.white
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 2
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: Path()
                                ..moveTo(0, 0)
                                ..lineTo(itemSize, itemSize),
                            ),
                          ]
                        ],
                      ),
                    ),
                    Container(
                      width: itemSize,
                      height: itemSize,
                      color: Colors.amber,
                      child: SimpleAnimatedPathWidget(
                        animation: _anim,
                        size: Size(itemSize, itemSize),
                        configurations: [
                          ...List.generate(
                            7,
                            (index) => PathConfiguration(
                              paint: Paint()
                                ..color = Colors.white
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 1
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: _squarePath(Size(itemSize - (step * index),
                                  itemSize - (step * index))),
                            ),
                          ),
                          ...[
                            PathConfiguration(
                              paint: Paint()
                                ..color = Colors.white
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 2
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: Path()
                                ..moveTo(0, 0)
                                ..lineTo(itemSize, itemSize),
                            ),
                          ]
                        ],
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 10),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Container(
                      width: itemSize,
                      height: itemSize,
                      color: Colors.indigo,
                      child: SimpleAnimatedPathWidget(
                        animation: _anim,
                        size: Size(itemSize, itemSize),
                        configurations: [
                          ...List.generate(
                            7,
                            (index) => PathConfiguration(
                              paint: Paint()
                                ..color = Colors.deepOrangeAccent
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 2
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: _trianglePath(
                                  Size(itemSize - (step * index),
                                      itemSize - (step * index)),
                                  itemSize),
                            ),
                          ),
                          ...[
                            PathConfiguration(
                              paint: Paint()
                                ..color = Colors.white
                                ..style = PaintingStyle.stroke
                                ..strokeWidth = 2
                                ..isAntiAlias = true
                                ..strokeCap = StrokeCap.round,
                              path: Path()
                                ..moveTo(itemSize / 2, 0)
                                ..lineTo(itemSize / 2, itemSize),
                            ),
                          ]
                        ],
                      ),
                    ),
                    Container(
                      width: itemSize,
                      height: itemSize,
                      color: Colors.black12,
                      child: SimpleAnimatedPathWidget(
                        animation: _anim,
                        size: Size(itemSize, itemSize),
                        configurations: [
                          PathConfiguration(
                            paint: Paint()
                              ..color = Colors.green
                              ..style = PaintingStyle.stroke
                              ..strokeWidth = 14
                              ..isAntiAlias = true
                              ..strokeCap = StrokeCap.round,
                            path: _checkBoxPath(itemSize),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 10),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Path _checkBoxPath(double itemSize) {
    return Path()
      ..moveTo(0.27083 * itemSize, 0.54167 * itemSize)
      ..lineTo(0.41667 * itemSize, 0.68750 * itemSize)
      ..lineTo(0.75000 * itemSize, 0.35417 * itemSize);
  }

  Path _circlePath(Size size) {
    final dimSize = min(size.width, size.height);
    final r = Radius.circular(max(dimSize / 2, dimSize / 2));
    final w = dimSize;
    final h = dimSize;
    return Path()
      ..moveTo(0, h / 2)
      ..arcToPoint(
        Offset(w, h / 2),
        radius: r,
      )
      ..arcToPoint(
        Offset(0, h / 2),
        radius: r,
      );
  }

  Path _squarePath(Size size) {
    const start = 2.0;
    final dimSize = min(size.width, size.height);
    final w = dimSize - start;
    final h = dimSize - start;
    return Path()
      ..moveTo(start, start)
      ..lineTo(start, w)
      ..lineTo(h, w)
      ..lineTo(h, start)
      ..lineTo(start, start);
  }

  Path _trianglePath(Size size, double panW) {
    const startY = 2.0;
    final dimSize = min(size.width, size.height);
    final h = dimSize;
    final startX = panW / 2;
    return Path()
      ..moveTo(startX, startY)
      ..lineTo(panW - startY, h)
      ..lineTo(startY, h)
      ..lineTo(startX, startY);
  }
}

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

1 回复

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


当然,以下是如何在Flutter中使用simple_animated_path插件来实现动画路径的一个示例代码。simple_animated_path插件允许你沿着自定义路径移动小部件,非常适合创建复杂的动画效果。

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

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

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

接下来,你可以创建一个Flutter应用,并在其中使用SimpleAnimatedPath来实现动画路径。以下是一个完整的示例代码:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Simple Animated Path Example'),
        ),
        body: Center(
          child: AnimatedPathExample(),
        ),
      ),
    );
  }
}

class AnimatedPathExample extends StatefulWidget {
  @override
  _AnimatedPathExampleState createState() => _AnimatedPathExampleState();
}

class _AnimatedPathExampleState extends State<AnimatedPathExample> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

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

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

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    final Path path = Path()
      ..moveTo(size.width * 0.1, size.height * 0.5)
      ..quadraticBezierTo(
        size.width * 0.5, size.height * 0.1,
        size.width * 0.9, size.height * 0.5,
      )
      ..quadraticBezierTo(
        size.width * 0.5, size.height * 0.9,
        size.width * 0.1, size.height * 0.5,
      );

    return SimpleAnimatedPath(
      path: path,
      animatedAlign: 0.5, // 动画对象沿路径的对齐方式(0.0到1.0)
      size: 50.0, // 动画对象的大小
      duration: _controller.duration,
      animation: _controller,
      child: Container(
        decoration: BoxDecoration(
          color: Colors.blue,
          shape: BoxShape.circle,
        ),
      ),
    );
  }
}

在这个示例中:

  1. 我们创建了一个AnimatedPathExample小部件,它包含一个AnimationController来控制动画的时长和循环。
  2. initState方法中,我们初始化了AnimationController并设置其持续时间为2秒,同时让它不断反向重复。
  3. 我们定义了一个Path对象,该对象描述了一个从屏幕左上角到右下角再回到左上角的二次贝塞尔曲线。
  4. SimpleAnimatedPath小部件接受这个路径、动画控制器以及其他一些参数(如对象的大小和对齐方式),并在路径上移动一个蓝色的圆形容器。

运行这个示例代码,你将看到一个蓝色的圆沿着定义的路径来回移动。你可以根据需要调整路径的形状、动画的时长和其他参数,以实现不同的动画效果。

回到顶部