Flutter动画线条插件animated_lines的使用

Flutter动画线条插件animated_lines的使用

使用说明

animated_lines 插件可以绘制动画曲线和直线。它支持创建不同样式的线条,并且可以配置动画效果。

创建一个曲线线条
var curvedLine = LineInfo(
  source: Offset.zero,
  destination: const Offset(100, 100),
  backgroundLineColor: Colors.black,
  progressLineColor: Colors.lightGreen,
  lineStyle: LineStyle.curved(),
  strokeStyle: StrokeStyle.dashed(),
  animationCount: 4,
);
创建一条直线
var straightLine = LineInfo(
  source: Offset.zero,
  destination: const Offset(100, 100),
  backgroundLineColor: Colors.black,
  progressLineColor: Colors.lightGreen,
  lineStyle: LineStyle.straight(),
  strokeStyle: StrokeStyle.dashed(),
  animationCount: 4,
);
设置虚线样式
var strokeStyle = StrokeStyle.dashed(gapLength: 5, dashLength: 5);

gapLengthdashLength 都是可选参数。

设置实线样式
var strokeStyle = StrokeStyle.plain();
设置直线样式
var lineStyle = LineStyle.straight();
设置曲线样式
var lineStyle = LineStyle.curved(radius: 100, isClockwise: true);

radiusisClockwise 都是可选参数。

动画进度控制
line.animate();

如果在创建线条对象时将 animationCount 设为小于 0 的值,则表示动画将无限循环。

停止动画
line.stopAnimation();
重置动画
line.resetAnimation();
监听动画结束事件
line.onAnimationComplete = () {
  // TODO: 执行动画结束后的代码
};
更新线条信息
line.update(
  source: const Offset(2, 5),
  backgroundLineColor: Colors.blue,
  progress: 0.2,
);

完整示例

import 'dart:math';

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

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

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

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

class AnimatedLinesExample extends StatefulWidget {
  final String title;

  const AnimatedLinesExample({
    Key? key,
    required this.title,
  }) : super(key: key);

  [@override](/user/override)
  State<AnimatedLinesExample> createState() => _AnimatedLinesExampleState();
}

class _AnimatedLinesExampleState extends State<AnimatedLinesExample> with TickerProviderStateMixin {
  bool _arePointsInitialized = false;
  final List<LineInfo> _lines = [];
  late double _maxRadius;
  late double _containerWidth;
  late double _containerHeight;
  late Offset _profilePoint;
  late Offset _groceryPoint;
  late Offset _homePoint;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextButton(
                onPressed: () {
                  for (var e in _lines) {
                    e.animate();
                  }
                },
                child: const Text("Animate"),
              ),
              TextButton(
                onPressed: () {
                  for (var e in _lines) {
                    e.stopAnimation();
                  }
                },
                child: const Text("Stop Animation"),
              ),
            ],
          ),
          Expanded(
            child: LayoutBuilder(
              builder: (p0, p1) {
                _containerWidth = p1.maxWidth;
                _containerHeight = p1.maxHeight;
                _initializeObjects(_containerWidth, _containerHeight);
                return AnimatedLinesContainer(
                  lines: _lines,
                  child: !_arePointsInitialized
                      ? Container()
                      : Stack(
                          children: [
                            _profileIcon(),
                            _groceryIcon(),
                            _homeIcon(),
                          ],
                        ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _profileIcon() {
    return Positioned(
      top: _profilePoint.dy,
      left: _profilePoint.dx,
      child: Transform.translate(
        offset: const Offset(-30, -60),
        child: Image.asset(
          "assets/profile.png",
          width: 60,
        ),
      ),
    );
  }

  Widget _groceryIcon() {
    return Positioned(
      top: _groceryPoint.dy,
      left: _groceryPoint.dx,
      child: Transform.translate(
        offset: const Offset(-30, -60),
        child: Image.asset(
          "assets/grocery.png",
          width: 60,
        ),
      ),
    );
  }

  Widget _homeIcon() {
    return Positioned(
      top: _homePoint.dy,
      left: _homePoint.dx,
      child: Transform.translate(
        offset: const Offset(-30, 0),
        child: Image.asset(
          "assets/house.png",
          width: 60,
        ),
      ),
    );
  }

  void _initializeObjects(double width, double height) {
    if (!_arePointsInitialized) {
      Offset center = Offset(width / 2, height / 2);
      _maxRadius = sqrt(pow(MediaQuery.of(context).size.width, 2) + pow(MediaQuery.of(context).size.height, 2)) * 0.2;
      _profilePoint = center + Offset(-width / 4, -height / 8);
      _groceryPoint = center + Offset(width / 4, -height / 8);
      _homePoint = center + Offset(0, height / 8);
      _createLines();
      _arePointsInitialized = true;
    }
  }

  void _createLines() {
    // 创建一条虚线直线,从家到个人资料
    const straightLineBottomLeftGap = Offset(-10, 0);
    const straightLineTopLeftGap = Offset(-10, 5);
    _lines.add(LineInfo(
      source: _homePoint + straightLineBottomLeftGap,
      destination: _profilePoint + straightLineTopLeftGap,
      progressLineColor: Colors.green,
      lineStyle: LineStyle.straight(),
      strokeStyle: StrokeStyle.dashed(),
      animationCount: 4,
      onAnimationComplete: () {
        debugPrint("Animation Complete for line between Home and Profile");
      },
    ));

    // 创建一条带虚线样式的曲线,从个人资料到杂货店
    const curvedLineTopLeftGap = Offset(10, -5);
    const curvedLineTopRightGap = Offset(-10, -5);
    _lines.add(LineInfo(
      source: _profilePoint + curvedLineTopLeftGap,
      destination: _groceryPoint + curvedLineTopRightGap,
      progressLineColor: Colors.lightGreen,
      lineStyle: LineStyle.curved(radius: _maxRadius),
      strokeStyle: StrokeStyle.dashed(
        dashLength: 10,
        gapLength: 5,
      ),
    ));

    // 创建一条实线曲线,从杂货店到个人资料
    const curvedLineBottomRightGap = Offset(-10, 5);
    const curvedLineBottomLeftGap = Offset(10, 5);
    _lines.add(LineInfo(
      source: _groceryPoint + curvedLineBottomRightGap,
      destination: _profilePoint + curvedLineBottomLeftGap,
      progressLineColor: Colors.pink,
      lineStyle: LineStyle.curved(radius: _maxRadius),
      strokeStyle: StrokeStyle.plain(),
    ));

    // 创建一条实线直线,从杂货店到家
    const straightLineBottomRightGap = Offset(10, 0);
    const straightLineTopRightGap = Offset(5, 5);
    _lines.add(LineInfo(
      source: _groceryPoint + straightLineTopRightGap,
      destination: _homePoint + straightLineBottomRightGap,
      progressLineColor: Colors.lightBlue,
      lineStyle: LineStyle.straight(),
      strokeStyle: StrokeStyle.plain(),
    ));
  }
}

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

1 回复

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


当然,关于Flutter动画线条插件animated_lines的使用,下面是一个简单的代码示例,展示了如何集成和使用这个插件来创建动画线条效果。

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

dependencies:
  flutter:
    sdk: flutter
  animated_lines: ^最新版本号  # 请替换为实际最新版本号

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

接下来,你可以在你的Flutter应用中这样使用animated_lines插件:

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

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

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

class AnimatedLinesExample extends StatefulWidget {
  @override
  _AnimatedLinesExampleState createState() => _AnimatedLinesExampleState();
}

class _AnimatedLinesExampleState extends State<AnimatedLinesExample> 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) {
    return AnimatedBuilder(
      animation: _controller,
      child: Container(
        height: 200,
        width: 200,
        color: Colors.transparent,
      ),
      builder: (context, child) {
        return Stack(
          children: [
            // 背景容器
            Container(
              decoration: BoxDecoration(
                color: Colors.grey[200],
                borderRadius: BorderRadius.circular(10),
              ),
            ),
            // 动画线条
            AnimatedLines(
              lines: [
                Line(
                  start: Offset(50, 50),
                  end: Offset(150, 150),
                  color: Colors.blue,
                  strokeWidth: 2,
                  animationValue: _controller.value,
                ),
                Line(
                  start: Offset(150, 50),
                  end: Offset(50, 150),
                  color: Colors.red,
                  strokeWidth: 2,
                  animationValue: _controller.value,
                ),
              ],
            ),
          ],
        );
      },
    );
  }
}

class Line {
  final Offset start;
  final Offset end;
  final Color color;
  final double strokeWidth;
  final double animationValue;

  Line({
    required this.start,
    required this.end,
    required this.color,
    required this.strokeWidth,
    required this.animationValue,
  });
}

注意:上面的代码是一个示例,其中Line类并不是animated_lines插件的一部分,而是为了方便说明而创建的。在实际使用中,你可能需要根据animated_lines插件的具体API来调整代码。由于animated_lines插件的API可能会有所不同,请参考其官方文档来获取最新的API信息。

另外,如果animated_lines插件提供了自定义线条动画的API,你可能需要将Line类的属性映射到该API上。上面的代码主要是为了展示如何结合AnimationController和自定义动画效果。

如果animated_lines插件的API有所不同,你可能需要查阅其官方文档或示例代码来获取正确的使用方法。

回到顶部