如何仅使用 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;
  }
}