Creating a Linear Timer Widget in Flutter
Are you looking for a simple way to add a timer to your Flutter app? Look no further than the Linear Timer widget! This widget will allow you to easily implement a timer with a linear progress bar and a countdown display. In this tutorial, we’ll walk through how to create a Linear Timer widget in Flutter.
Setting Up the Widget
To get started, let’s create a new Flutter widget called LinearTimer. This widget will have two required properties: durationMiliseconds, which is the total duration of the timer in milliseconds, and onTimerFinish, which is a callback function that will be called when the timer has finished.
import 'dart:async';
import 'package:flutter/material.dart';
class LinearTimer extends StatefulWidget {
final int durationMiliseconds;
final Function onTimerFinish;
const LinearTimer({
Key? key,
required this.durationMiliseconds,
required this.onTimerFinish,
}) : super(key: key);
@override
_LinearTimerState createState() => _LinearTimerState();
}
Implementing the Timer
Next, we’ll implement the timer logic. In the widget’s state class, we’ll initialize the remaining milliseconds to the total duration of the timer, and the width of the progress bar to 1.0. We’ll then start a periodic timer using the Timer.periodic method, which will call a function every durationMiliseconds
milliseconds. In this function, we'll update the remaining milliseconds and the width of the progress bar. If the remaining milliseconds are zero or less, we'll call the onTimerFinish
callback function and cancel the timer.
class _LinearTimerState extends State<LinearTimer> {
late int _milisecondsRemaining;
late double _barWidth;
@override
void initState() {
super.initState();
_milisecondsRemaining = widget.durationMiliseconds;
_barWidth = 1.0;
startTimer();
}
void startTimer() {
Timer.periodic(Duration(milliseconds: widget.durationMiliseconds), (timer) {
setState(() {
if (_milisecondsRemaining > 0) {
_milisecondsRemaining -= widget.durationMiliseconds;
_barWidth = _milisecondsRemaining <= 0
? 0
: _milisecondsRemaining / widget.durationMiliseconds;
} else {
widget.onTimerFinish();
timer.cancel();
}
});
});
}
@override
Widget build(BuildContext context) {
// ...
}
}
Building the UI
Stack(
children: [
Container(
height: 30.0,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Colors.black,
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: _barWidth,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Colors.white,
),
),
),
),
),
SizedBox(
height: 30,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Text(
_milisecondsRemaining <= 0
? "finished"
: ("${(_milisecondsRemaining / 1000).toStringAsFixed(0)}s left"),
style: TextStyle(
color: _milisecondsRemaining <= 0
? Colors.white
: Colors.black,
fontSize: 14,
),
),
),
const Icon(
Icons.timer_sharp,
color: Colors.white,
)
],
),
),
)
],
);
Full Code
import 'dart:async';
import 'package:flutter/material.dart';
class LinearTimer extends StatefulWidget {
final int durationMiliseconds;
final Function onTimerFinish;
const LinearTimer(
{super.key,
required this.durationMiliseconds,
required this.onTimerFinish});
@override
_LinearTimerState createState() => _LinearTimerState();
}
class _LinearTimerState extends State<LinearTimer> {
late int _milisecondsRemaining;
late double _barWidth;
int durationMiliseconds = 17;
@override
void initState() {
super.initState();
_milisecondsRemaining = widget.durationMiliseconds;
_barWidth = 1.0;
startTimer();
}
void startTimer() {
Timer.periodic(Duration(milliseconds: durationMiliseconds), (timer) {
setState(() {
if (_milisecondsRemaining > 0) {
_milisecondsRemaining -= durationMiliseconds;
_barWidth = _milisecondsRemaining <= 0
? 0
: _milisecondsRemaining / widget.durationMiliseconds;
} else {
widget.onTimerFinish();
timer.cancel();
}
});
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 30.0,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Colors.black,
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor: _barWidth,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: Colors.white,
),
),
),
),
),
SizedBox(
height: 30,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Text(
_milisecondsRemaining <= 0
? "finished"
: ("${(_milisecondsRemaining / 1000).toStringAsFixed(0)}s left"),
style: TextStyle(
color: _milisecondsRemaining <= 0
? Colors.white
: Colors.black,
fontSize: 14,
),
),
),
const Icon(
Icons.timer_sharp,
color: Colors.white,
)
],
),
),
)
],
);
}
}
I hope you liked it.