Created
November 15, 2024 10:05
-
-
Save tejaswini-dev-techie/2b686ea47a72f60d509b8722750ce1bc to your computer and use it in GitHub Desktop.
Flutter animated timer that counts up smoothly with dynamic transitions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'dart:async'; | |
| void main() { | |
| runApp(const MyApp()); | |
| } | |
| class MyApp extends StatelessWidget { | |
| const MyApp({Key? key}) : super(key: key); | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| title: 'CountUp Timer Demo', | |
| theme: ThemeData( | |
| primarySwatch: Colors.blue, | |
| ), | |
| home: const TimerScreen(), | |
| ); | |
| } | |
| } | |
| class TimerScreen extends StatelessWidget { | |
| const TimerScreen({Key? key}) : super(key: key); | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| backgroundColor: Color(0xFFffedb7), | |
| appBar: AppBar( | |
| title: const Text("CountUp Timer"), | |
| ), | |
| body: Center( | |
| child: CountUpTimer( | |
| maxDuration: const Duration(seconds: 10), // Set the duration | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| class CountUpTimer extends StatefulWidget { | |
| final Duration maxDuration; | |
| const CountUpTimer({Key? key, this.maxDuration = const Duration(minutes: 15)}) : super(key: key); | |
| @override | |
| _CountUpTimerState createState() => _CountUpTimerState(); | |
| } | |
| class _CountUpTimerState extends State<CountUpTimer> { | |
| Duration duration = const Duration(); // Starts from 0 | |
| Timer? timer; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| startTimer(); | |
| } | |
| void startTimer() { | |
| timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); | |
| } | |
| void addTime() { | |
| setState(() { | |
| final seconds = duration.inSeconds + 1; | |
| if (seconds >= widget.maxDuration.inSeconds + 1) { | |
| timer?.cancel(); | |
| } else { | |
| duration = Duration(seconds: seconds); | |
| } | |
| }); | |
| } | |
| @override | |
| void dispose() { | |
| timer?.cancel(); | |
| super.dispose(); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| return buildTime(); | |
| } | |
| Widget buildTime() { | |
| String twoDigits(int n) => n.toString().padLeft(2, '0'); | |
| final hours = twoDigits(duration.inHours); | |
| final minutes = twoDigits(duration.inMinutes.remainder(60)); | |
| final seconds = twoDigits(duration.inSeconds.remainder(60)); | |
| return Row( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| buildTimeColumn(hours, "Hrs"), | |
| buildTimeColumn(minutes, "Mins"), | |
| buildTimeColumn(seconds, "Secs", isLast: true), | |
| ], | |
| ); | |
| } | |
| Widget buildTimeColumn(String time, String label, {bool isLast = false}) { | |
| return Column( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| Row( | |
| children: [ | |
| buildDigit(time[0]), | |
| buildDigit(time[1]), | |
| if (!isLast) buildTimeSeparator(), | |
| ], | |
| ), | |
| buildLabel(label), | |
| ], | |
| ); | |
| } | |
| Widget buildDigit(String digit) { | |
| return Container( | |
| padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), | |
| margin: const EdgeInsets.symmetric(horizontal: 2), | |
| decoration: BoxDecoration( | |
| color: Colors.white, | |
| borderRadius: BorderRadius.circular(10), | |
| ), | |
| child: ClipRect( | |
| child: AnimatedSwitcher( | |
| duration: const Duration(milliseconds: 600), | |
| switchInCurve: Curves.easeOutExpo, | |
| switchOutCurve: Curves.easeInExpo, | |
| transitionBuilder: (Widget child, Animation<double> animation) { | |
| return Stack( | |
| children: <Widget>[ | |
| SlideTransition( | |
| position: Tween<Offset>( | |
| begin: const Offset(0, -1), | |
| end: const Offset(0, 1), | |
| ).animate(CurvedAnimation( | |
| parent: animation, | |
| curve: Curves.easeOutCubic, | |
| )), | |
| child: FadeTransition( | |
| opacity: animation, | |
| child: child, | |
| ), | |
| ), | |
| SlideTransition( | |
| position: Tween<Offset>( | |
| begin: const Offset(0, -1), | |
| end: const Offset(0, 0), | |
| ).animate(CurvedAnimation( | |
| parent: animation, | |
| curve: Curves.bounceIn, | |
| )), | |
| child: FadeTransition( | |
| opacity: animation, | |
| child: child, | |
| ), | |
| ), | |
| ], | |
| ); | |
| }, | |
| child: Text( | |
| digit, | |
| key: ValueKey<String>(digit), | |
| style: const TextStyle( | |
| fontWeight: FontWeight.bold, | |
| color: Colors.black, | |
| fontSize: 50, | |
| ), | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| Widget buildLabel(String label) { | |
| return Text( | |
| label, | |
| style: const TextStyle( | |
| color: Colors.black, | |
| fontSize: 20, | |
| fontWeight: FontWeight.bold, | |
| ), | |
| ); | |
| } | |
| Widget buildTimeSeparator() { | |
| return Padding( | |
| padding: const EdgeInsets.symmetric(horizontal: 2.0), | |
| child: const Text( | |
| ":", | |
| style: TextStyle( | |
| color: Colors.black, | |
| fontSize: 50, | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
π Check out the Animated Count-Up Timer! β±οΈ
Experience a sleek, animated timer that counts up smoothly with dynamic transitions. Perfect for keeping track of time in your app with an engaging and modern UI! π₯
Whether you're building a fitness tracker, a countdown, or just want a stylish timer, this animated CountUp Timer will elevate your app's design and functionality!
Try it now and add a professional touch to your project. π
β For Countdown Timer: Learn how to build a countdown timer with animations to add excitement to your time-sensitive features.
π Read the step-by-step guide on Medium
Visual Preview
animated_count_up_timer_preview.mov