Search code examples
flutterdartflutter-animationflareflame

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).


Solution

  • 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!'),
              );
            });
      }
    }