我的小部件使用TickerProviderStateMixn
andAnimationController
来制作动画CustomPaint
。并且每当AnimationController
更新时CustomPaint
,它还会通过调用update()
此数据处理程序中的方法来更新数据处理程序类。
这个小部件包含播放器的东西,每当update()
被调用时,我都会让播放器栏移动特定数量的 x 位置。播放器还必须显示时间值,基本上,播放器栏的位置指示时间如何流逝。所以时间值会沿着播放器栏的位置显示。我以为 Flutter 只支持 60 fps,意思update()
是每秒调用 60 次,我让玩家栏在被调用X/60
时移动。只是任何值,如果当玩家栏到达 100 的位置时时间变为 1 秒,那么将是 100。update()
X
X
但是最近,我把我的手机换成了新的,这个设备可以支持 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),
),
],
),
),
)),
);
}
}
所以基本上小部件被调用Editor
,EditPanel
需要数据处理程序被调用TestData
,而画家被调用TestPainter
。TestData
需要通过调用和_controller
来操纵的动画状态。通过小部件的状态(用户是否正在触摸/缩放/长触摸/等小部件,或者是否单击了按钮),动画小部件或停止动画它。需要作为变量(我现在知道可以访问,但只要我不触摸,应该没问题?),并且每当调用方法时它都会更新数据。即使调用了数据更新代码,数据的结果也与小部件的状态不同TestPainter
repeat()
stop()
EditPanel
_controller
TestPainter
TestData
TestPainter
_controller
TestPainter
_controller
paint
例如,
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()
AnimationController
TestData
所以如果玩家状态是playing
,_controller
会重复自己,导致调用 in 中的paint
方法TestPainter
。然后通过调用TestPainer
in方法进行调用,因此可以使用. 每当被调用时,数据都会更新,因此每个都会导致不同的外观(或者可能不取决于玩家的状态。如果玩家处于空闲状态,那么它会绘制相同的。实际上,它根本不会绘制,因为会称为)。_handlePlaying()
updateData(Size s)
paint
AnimationController
paint
paint
controller.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 数量。所以在这段代码X
中tick * blockSize
,frames
最初是 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
}