Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persistent Ticker in Flutter

How to get a persistent tick at every frame refresh time. For example in Flame game engine update method gets called at around every 1/60 seconds and a value dt with elapsed time is passed.

I want to implement one simple animation where a fan will rotate. I want to change its rotation speed depending on user input. My idea is that at every tick I will rotate the fan image/ container at a fixed value. As the user increases the speed I will increase the multiplier. There are few options like using the Flame engine or Flare, but they seem overkill. Also, I can use SingleTickerProviderMixin but there are few overheads like reverse the animation when finished and forwarded it and so...

I think there will be a simple solution, which will notify me at each frame refresh time that occurs at around every 1/60 seconds, and pass me the elapsed time dt (around 167 mS or so).

like image 672
Epsi95 Avatar asked Sep 05 '25 07:09

Epsi95


2 Answers

A nice way to do it (without Animation widgets), is to implement a Timer with a Stream; see the example below:

import 'package:flutter/material.dart';
import "dart:async";

const frequency = Duration(milliseconds: 50);

void main() => runApp(
      MaterialApp(
        home: Material(
          child: Center(
            child: Container(
              color: Colors.white,
              child: MyWidget(),
            ),
          ),
        ),
      ),
    );

class MyWidget extends StatefulWidget {
  MyWidgetState createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  final StreamController<double> _streamer =
      StreamController<double>.broadcast();

  Timer timer;

  double _rotation = 0.0;

  @override
  void initState() {
    super.initState();

    timer = Timer.periodic(frequency, (t) {
      _rotation++;
      _streamer.add(1);
    });
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<double>(
        initialData: 0,
        stream: _streamer.stream,
        builder: (context, snapshot) {
          return Transform(
            transform: Matrix4.rotationZ(_rotation),
            child: Text('Hello, World!'),
          );
        });
  }
}
like image 146
camillo777 Avatar answered Sep 07 '25 20:09

camillo777


I would also make sure to implement the dispose() callback if you copy this code. You need to make sure to cancel() any running timers to prevent odd behaviors or they will become a source of memory leaks.

The timer = null; is not always needed, but there are situations where the state object will hold a reference to the timer var itself and also cause a memory leak. For example, if you capture the timer var inside the timer callback body.

Example:

@override
  void dispose() {
    timer?.cancel();
    timer = null;
    super.dispose();
  }
like image 33
Curt Eckhart Avatar answered Sep 07 '25 20:09

Curt Eckhart