我的小部件使用TickerProviderStateMixn
和AnimationController
来制作CustomPaint
的动画。每当AnimationController
更新CustomPaint
时,它也通过调用该数据处理程序中的update()
方法来更新数据处理程序类。
这个小部件包含播放器的东西,每当update()
被调用时,我让播放器栏移动特定数量的x位置。玩家还必须显示时间值,基本上,玩家栏的位置指示时间如何流逝。所以时间值是沿着播放器栏的位置显示的。我以为flutter只支持60 fps,这意味着update()
每秒被调用60次,我让播放器栏在update()
被调用时移动X/60
。X
是任意值,如果播放条到达100位置时时间变为1秒,则X
将为100。
但最近,我把我的手机换成了新的,这个设备可以支持120 fps,似乎AnimationController
试图用这个最大的刷新率来动画小部件。所以update()
现在每秒被调用120次,所有事情都快了两倍。我想使数据处理程序能够获得最大的刷新率,所以它可以同步播放器栏移动与设备的屏幕。如果设备的最大刷新率为60 fps,则处理程序将使bar每次调用移动X/60
,如果设备可以支持120 fps,则它将使bar每次调用移动X/120
,依此类推。
为了实现这一点,我需要通过设备的显示器的最大刷新率的数据处理程序。我试着用谷歌搜索,但我找不到任何有用的帖子或代码,告诉如何获得此信息。
有没有什么方法可以在flutter中获得最大刷新率?或者有任何插件可以提供这样的信息?
编辑:
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
通过调用repeat()
和stop()
来操纵TestPainter
的动画状态。通过小部件的状态(用户是否正在触摸/缩放/长时间触摸/等小部件,或者是否点击了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()
,这是存储在TestData
中的AnimationController
。
因此,如果播放器状态为playing
,则_controller
将重复其自身,从而导致TestPainter
中的paint
方法的调用。然后TestPainer
通过调用paint
中的updateData(Size s)
方法来调用_handlePlaying()
,这样就可以用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
变量,我可以让玩家在每帧中移动得更慢或更快。如果我在显示器可以支持120 fps的设备上将frames
设置为120,则播放器与实时同步,但在60 fps显示器上,播放器会慢两倍。相反,如果我将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
}
1条答案
按热度按时间zujrkrfu1#
您可以使用flutter_displaymode插件来获取设备的显示刷新率。该插件还允许设置设备的首选显示模式,但它有限制,具体取决于设备。
您可以使用
FlutterDisplayMode.supported
获取支持的显示模式,以确定最大刷新率。