如何仅使用 flutter 制作自定义加载小部件
How to make custom loading widget only using flutter
我正在尝试制作一个像这样的自定义动画进度指示器
但我不知道从哪里开始..
感谢帮助谢谢!
编辑:仅在可能的情况下使用颤振
我不知道你可以控制 lotti 资产和包。
https://pub.dev/packages/lottie
使用 lotti 资产,您可以制作如下所示的加载指示器页面。
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:lottie/lottie.dart';
class LoadingIndicatorPage extends StatelessWidget {
final Color backgroundColor;
LoadingIndicatorPage({this.backgroundColor});
@override
Widget build(BuildContext context) => Container(
color: backgroundColor ?? Colors.transparent,
child: Center(
child: Lottie.asset(
'assets/loading_indicator3.zip',
width: ScreenUtil().setWidth(64),
height: ScreenUtil().setWidth(64),
),
),
);
}
您可以像这样使用 ClipPath 和 CustomPaint:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
controller.addStatusListener(statusListener);
controller.forward();
}
statusListener(AnimationStatus status) {
if (status == AnimationStatus.completed) {
controller.value = 0.0;
controller.forward();
}
}
@override
void dispose() {
controller.removeStatusListener(statusListener);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Center(
child: AnimatedBuilder(
animation: controller.view,
builder: (context, snapshot) {
return ClipPath(
clipper: MyClipper(),
child: Container(
width: 100,
height: 100,
color: Colors.white,
child: CustomPaint(
painter: MyCustomPainter(controller.value),
),
),
);
}),
),
)),
);
}
}
class MyClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
double w = size.width;
double h = size.height;
Path path = Path();
path.moveTo(w / 2, 0);
path.lineTo(w / 2 - 20, 55);
path.lineTo(w / 2, 55);
path.lineTo(w / 2 - 20, h);
path.lineTo(w / 2 + 20, 45);
path.lineTo(w / 2, 45);
path.lineTo(w / 2 + 20, 0);
path.close();
return path;
}
@override
bool shouldReclip(covariant CustomClipper oldClipper) {
return false;
}
}
class MyCustomPainter extends CustomPainter {
final double percentage;
MyCustomPainter(this.percentage);
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.amber
..style = PaintingStyle.fill;
Rect rect = Rect.fromLTWH(0, 0, size.width, size.height * percentage);
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
我正在尝试制作一个像这样的自定义动画进度指示器
但我不知道从哪里开始..
感谢帮助谢谢!
编辑:仅在可能的情况下使用颤振
我不知道你可以控制 lotti 资产和包。
https://pub.dev/packages/lottie
使用 lotti 资产,您可以制作如下所示的加载指示器页面。
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:lottie/lottie.dart';
class LoadingIndicatorPage extends StatelessWidget {
final Color backgroundColor;
LoadingIndicatorPage({this.backgroundColor});
@override
Widget build(BuildContext context) => Container(
color: backgroundColor ?? Colors.transparent,
child: Center(
child: Lottie.asset(
'assets/loading_indicator3.zip',
width: ScreenUtil().setWidth(64),
height: ScreenUtil().setWidth(64),
),
),
);
}
您可以像这样使用 ClipPath 和 CustomPaint:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
controller.addStatusListener(statusListener);
controller.forward();
}
statusListener(AnimationStatus status) {
if (status == AnimationStatus.completed) {
controller.value = 0.0;
controller.forward();
}
}
@override
void dispose() {
controller.removeStatusListener(statusListener);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Center(
child: AnimatedBuilder(
animation: controller.view,
builder: (context, snapshot) {
return ClipPath(
clipper: MyClipper(),
child: Container(
width: 100,
height: 100,
color: Colors.white,
child: CustomPaint(
painter: MyCustomPainter(controller.value),
),
),
);
}),
),
)),
);
}
}
class MyClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
double w = size.width;
double h = size.height;
Path path = Path();
path.moveTo(w / 2, 0);
path.lineTo(w / 2 - 20, 55);
path.lineTo(w / 2, 55);
path.lineTo(w / 2 - 20, h);
path.lineTo(w / 2 + 20, 45);
path.lineTo(w / 2, 45);
path.lineTo(w / 2 + 20, 0);
path.close();
return path;
}
@override
bool shouldReclip(covariant CustomClipper oldClipper) {
return false;
}
}
class MyCustomPainter extends CustomPainter {
final double percentage;
MyCustomPainter(this.percentage);
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.amber
..style = PaintingStyle.fill;
Rect rect = Rect.fromLTWH(0, 0, size.width, size.height * percentage);
canvas.drawRect(rect, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}