DEV Community

Cover image for Counter app in Flutter
vasanthkumar
vasanthkumar

Posted on • Edited on

Counter app in Flutter

Hi there,

I have the journey of learning flutter. I did learn Flutter almost 3 years back for some time but didn't work on any significant flutter project. I want to use the Github repo with a different app for each branch other than main branch.

The basic idea of counter app is to learn about stateful widget.

lib/main.dart

import 'package:flutter/material.dart';
import 'package:learning_flutter/pages/counter.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Counter(),
    ) ;
  }
}

Enter fullscreen mode Exit fullscreen mode

calling the counter widget in home of MaterialApp of MyApp. Create a pages folder under lib and create a file in pages with counter.dart

lib/pages/counter.dart

import 'package:flutter/material.dart';

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {

  int _counter = 0;

  void _incrementCounter(){
    setState(() {
      _counter++;
    });
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
          const Text("You have pressed the button this many times"),
          Text(_counter.toString(),
          style: const TextStyle(fontSize: 38),
          ),
          ElevatedButton(onPressed: _incrementCounter, child: const Text("Increment"))
        ],),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

create Counter class as a stateful widget. declare a variable _counter and initialise with the value zero.

int _counter = 0;
Enter fullscreen mode Exit fullscreen mode

write a function to update the value of _counter by 1 when ever the function is invoked. We should wrap the update of _counter variable inside the setState function.

void _incrementCounter(){
    setState(() {
      _counter++;
    });
  }
Enter fullscreen mode Exit fullscreen mode

updating _counter++ directly without setState in Flutter application will not automatically update the UI with the new value of _counter. This is because Flutter's UI is declarative, meaning the Ui is a function of the current state. When the state changes, the UI needs to be updated to reflect these changes. However, without calling setState, Flutter's framework is not notified of the state changes, and thus UI is not updated.

Finally, setState notifies the Flutter Framework that the state of the widget has changed. This triggers to schedule a call to build method which updates the UI to reflect the new state.

Add an ElevatedButton to trigger the _incrementCounter when clciked.

 ElevatedButton(
onPressed: _incrementCounter,
child: const Text("Increment")
)
Enter fullscreen mode Exit fullscreen mode

To see the changes value convert the _counter to string and display using Text widget

Text(_counter.toString())
Enter fullscreen mode Exit fullscreen mode

Result:

basic counter flutter app

I am following the Mitch Koko youtube video FULL Flutter Masterclass: Beginner to Pro for learning Flutter as of now.

Whenever I follow along the youtube video for coding I want to go one step further to learn better. So, from this Counter I want to transform it as 25 min timer.

import 'dart:async';
Enter fullscreen mode Exit fullscreen mode

The idea for timer is that assign a variable with 25*60 => 25 minutes and decrement it every second after clicking play, we can pause while timer decrementing and playing again will start from where timer paused. Reset timer when ever needed.

 final _oneSecond = const Duration(seconds: 1); // one second Duration
  Timer? _timer; // timer object 
  bool _isPause = true; // toggle between pause and play.
  int _counter = 25 * 60; // 25 minutes.
Enter fullscreen mode Exit fullscreen mode

start Timer function => toggle the pause option,
start the timer and decrement the _counter variable every second.
clear the time and toggle the pause option when _counter == 0

void _startTimer() {
    // toggle the pause option
    setState(() {
      _isPause = false;
    });

    _timer = Timer.periodic(
             _oneSecond, // duration
            (timer) {
      if (_counter == 0) {
        // cancel the timer and toggle the pause
        setState(() {
          _timer?.cancel();
          _isPause = true;
        });

      } else {
      // decrement the counter
        setState(() {
          _counter--;
        });
      }
    });
  }
/*The dispose method is then used to cancel the timer when the widget is removed from the tree, ensuring that the timer does not continue to run and use resources unnecessarily. */
 @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
Enter fullscreen mode Exit fullscreen mode

start Timer function => toggle the pause option and cancel the timer.

void _pauseTimer() {
    setState(() {
      _timer?.cancel();
      _isPause = true;
    });
  }
Enter fullscreen mode Exit fullscreen mode

reset Timer function => set the pause option to initial state, cancel the timer and set the counter to initial state.

  void _resetTimer() {
    setState(() {
      _timer?.cancel();
      _isPause = true;
      _counter = 25 * 60;
    });
  }
Enter fullscreen mode Exit fullscreen mode

converting the integer _counter to minutes and seconds

"${(_counter ~/ 60).toString().padLeft(2, "0")}:${(_counter % 60).toString().padLeft(2, "0")}",
Enter fullscreen mode Exit fullscreen mode

~/60 is integer division with 60 gives minutes,
%60 is modulo with 60 gives seconds.
toString() converts integer to string
padLeft(2,"0") gives consistent 2 digits => padded the string "0" until the length of the string is 2.

lib/pages/counter.dart

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

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

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  final _oneSecond = const Duration(seconds: 1);
  Timer? _timer;
  bool _isPause = true;

  int _counter = 25 * 60; // 25 minutes.

  void _startTimer() {
    setState(() {
      _isPause = false;
    });
    _timer = Timer.periodic(_oneSecond, (timer) {
      if (_counter == 0) {
        setState(() {
          _timer?.cancel();
          _isPause = true;
        });
      } else {
        setState(() {
          _counter--;
        });
      }
    });
  }

  void _pauseTimer() {
    setState(() {
      _timer?.cancel();
      _isPause = true;
    });
  }

  void _resetTimer() {
    setState(() {
      _timer?.cancel();
      _isPause = true;
      _counter = 25 * 60;
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "${(_counter ~/ 60).toString().padLeft(2, "0")}:${(_counter % 60).toString().padLeft(2, "0")}",
              style: const TextStyle(fontSize: 38),
            ),
            ElevatedButton(
              onPressed: _isPause ? _startTimer : _pauseTimer,
              child: _isPause
                  ? const Icon(Icons.play_arrow)
                  : const Icon(Icons.pause),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _resetTimer,
        child: const Icon(Icons.restart_alt_sharp),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

25 minutes timer

Thats it,
Thank you reading.
See you in the next article.

Lets Level Up.

References:
github counter/timer
FULL Flutter Masterclass: Beginner to Pro
Working with timers in flutter

Top comments (0)