Flutter game loop with Timer.periodic(16ms) has unstable frame pacing and low FPS on Android emulator/Web
I have a simple engine loop in Flutter driven by Timer.periodic(const Duration(milliseconds: 16)).
Even with a small fixed internal resolution, reported FPS can be very unstable and performance is poor on Web and Android emulator.
Current loop (simplified):
class EngineLoop { final StreamController<FrameData> _frameController = StreamController.broadcast(); Timer? _timer; DateTime _lastTick = DateTime.now(); double fps = 0; double dt = 0; Stream<FrameData> get frames => _frameController.stream; void start() { _timer ??= Timer.periodic(const Duration(milliseconds: 16), (_) => _tick()); } void _tick() { final now = DateTime.now(); final elapsed = now.difference(_lastTick).inMicroseconds / 1e6; _lastTick = now; final clamped = elapsed.clamp(0.0, 0.1).toDouble(); dt = clamped; fps = clamped == 0 ? 0 : 1 / clamped; update(clamped); draw(); _frameController.add(buildFrame()); } }
The CPU-side simulation benchmarks perfectly in isolation, so I’m fairly sure the bottleneck is how I’m handling timing and presentation. I'm starting to suspect that Timer.periodic is just the wrong tool for frame pacing in a render-heavy Flutter app.
I’m wondering if I should be moving toward something like Ticker or SchedulerBinding.scheduleFrameCallback to stay in sync with Flutter's internal clock. If I do make that jump, or if I decide to decouple the update and render logic entirely, what’s the best pattern to prevent "bursty" timing and that dreaded frame backlog? I'm aiming for a stable 60 FPS, but right now, it feels like the loop is fighting the engine rather than working with it.