esse é um código flutter. por que o valor de ballPosition é sempre 0 no paint, mas no update não? Ambos atualizam a todo momento, mas o valor de ballPosition é sempre 0 em buildPaint:

class Ball {
  ui.Image? textureImage;
  bool visibleLights = true;
  late Object ball;
  double mass = 1;
  double radius = 5.9;
  Vector3 ballVelocity = Vector3.zero();
  double spin;
  late Vector3 ballPosition;
  bool run = false;
  bool inPocket = false;
  static AudioPlayer _audioPlayer = AudioPlayer(); // Reutilizando o player
  static bool _audioOnCooldown = false; // Flag para controle do áudio
  static Duration _cooldownDuration =
      Duration(milliseconds: 30); // Duração mínima entre sons
  static DateTime _lastPlayedTime = DateTime.now()
      .subtract(_cooldownDuration); // Última vez que o áudio foi tocado
  bool _collisionHappened = false;
  Vector3 posicaoAnterior = Vector3.all(0);
  BuildContext context;
  Ball(
      double initialX,
      double initialY,
      BlendMode blendMode,
      Mesh mesh,
      Scene? scene,
      this.spin,
      Vector3 rotation,
      double random,
      this.textureImage,
      this.context) {
    ball = Object(
        blendMode: blendMode,
        lighting: false,
        mesh: mesh,
        backfaceCulling: true,
        scene: scene,
        rotation: rotation);

    ball.position.setValues(initialX, initialY, 0);
    ballPosition = Vector3(initialX, initialY, 0);

    ball.rotate(Vector3(rotation.x, rotation.y, rotation.z), random);

    posicaoAnterior = ballPosition;

    ball.scale.setValues(radius, radius, radius);
  }

  Object get object => ball;

  static bool _canPlayAudio() {
    DateTime now = DateTime.now();
    if (now.difference(_lastPlayedTime) > _cooldownDuration) {
      _lastPlayedTime = now; // Atualiza a última vez que o áudio foi tocado
      return true;
    }
    return false;
  }

  // Reproduz o áudio com cooldown
  Future<void> playAudio() async {
    if (_canPlayAudio()) {
      await _audioPlayer.play(AssetSource('balls/ball.wav'));
    }
  }

  bool isCollidingWith(Ball otherBall) {
    // Verifica se qualquer uma das bolas está no buraco
    if (inPocket || otherBall.inPocket) {
      return false; // Não há colisão se qualquer uma das bolas estiver no buraco
    }

    // Calcula a distância entre as bolas
    double dx = ballPosition.x - otherBall.ballPosition.x;
    double dy = ballPosition.y - otherBall.ballPosition.y;
    double distance = sqrt(dx * dx + dy * dy);

    return distance <= radius;
  }

  double angl = 0;

  Vector3 previousPosition =
      Vector3.zero(); // Posição anterior para interpolação
  double previousAngl = 0; // Ângulo anterior para interpolação
  double smoothFactor = 0.1; // Fator de suavização (entre 0 e 1)

  void update(
      double dt, double friction, List<Rectangle> rectangles, Scene scene) {
    ballPosition += ballVelocity;

    ball.position.setFrom(ballPosition);

    // Aplicar atrito à velocidade da bola
    ballVelocity *= friction;

    // Calcular a distância percorrida pela bola no frame atual
    double speed = ballVelocity.length;
    double distanceTraveled = speed;

    // Calcular o incremento angular com base na distância percorrida
    double ballRadius = radius;
    double circumference = 2 * pi * ballRadius;
    double angleIncrement = (distanceTraveled / circumference) * 360;

    // Calcular o ângulo de rotação baseado na direção do movimento (velocidade)
    angl = atan2(ballVelocity.y, ballVelocity.x);

    // Vetor de rotação para aplicar a rotação na bola
    Vector3 rotationAxis = Vector3(sin(-angl), cos(angl), 0);

    // Aplicar rotação à bola com base na distância percorrida
    ball.rotate(rotationAxis, radians(angleIncrement));
    // ball.updateTransform();
    ball.scene!.update();

    if (ballVelocity.length < 0.03) {
      ballVelocity.setZero();
    }
  }

  CustomPaint buildPaint() {
    return CustomPaint(
      //size: const Size(10, 10),
      painter: SpherePainter(textureImage!, ballPosition, context),
    );
  }
}