vasanthkumar
Posted on April 25, 2024
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(),
) ;
}
}
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"))
],),
),
);
}
}
create Counter class as a stateful widget. declare a variable _counter
and initialise with the value zero.
int _counter = 0;
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++;
});
}
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")
)
To see the changes value convert the _counter to string and display using Text widget
Text(_counter.toString())
Result:
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';
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.
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();
}
start Timer function => toggle the pause option and cancel the timer.
void _pauseTimer() {
setState(() {
_timer?.cancel();
_isPause = true;
});
}
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;
});
}
converting the integer _counter
to minutes and seconds
"${(_counter ~/ 60).toString().padLeft(2, "0")}:${(_counter % 60).toString().padLeft(2, "0")}",
~/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),
),
);
}
}
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
Posted on April 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.