flutter lottie animation progress bar 美丽多彩生动的动画进度条
flutter支持使用lottie动画,有了lottie,我们也可以在flutter中实现很多炫酷的动画效果,这次为我们就来学习使用lottie动画,实现一个显示系统内存使用状态的动画进度条。本文将涉及到lottie动画播放控制、Isoalte多线程、系统信息获取等知识。
Lottie
Lottie 是 Airbnb 开源的一套跨平台的完整的动画效果解决方案,是一种基于 JSON 的动画文件格式,设计师可以使用 Adobe After Effects 设计出漂亮的动画并导出成 JSON 格式,允许您在任何平台上发布动画,就像交付静态资产一样容易。它们是在任何设备上都可以使用的小文件。LottieFiles 允许您以最简单的方式创建、编辑、测试、协作和交付 Lottie。
文件大小
与其他格式(如 GIF 或 MP4)相比,Lottie 动画要小得多,同时保持相同的质量。
无限可扩展
Lottie 动画基于矢量,这意味着您可以放大和缩小它们,而不必担心分辨率。
多平台支持和库
对于所有开发人员来说,Lottie 交接非常容易。您可以在iOS,Android,Web和React Native上使用Lottie动画,而无需修改。
互动
在 Lottie 动画中,动画元素是公开的,因此您可以操作它们以使其具有交互性并响应滚动、单击和悬停等交互。
Lottie for Flutter
Lottie for Flutter适用于Android,iOS,macOS,linux,Windows和Web。插件地址:github.com/xvrh/lottie...。在flutter中使用lottie也很简单,可以使用Lottie.asset设置本地资源,也可以使用Lottie.network设置网络lottie资源。如下代码所示:
scala
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [
// Load a Lottie file from your assets
Lottie.asset('assets/LottieLogo1.json'),
// Load a Lottie file from a remote url
Lottie.network(
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
// Load an animation and its images from a zip file
Lottie.asset('assets/lottiefiles/angel.zip'),
],
),
),
);
}
}
lottie animation progress bar 动画进度条
lottie动画文件下载使用
接下来我们来实现一个动画进度条来显示内存使用情况。首先到lottiefiles.com/上找一些免费的lottie进度条动画文件下载下来使用。放到我们的本地文件夹中,还需要在pubspec.yaml文件中对本地资源进行设置。
yaml
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
接着使用 Lottie.asset()导入本地lottie动画资源, 导入本地动画资源时还可以通过 width、height属性设置要显示的动画的大小。使用Column组件放在外层,这样我们就可以将四个lottie动画文件竖着展示出来。结合使用Padding和Center组件来调整动画显示的位置,让他看起来顺眼一些。
scala
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
const Padding(padding: EdgeInsets.all(20)),
Lottie.asset(
'assets/78495-progress.json',
width: 150,
height: 150,
),
Lottie.asset(
'assets/88697-neomorphism-radial-progress-bar-style-2-light-theme-01.json',
width: 150,
height: 150,
),
Lottie.asset(
'assets/91938-green-progress-bar-without-popover.json',
width: 200,
height: 150,
),
Lottie.asset(
'assets/93125-progress-bar.json',
width: 300,
height: 100,
),
],
),
),
);
}
}
效果图:
lottie动画播放控制
实际使用的环境会很复杂,我们需要控制动画的播放状态,lottie可以使用AnimationController控制动画的播放。首先定义late final AnimationController _controller;
动画控制器,然后在asset中指定我们声明的控制器,这样我们就可以在外部使用_controller控制动画的播放状态。
less
Lottie.asset(
'assets/78495-progress.json',
width: 150,
height: 150,
controller: _controller,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
为了控制动画播放,我们需要添加一些按钮,点击按钮时对动画的播放状态进行控制,使用Row组件横向包裹三个TextButton按钮,分别控制动画播放状态的前进、停止、后退,设置 mainAxisAlignment: MainAxisAlignment.center,
居中显示按钮。只要使用_controller.forward();
、_controller.stop();
、 _controller.reverse();
按个控制器方法就可以分别控制动画的前进、停止、后退。
less
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
_controller.forward();
},
child: Text("前进"),
),
TextButton(
onPressed: () {
_controller.stop();
},
child: Text("暂停"),
),
TextButton(
onPressed: () {
_controller.reverse();
},
child: Text("后退"),
),
],
)
效果图:
内存使用状态展示
我们使用system_info2插件来读取内存使用状态,本来想同时显示CPU使用率,但是找了半天没找到Windows下读取CPU使用率的插件,还是很遗憾的。因为使用system_info2读取内存比较耗费时间,导致我们的UI会有卡顿,所以我要新建一个Isolate来读取我们的内存状态,在新isolate中设置定时器,定时读取内存状态,计算出内存使用率,并将结果发回到UI Isoalte,通知UI进行页面更新。在UI Isolate中 使用receivePort.listen
监听新isolate发回来的结果,利用此结果控制动画的播放。_controller.animateTo
和_controller.animateBack
可以控制动画正向或者逆向播放到某一帧,这正是我所需要的。
ini
void makenewisolate() {
ReceivePort receivePort = ReceivePort();
Isolate.spawn(newisolate, receivePort.sendPort);
receivePort.listen((message) {
if (message > _controller.value) {
_controller.animateTo(message);
}
if (message < _controller.value) {
_controller.animateBack(message);
}
});
}
void newisolate(SendPort sendPort) {
var timer = Timer.periodic(const Duration(seconds: 2), (timer) {
const int megaByte = 1024 * 1024;
var last = 1 -
((SysInfo.getFreePhysicalMemory() ~/ megaByte) /
(SysInfo.getTotalPhysicalMemory() ~/ megaByte));
sendPort.send(last);
});
}
效果图:
可以看到,我们的进度条会随着内存的使用情况进行变化,反应出内存的使用情况。图中四个进度条只有后两个比较如实的反应内存使用情况,前两个动画显示的百分比不是很正确,这是由于前两个动画不是从0%开始的,都有一个前摇,导致百分比无法完全对应,后续可以针对问题再进行优化。
总结
这次简单的学习了lottie动画在flutetr中的使用、控制方法,效果还是很不错的。同时还涉及到了flutter多线程Isolate的使用。后续让我们一起继续深入学习吧。
完整代码:
less
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:system_info2/system_info2.dart';
import 'package:lottie/lottie.dart';
import 'dart:isolate';
late final AnimationController _controller;
void main() async {
makenewisolate();
runApp(const Homepage());
}
void makenewisolate() {
ReceivePort receivePort = ReceivePort();
Isolate.spawn(newisolate, receivePort.sendPort);
receivePort.listen((message) {
if (message > _controller.value) {
_controller.animateTo(message);
}
if (message < _controller.value) {
_controller.animateBack(message);
}
});
}
void newisolate(SendPort sendPort) {
var timer = Timer.periodic(const Duration(seconds: 2), (timer) {
const int megaByte = 1024 * 1024;
var last = 1 -
((SysInfo.getFreePhysicalMemory() ~/ megaByte) /
(SysInfo.getTotalPhysicalMemory() ~/ megaByte));
sendPort.send(last);
});
}
class Homepage extends StatelessWidget {
const Homepage({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
useMaterial3: true,
),
home: const Scaffold(
body: MyApp(),
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
const Padding(padding: EdgeInsets.all(20)),
Lottie.asset(
'assets/78495-progress.json',
width: 150,
height: 150,
controller: _controller,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
Lottie.asset(
'assets/88697-neomorphism-radial-progress-bar-style-2-light-theme-01.json',
width: 150,
height: 150,
controller: _controller,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
Lottie.asset(
'assets/91938-green-progress-bar-without-popover.json',
width: 200,
height: 150,
controller: _controller,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
Lottie.asset(
'assets/93125-progress-bar.json',
width: 300,
height: 100,
controller: _controller,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
_controller.forward();
},
child: Text("前进"),
),
TextButton(
onPressed: () {
_controller.stop();
},
child: Text("暂停"),
),
TextButton(
onPressed: () {
_controller.reverse();
},
child: Text("后退"),
),
],
)
],
),
),
);
}
}