首页 > 解决方案 > 每次可侦听值更改时,动画小部件似乎都不会调用 build

问题描述

我正在制作一个自定义倒计时圆形指示器。

这是代码。

import 'dart:math';

import 'package:audioplayers/audio_cache.dart';
import 'package:flutter/material.dart';
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hive/hive.dart';
import 'package:jailhouseworkout/consts.dart';
import 'package:jailhouseworkout/prefs.dart';
import 'package:jailhouseworkout/providers/juarez_provider.dart';
import 'package:provider/provider.dart';

class JuarezScreen extends StatefulWidget {
  @override
  _JuarezScreenState createState() => _JuarezScreenState();
}

class _JuarezScreenState extends State<JuarezScreen>
    with SingleTickerProviderStateMixin {
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    final staticJuarez = Provider.of<JuarezProvider>(context, listen: false);
    return WillPopScope(
      onWillPop: () {
        if (staticJuarez.isResting) {
          staticJuarez.timer.cancel();
          controller.stop();
        }
        return (Future(() => true));
      },
      child: SafeArea(
        child: Material(
          color: kMainColor,
          child: Column(
            children: <Widget>[
              Spacer(),
              Row(
                children: <Widget>[
                  Spacer(),
                  NeumorphicButton(
                    onPressed: () {
                      if (staticJuarez.isResting) {
                        staticJuarez.timer.cancel();
                        controller.stop();
                      }
                      Navigator.of(context).pop();
                    },
                    boxShape: NeumorphicBoxShape.circle(),
                    child: Icon(Icons.arrow_back, color: kAccentColor),
                    style: NeumorphicStyle(color: kMainColor),
                  ),
                  Spacer(),
                  Text(
                    "Juarez Valley",
                    style: TextStyle(fontSize: 25, color: kAccentColor),
                  ),
                  Spacer(),
                  NeumorphicButton(
                    boxShape: NeumorphicBoxShape.circle(),
                    child: Icon(FontAwesomeIcons.cog, color: kAccentColor),
                    style: NeumorphicStyle(color: kMainColor),
                  ),
                  Spacer(),
                ],
              ),
              Spacer(),
              SizedBox(
                width: MediaQuery.of(context).size.width * 0.8,
                height: MediaQuery.of(context).size.width * 0.8,
                child: Stack(
                  children: [
                    Neumorphic(
                      padding: EdgeInsets.all(12),
                      boxShape: NeumorphicBoxShape.circle(),
                      style: NeumorphicStyle(
                        depth: -3,
                        intensity: 1,
                        shape: NeumorphicShape.concave,
                      ),
                      child: Neumorphic(
                        padding: EdgeInsets.all(40),
                        boxShape: NeumorphicBoxShape.circle(),
                        style: NeumorphicStyle(
                            depth: 3,
                            shape: NeumorphicShape.flat,
                            intensity: 0.7,
                            color: kMainColor),
                        child: Neumorphic(
                          padding: EdgeInsets.all(6),
                          boxShape: NeumorphicBoxShape.circle(),
                          style: NeumorphicStyle(depth: -2),
                          child: Consumer(
                            builder: (BuildContext context,
                                JuarezProvider juarez, Widget child) {
                              return Neumorphic(
                                boxShape: NeumorphicBoxShape.circle(),
                                style: NeumorphicStyle(
                                    depth: 7, color: kMainColor),
                                child: Center(
                                    child: juarez.hasBegun
                                        ? juarez.isResting
                                            ? Text(
                                                "${juarez.displayedRestingTime}")
                                            : Text(
                                                "REPS\n ${juarez.displayedReps}")
                                        : Text(
                                            "START",
                                          )),
                              );
                            },
                          ),
                        ),
                      ),
                    ),
                    NeumorphicCircularIndicator(
                      controller: controller,
                      size: Size.fromHeight(
                          MediaQuery.of(context).size.width * 0.8),
                    )
                  ],
                ),
              ),
              Spacer(flex: 2),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Spacer(
                    flex: 1,
                  ),
                  NeumorphicButton(
                    onPressed: () {},
                    boxShape: NeumorphicBoxShape.circle(),
                    child: Icon(FontAwesomeIcons.redo, color: kAccentColor),
                    style: NeumorphicStyle(color: kMainColor),
                  ),
                  Spacer(
                    flex: 2,
                  ),
                  NeumorphicButton(
                    onPressed: () {
                      if (!staticJuarez.hasBegun) {
                        staticJuarez.start();
                      } else if (staticJuarez.isResting &&
                          !staticJuarez.paused) {
                        staticJuarez.onClickPause();
                        controller.stop();
                      } else if (staticJuarez.isResting &&
                          staticJuarez.paused) {
                        staticJuarez.onClickResume();
                        controller.forward(from: controller.value);
                      }
                    },
                    boxShape: NeumorphicBoxShape.circle(),
                    child: Consumer(
                      builder: (BuildContext context, JuarezProvider juarez,
                          Widget child) {
                        return Icon(
                            !juarez.isResting || juarez.paused
                                ? FontAwesomeIcons.play
                                : FontAwesomeIcons.pause,
                            color: kAccentColor);
                      },
                    ),
                    style: NeumorphicStyle(color: kMainColor),
                  ),
                  Spacer(
                    flex: 2,
                  ),
                  NeumorphicButton(
                    onPressed: () {
                      if (!staticJuarez.isResting && staticJuarez.hasBegun) {
                        staticJuarez.onClickRepFinished();
                        controller
                          ..duration = Duration(seconds: staticJuarez.rest + 1);
                        controller.forward();
                      }
                    },
                    boxShape: NeumorphicBoxShape.circle(),
                    child:
                        Icon(FontAwesomeIcons.stepForward, color: kAccentColor),
                    style: NeumorphicStyle(color: kMainColor),
                  ),
                  Spacer(
                    flex: 1,
                  ),
                ],
              ),
              Spacer(
                flex: 5,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class CircularIndicatorPainter extends CustomPainter {
  final Animation<double> animation;

  CircularIndicatorPainter(this.animation);
  @override
  void paint(Canvas canvas, Size size) {
    final myPaint = Paint()
      ..color = Colors.greenAccent
      ..strokeWidth = 5
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke;

    double radius = (size.width / 2 - myPaint.strokeWidth / 2) - 3;
    Offset center = Offset(size.width / 2, size.height / 2);
    double arcAngle = (1.0 - animation.value) * 2 * pi;

    canvas.drawArc(Rect.fromCircle(center: center, radius: radius), -pi / 2,
        arcAngle, false, myPaint);
  }

  @override
  bool shouldRepaint(CircularIndicatorPainter old) {
    return old.animation.value != animation.value;
  }
}

class NeumorphicCircularIndicator extends AnimatedWidget {
  final AnimationController controller;
  final Size size;

  NeumorphicCircularIndicator({this.size, Key key, this.controller})
      : super(key: key, listenable: controller);

  Animation<double> get _progress => listenable;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: size,
      foregroundPainter: CircularIndicatorPainter(_progress),
    );
  }
}

所以这里的问题是,当我单击“下一步按钮”时,这意味着

staticJuarez.onClickRepFinished();
                        controller
                          ..duration = Duration(seconds: staticJuarez.rest + 1);
                        controller.forward();

当这部分执行时,CustomPaint应该平滑地绘制减小的外圆,但它每秒只更新外圆。

在提供程序中,有一个Timer每 1 秒计时一次的时间,但似乎Consumers 监听Provider和 my Animated Widget是独立的,因此即使Provider更新其值并重建build消费者中的所有功能,它也不会影响Animated Widget.

但我可能错了。

Animated Widget每次动画值更改而不是更新值后的每一秒时,我应该如何重建Provider

谢谢!

标签: flutteranimationprovider

解决方案


推荐阅读