七爪源码:Flutter 动画:圆形页面指示器(第 1 部分)

解释

如果你正在读这篇文章,我已经喜欢你了。 这表明您对学习编码的奉献精神。

开始,


#1 按钮

圆形按钮是我们想要的第一件事。

class Btn extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {},
      child: Container(
        height: 100,
        width: 100,
        decoration: const BoxDecoration(
          color: Colors.blue,
          shape: BoxShape.circle,
        ),
        child: const Center(
          child: Icon(Icons.arrow_forward_ios, color: Colors.white),
        ),
      ),
    );
  }
}

我们将得到一个简单的按钮,例如:

在代码中,我们想将高度、宽度修改为相同的变量,并且 onTap 也应该从类外部控制,所以我们将代码修改为:

class Btn extends StatelessWidget {
  final double size;
  final void Function() onTap;
  const Btn({
    Key? key,
    this.size = 100,
    required this.onTap,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: size,
        width: size,
        decoration: const BoxDecoration(
          color: Colors.blue,
          shape: BoxShape.circle,
        ),
        child: const Center(
          child: Icon(Icons.arrow_forward_ios, color: Colors.white),
        ),
      ),
    );
  }
}


#2 电弧指示器

这个会很长

要绘制和控制弧线,我们将使用 Custom Painter。

因此,起始代码将如下所示:

class IndicatorPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}

在实现这些功能之前,我们应该有办法在屏幕上看到它。 因此,我们将按钮代码修改为:

class Btn extends StatelessWidget {
  final double size;
  final void Function() onTap;
  const Btn({
    Key? key,
    this.size = 100,
    required this.onTap,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: size + 20,
      width: size + 20,
      child: Stack(
        children: [
          SizedBox(
            height: size + 20,
            width: size + 20,
            child: CustomPaint(
              painter: IndicatorPainter(),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: onTap,
              child: Container(
                height: size,
                width: size,
                decoration: const BoxDecoration(
                  color: Colors.blue,
                  shape: BoxShape.circle,
                ),
                child: const Center(
                  child: Icon(Icons.arrow_forward_ios, color: Colors.white),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

现在,我们为弧创建一个 Paint 对象并在 canvas.drawArc() 的帮助下绘制它

class IndicatorPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke;

    canvas.drawArc(
      const Offset(0, 0) & const Size(120, 120),
      0,
      10,
      false,
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

目前该按钮将如下所示:

现在,我们根据从 0 到 1 的变量值调整弧。

当变量为 0 时,看不到圆弧;当变量为 1 时,圆弧形成一个圆。

class IndicatorPainter extends CustomPainter {
  final double value;
  IndicatorPainter({
    required this.value,
  }) : assert(value >= 0 && value <= 1);
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 3
      ..style = PaintingStyle.stroke;

    final angle = 6.2832 * value;

    canvas.drawArc(
      const Offset(0, 0) & const Size(120, 120),
      -1.5708,
      angle,
      false,
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

您可以通过将不同的值(在 1 和 0 之间)传递给 Btn() 中的 IndicatorPainter() 来测试它,例如:

IndicatorPainter(value: 0.1)
或者
IndicatorPainter(value: 0.5)
或者
IndicatorPainter(value: 0.7)

现在,我们只需使用动画控制器控制值。

为此,我们只需通过构造函数将动画控制器的值传递给按钮。

class Btn extends StatelessWidget {
  final double size;
  final double value;
  final void Function() onTap;
  const Btn({
    Key? key,
    this.size = 100,
    required this.onTap,
    required this.value,
  }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: size + 20,
      width: size + 20,
      child: Stack(
        children: [
          SizedBox(
            height: size + 20,
            width: size + 20,
            child: CustomPaint(
              painter: IndicatorPainter(value: value),
            ),
          ),
          Center(
            child: GestureDetector(
              onTap: onTap,
              child: Container(
                height: size,
                width: size,
                decoration: const BoxDecoration(
                  color: Colors.blue,
                  shape: BoxShape.circle,
                ),
                child: const Center(
                  child: Icon(Icons.arrow_forward_ios, color: Colors.white),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

现在,在我们使用这个按钮的地方,我们还必须为它定义一个动画控制器。 这可以按如下方式完成:

class _Home extends StatefulWidget {
  @override
  State<_Home> createState() => __HomeState();
}

class __HomeState extends State<_Home> with TickerProviderStateMixin {
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: AnimatedBuilder(
          animation: controller,
          builder: (c, child) => Btn(
            value: controller.value,
            onTap: () {
              controller.forward();
            },
          ),
        ),
      ),
    );
  }
}

这篇文章已经太长了。 如果您希望第 2 部分完全了解它是如何工作的,请告诉我。


关注七爪网,获取更多APP/小程序/网站源码资源!

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章