Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create an animated number counter?

Tags:

flutter

dart

I'd like to create a number counter that animates from a starting value to an end value. I've looked into using a Timer but can't seem to animate/update state properly. Including the decimal value would be great, but a simple integer animation is fine.

Number counter that needs to animate

double _mileCounter = 643.6;

_animateMileCounter() {
  Duration duration = new Duration(milliseconds: 300);
  return new Timer(duration, _updateMileCounter);
}

_updateMileCounter() {
  setState(() {
    _mileCounter += 1;
  });
}

How would I increment the counter X number of times (with animation)? Similar to how a car's odometer increments.

like image 355
Albert Lardizabal Avatar asked May 11 '17 13:05

Albert Lardizabal


People also ask

What is a number number animation?

Number animation, as in, imagine a number changing from 1 to 2, then 2 to 3, then 3 to 4, etc. over a specified time. Like a counter, except controlled by the same kind of animation that we use for other design animation on the web.

What would you do with a counter animation?

Like a counter, except controlled by the same kind of animation that we use for other design animation on the web. This could be useful when designing something like a dashboard, to bring a little pizazz to the numbers.

How can I animate a number in CSS?

Keeping it to CSS, we could use CSS counters to animate a number by adjusting the count at different keyframes: Another way would be to line up all the numbers in a row and animate the position of them only showing one at a time:

How can I animate the number row on a graph?

Another way would be to line up all the numbers in a row and animate the position of them only showing one at a time: Some of the repetitive code in these examples could use a preprocessor like Pug for HTML or SCSS for CSS that offer loops to make them perhaps easier to manage, but use vanilla on purpose so you can see the fundamental ideas.


3 Answers

For anyone still looking, you can use ImplicitlyAnimatedWidget.

Here is an example of an int counter. Works analogously for doubles.

class AnimatedCount extends ImplicitlyAnimatedWidget {
  final int count;

  AnimatedCount({
    Key key,
    @required this.count,
    @required Duration duration,
    Curve curve = Curves.linear
  }) : super(duration: duration, curve: curve, key: key);

  @override
  ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState() => _AnimatedCountState();
}

class _AnimatedCountState extends AnimatedWidgetBaseState<AnimatedCount> {
  IntTween _count;

  @override
  Widget build(BuildContext context) {
    return new Text(_count.evaluate(animation).toString());
  }

  @override
  void forEachTween(TweenVisitor visitor) {
    _count = visitor(_count, widget.count, (dynamic value) => new IntTween(begin: value));
  }
}

Just rebuild the widget with a new value and it automatically animates there.

like image 70
Norbert Avatar answered Oct 23 '22 03:10

Norbert


You should use an AnimationController with an AnimatedBuilder to rebuild your text when the controller changes. Here's an example that increments the miles when the floating action button is pressed (double.toStringAsFixed to get the decimal to show), with a curve on the animation speed:

screenshot

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        title: 'Flutter Demo',
        theme: new ThemeData(primarySwatch: Colors.purple),
        home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);
  @override
  createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _animation;
  double _miles = 0.0;

  @override initState() {
    _controller = new AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1500),
    );
    _animation = _controller;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    TextTheme textTheme = Theme.of(context).textTheme;
    return new Scaffold(
      body: new Material(
        color: const Color.fromRGBO(246, 251, 8, 1.0),
        child: new Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              new AnimatedBuilder(
                animation: _animation,
                builder: (BuildContext context, Widget child) {
                  return new Text(
                    _animation.value.toStringAsFixed(1),
                    style: textTheme.display4.copyWith(fontStyle: FontStyle.italic),
                  );
                },
              ),
              new Text(
                  "MILES",
                  style: textTheme.display1.copyWith(fontStyle: FontStyle.italic),
              )
            ],
          ),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.directions_run),
        onPressed: () {
          Random rng = new Random();
          setState(() {
            _miles += rng.nextInt(20) + 0.3;
            _animation = new Tween<double>(
                begin: _animation.value,
                end: _miles,
            ).animate(new CurvedAnimation(
              curve: Curves.fastOutSlowIn,
              parent: _controller,
            ));
          });
          _controller.forward(from: 0.0);
        }
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
like image 13
Collin Jackson Avatar answered Oct 23 '22 03:10

Collin Jackson


You can use Countup package.

Countup(
  begin: 0,
  end: 7500,
  duration: Duration(seconds: 3),
  separator: ',',
  style: TextStyle(
    fontSize: 36,
  ),
)

https://pub.dev/packages/countup

like image 9
alpceylan Avatar answered Oct 23 '22 03:10

alpceylan