skip to Main Content

In this code i try to make a simple increasing number with SlideTransition but it doesn’t work correctly, i could only use ScaleTransition successful. that means i want to increase number with SlideTransition

Gif example of what i want to create:

please see second part as animation

enter image description here

class TestScreen extends StatefulWidget {
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> with SingleTickerProviderStateMixin {
  int num = 0;
  late final AnimationController _animationController = AnimationController(
      duration: Duration(milliseconds: 750),
      vsync: this)
    ..forward();

  static final offset1 = Tween<Offset>(begin: Offset.zero, end: Offset(0, -1));
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Container(
        width: double.infinity,
        height: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            AnimatedSwitcher(
              duration: Duration(milliseconds: 1000),
              transitionBuilder: (child, animation) => SlideTransition(
                position: (offset1).animate(animation),
                child: child,
              ),
              child: Text(
                key: ValueKey(num),
                '$num',
                style: TextStyle(color: Colors.black, fontSize: 64.0),
              ),
            ),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  num++;
                });
              },
              child: Text('increment'),
            ),
          ],
        ),
      ),
    );
  }
}

2

Answers


  1. I’ve used a StreamBuilder to make a timer and assumed three situations to make change in position of the Text

    1. Time to change position from cener to down
    2. Time to update digit and move it from bottom to up (without animation).
    3. Time to move it from top to center.

    In order to make these three conditions I’ve used two Boolean variables and toggled their values.

    I know this looks so complicated. But this works,

    import 'package:flutter/material.dart';
    
    class TimeTest extends StatelessWidget {
      TimeTest({super.key});
    
      int mils = 0;
      int sec = 0;
      int min = 0;
    
      bool removeNumber = true;
      bool changeNumber = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: StreamBuilder(
              stream: Stream.periodic(Duration(milliseconds: 1)),
              builder: (context, snap) {
                //following code is for creating timer digits from the millisecods from stream
                //timer isn't accurate I think
                if (mils == 99) {
                  mils = 0;
                  if (sec == 59) {
                    sec = 0;
                    min++;
                  } else {
                    changeNumber = true;
                    removeNumber = false;
                    Future.delayed(Duration(milliseconds: 50)).then((value) async {
                      sec++;
                      changeNumber = false;
                      await Future.delayed(Duration(milliseconds: 15));
                      removeNumber = true;
                    });
                  }
                } else {
                  mils++;
                }
                //following part is for displaying the timer
                return Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('$min'.length < 2 ? '$min'.padLeft(2, '0') : '$min'),
                    const Text(':'),
                    AnimatedSlide(
                      //duration is set to zero while moving the text from bottom to up
                      duration: !removeNumber && !changeNumber
                          ? Duration.zero
                          : Duration(milliseconds: 50),
                      offset: removeNumber && !changeNumber
                          ? Offset(0, 0)
                          : !removeNumber && changeNumber
                              ? Offset(0, 1)
                              : !removeNumber && !changeNumber
                                  ? Offset(0, -1)
                                  : Offset(0, 0),
                      child:
                          Text('$sec'.length < 2 ? '$sec'.padLeft(2, '0') : '$sec'),
                    ),
                    const Text(':'),
                    Text('$mils'.length < 2 ? '$mils'.padLeft(2, '0') : '$mils'),
                  ],
                );
              },
            ),
          ),
        );
      }
    }
    
    

    Sorry that I didn’t optimised the code.
    Also note that the animation is only done for seconds.

    Login or Signup to reply.
  2. enter image description here

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    void fileMain() {
      runApp(
        const MaterialApp(
          home: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
      late AnimationController animatedController;
      late Timer timer;
    
      double milliseconds = 0;
    
      @override
      void initState() {
        animatedController = AnimationController.unbounded(
          vsync: this,
        );
        super.initState();
        timer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
          milliseconds += 500;
          animatedController.animateTo(
            milliseconds,
            duration: const Duration(milliseconds: 500),
          );
        });
      }
    
      @override
      void dispose() {
        animatedController.dispose();
    
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Padding(
              padding: const EdgeInsets.only(left: 120),
              child: AnimatedBuilder(
                animation: animatedController,
                builder: (context, child) {
                  final value = (animatedController.value / 100).ceil() / 10;
                  final map = value.toString().trim().split(".");
                  final animatedLetters = map[0].split("");
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: [
                      Container(
                          height: 10,
                          width: 10,
                          decoration: const BoxDecoration(
                              color: Colors.red, shape: BoxShape.circle)),
                      ...animatedLetters
                          .asMap()
                          .map(
                            (key, value) => MapEntry(
                              key,
                              AnimatedLetter(
                                key: ValueKey(key),
                                letter: value,
                              ),
                            ),
                          )
                          .values
                          .toList(),
                      Text(
                        '.${map[1]}',
                      ),
                    ],
                  );
                },
              ),
            ),
          ),
        );
      }
    }
    
    class AnimatedLetter extends StatefulWidget {
      const AnimatedLetter({super.key, this.letter});
    
      final String? letter;
    
      @override
      State<AnimatedLetter> createState() => _AnimatedLetterState();
    }
    
    class _AnimatedLetterState extends State<AnimatedLetter>
        with SingleTickerProviderStateMixin {
      AnimationController? controller;
    
      String? currentLetter;
      String? prevLetter;
    
      @override
      void initState() {
        controller = AnimationController(
          duration: const Duration(milliseconds: 300),
          vsync: this,
        );
        currentLetter = widget.letter;
        prevLetter = widget.letter;
        super.initState();
      }
    
      @override
      void didUpdateWidget(AnimatedLetter oldWidget) {
        if (widget.letter != oldWidget.letter) {
          setState(() {
            prevLetter = oldWidget.letter;
            currentLetter = widget.letter;
            controller!
              ..reset()
              ..forward();
          });
        }
    
        super.didUpdateWidget(oldWidget);
      }
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          child: AnimatedBuilder(
              animation: controller!,
              builder: (_, __) {
                return Stack(
                  alignment: Alignment.centerRight,
                  children: [
                    Transform.translate(
                      offset: Offset(0, controller!.value * 20),
                      child: Opacity(
                        opacity: 1 - controller!.value,
                        child: Text(
                          prevLetter!,
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                    Transform.translate(
                      offset: Offset(0, -20 + controller!.value * 20),
                      child: Opacity(
                        opacity: controller!.value,
                        child: Text(
                          currentLetter!,
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                  ],
                );
              }),
        );
      }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search