0

我的小部件使用TickerProviderStateMixnandAnimationController来制作动画CustomPaint。并且每当AnimationController更新时CustomPaint,它还会通过调用update()此数据处理程序中的方法来更新数据处理程序类。

这个小部件包含播放器的东西,每当update()被调用时,我都会让播放器栏移动特定数量的 x 位置。播放器还必须显示时间值,基本上,播放器栏的位置指示时间如何流逝。所以时间值会沿着播放器栏的位置显示。我以为 Flutter 只支持 60 fps,意思update()是每秒调用 60 次,我让玩家栏在被调用X/60时移动。只是任何值,如果当玩家栏到达 100 的位置时时间变为 1 秒,那么将是 100。update()XX

但是最近,我把我的手机换成了新的,这个设备可以支持 120fps,而且它似乎AnimationController试图用这个最大的刷新率来动画小部件。所以update()现在被称为每秒 120 次,一切都以两倍的速度完成。我想让数据处理程序能够获得最大的刷新率,这样它就可以同步播放器栏与设备屏幕的移动。如果设备的最大刷新率为 60fps,则处理程序将在X/60每次调用时移动条,如果设备可以支持 120fps,则将在X/120每次调用时移动条,依此类推。

为此,我需要将设备显示器的最大刷新率传递给数据处理程序。我试图用谷歌搜索它,但我找不到任何有用的帖子或代码来说明获取此信息的方式。

有没有办法在颤动中获得最大刷新率?或者有没有可以提供此类信息的插件?

编辑:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:test_app/util/core/TestPainter.dart';
import 'package:test_app/util/core/cubit/TestData.dart';
import 'package:test_app/widget/EditPanel.dart';
import 'package:test_app/widget/Editor.dart';

class EditorPage extends StatefulWidget {
  EditorPage({required this.sh, required this.sw});

  final double sh;
  final double sw;

  @override
  EditorStatus createState() => EditorStatus();
}

class EditorStatus extends State<EditorPage> with TickerProviderStateMixin {
  late AnimationController _controller;
  late TestPainter painter;
  late TestData data;

  @override
  void initState() {
    _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
    _controller.repeat();

    data = TestData(widget.sh * 0.85, widget.sw, _controller);

    painter = TestPainter(data: data, animated: _controller);

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider<TestData>.value(
      value: data,
      child: Scaffold(
          body: FractionallySizedBox(
            widthFactor: 1,
            heightFactor: 1,
            child: SafeArea(
              child: Stack(
                children: [
                  Positioned(
                    top: widget.sh * 0.15,
                    bottom: 0,
                    left: 0,
                    right: 0,
                    child: Editor(
                      sh: widget.sh,
                      sw: widget.sw,
                      painter: painter,
                    ),
                  ),
                  Positioned(
                    top: widget.sh * 0.15 / 3,
                    left: 0,
                    right: 0,
                    height: widget.sh * 0.1,
                    child: EditPanel(widget.sw, widget.sh * 0.1),
                  ),
                ],
              ),
            ),
          )),
    );
  }
}

所以基本上小部件被调用EditorEditPanel需要数据处理程序被调用TestData,而画家被调用TestPainterTestData需要通过调用和_controller来操纵的动画状态。通过小部件的状态(用户是否正在触摸/缩放/长触摸/等小部件,或者是否单击了按钮),动画小部件或停止动画它。需要作为变量(我现在知道可以访问,但只要我不触摸,应该没问题?),并且每当调用方法时它都会更新数据。即使调用了数据更新代码,数据的结果也与小部件的状态不同TestPainterrepeat()stop()EditPanel_controllerTestPainterTestDataTestPainter_controllerTestPainter_controllerpaint

例如,

return IconButton(
  splashRadius: sh * 0.75 / 3,
  icon: Icon(Icons.play_arrow, color: ThemeManager.getRGBfromHex(0x77d478),),
  onPressed: () {
    if (!data.playing) {
      data.setStatus(Status.playing);
    }
  },
);

EditPanel, 这个播放按钮调用data, 是TestData, 在TestData,

void setStatus(Status st) {
  if(st == Status.idle) {
    pause();
    stopVelocity();
    current = st;
    if(!blockAnimating) {
      controller.stop();
    }
  } else if(st == Status.playing) {
    play();
    controller.repeat();
    current = st;
  } else if(st == Status.sliding) {
    current = st;
    controller.repeat();
  } else if(st == Status.reversing) {
    reverse();
    controller.repeat();
    current = st;
  } else if(st == Status.selecting) {
    current = st;
    controller.repeat();
  }

  if(blockAnimating) {
    controller.repeat();
  }
}

如您所见,如果st是,它playing会调用存储在 中的。controller.repeat()AnimationControllerTestData

所以如果玩家状态是playing,_controller会重复自己,导致调用 in 中的paint方法TestPainter。然后通过调用TestPainerin方法进行调用,因此可以使用. 每当被调用时,数据都会更新,因此每个都会导致不同的外观(或者可能不取决于玩家的状态。如果玩家处于空闲状态,那么它会绘制相同的。实际上,它根本不会绘制,因为会称为)。_handlePlaying()updateData(Size s)paintAnimationControllerpaintpaintcontroller.stop()

void updateData(Size s) {
  sh = s.height;
  sw = s.width;

  if(current == Status.playing) {
    _handlePlaying();
  } else if(current == Status.selecting) {
    updateSelectMode();
  } else if(current == Status.sliding) {
    performVelocity();
  } else if(current == Status.reversing) {
    _handleReverse();
  }

  _handleBlockAnimation();
}

void _handlePlaying() {
  if(playPos == ((_lastBlock?.row ?? 0) + 48) * blockSize) {
    setStatus(Status.idle);
    return;
  }

  double upf = tick * blockSize / frames; //Here

  playPos += upf;

  if(playPos - posX >= sw - sh * 0.075) {
    posX += upf;
  }

  updatePlayer();

  if(posX > endPosX) {
    posX = endPosX;
  }

  if(playPos > (( _lastBlock?.row ?? 0) + 48) * blockSize) {
    playPos = ((_lastBlock?.row ?? 0) + 48) * blockSize;
  }
}

playPos是播放器栏的位置,并且upf(每帧单位)是每帧必须移动的 x-pos 数量。所以在这段代码Xtick * blockSizeframes最初是 60。通过调整这个frames变量,我可以让玩家每帧移动得更慢或更快。如果我frames在显示可以支持 120 fps 的设备上设置为 120,则播放器会与实时同步,但在 60fps 显示上,播放器会慢两倍。相反,如果我设置frames为 60,则播放器与实时同步,在 120 fps 设备上,播放器速度提高两倍。

我知道如果 fps 低于 60,这种同步将被破坏。我知道这一点。我检查了我的小部件可以在慢速设备的调试模式下以 60 fps 的速度运行,因此将来它应该在发布模式下运行得更快。让我们跳过这个。

@override
void paint(Canvas cv, Size s) {
  //Somewhere of codes in TestPainter

  data.updateData(s);

  //Keep going
}
4

0 回答 0