1Beginner Flutter Project - Break Timer
Last updated Sep 21, 2021In this flutter beginner project we will create a Break Timer app which will useful us when we want plan to do some work with break timings. So this app we can set timer for a specific time and we can measure our break timings. This will also show us the time to remaining at a specific period with the progress chart. In this app we have two screen where in the first screen we can see the timer indicator and set break timings with Work,short and long timers, where as in second screen we will set the timer for each individual periods. When we reached our timer it will notify us by play a sound.
What we will cover in this Application
- Building a layout with Animated progress status using an external library
- Handle asynchronous programming events to listen the data changes
- Flutter Navigating between screens in your app
- Implement Timer in flutter app
- Using shared preferences to persist data with in the device level storage
Let's get started
Step 1: Create a flutter application in your preferred IDE, present this app is developed under Android Studio
Step 2: Add required dependencies inside pubspec.yaml file
For this app we used below dependencies
percent_indicator : To show the Animated Progressbar
shared_preferences : To store the data with in the device level
dev_dependencies: flutter_test: sdk: flutter percent_indicator: ^3.3.0-nullsafety.1 shared_preferences: ^2.0.6 |
Step 3: Create Home which will display a status of the Time and buttons to set the timer
We were used Column widget to display work buttons, work progress and handle timer buttons
Column( children: [ Row( children: [ Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.lightGreen, text: "Work", onPressed: () => , size: 200,)), Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.amber, text: "Short Break", onPressed: () => , size: 200,)), Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.blueGrey, text: "Long Break", onPressed: ()=>,size: 200)), Padding(padding: EdgeInsets.all(defaultPadding),), ], ), StreamBuilder( initialData: '00:00', stream: , builder: (BuildContext context, AsyncSnapshot snapshot) { return Expanded( child: CircularPercentIndicator( radius: availableWidth / 2, lineWidth: 10.0, percent:0, center: Text("", style: Theme.of(context).textTheme.headline4), progressColor: Colors.deepPurpleAccent, )); } ), Row(children: [ Expanded( child: CustomButton( color: Colors.red, text: 'Stop', onPressed: () => , size: 200,)), Padding( padding: EdgeInsets.all(defaultPadding), ), Expanded( child: CustomButton( color: Colors.teal, text: 'Restart', onPressed: () => , size: 200)),],) ], ) |
![]() |
When we run the application we will get above result on the screen.
In the middle we have showed a Percentage indicator which will shows ua Timer. we will add this by
Expanded( child: CircularPercentIndicator( radius: availableWidth / 2, lineWidth: 8.0, percent: 1, center: Text( "${timer.time}\n\n $text", style: Theme.of(context).textTheme.headline6), progressColor: Colors.purpleAccent, )) |
Step 4: Now we will create Two class CountDownTimer, TimerModel for handle the timer events by streams
These two class will be like below
class TimerModel { String time; double percent; int work = 30; int selected = 0; TimerModel(this.time, this.percent,this.selected); } |
import 'dart:async'; import './timermodel.dart'; import 'package:shared_preferences/shared_preferences.dart'; class CountDownTimer { double _radius = 1; bool _isActive = true; late Timer timer; late Duration _time; late Duration _fullTime; int work = 30; int shortBreak = 5; int longBreak = 20; int selected=0; Stream stream() async* { yield* Stream.periodic(Duration(seconds: 1), (int a) { String time; if (this._isActive) { _time = _time - Duration(seconds: 1); _radius = _time.inSeconds / _fullTime.inSeconds; if (_time.inSeconds <= 0) { _isActive = false; } } time = returnTime(_time); return TimerModel(time, _radius,selected); }); } String returnTime(Duration t) { String minutes = (t.inMinutes<10) ? '0' + t.inMinutes.toString() : t.inMinutes.toString(); int numSeconds = t.inSeconds - (t.inMinutes * 60); String seconds = (numSeconds < 10) ? '0' + numSeconds.toString() : numSeconds.toString(); String formattedTime = minutes + ":" + seconds; return formattedTime; } void startWork()async { selected=0; await readSettings(); _radius = 1; _time = Duration(minutes: this.work, seconds: 0); _fullTime = _time; } void stopTimer() { selected=0; this._isActive = false; } void startTimer() { if (_time.inSeconds > 0) { this._isActive = true; } } void startBreak(bool isShort) { _radius = 1; if(isShort) selected=1; else selected=2; _time = Duration( minutes: (isShort) ? shortBreak: longBreak, seconds: 0); _fullTime = _time; } Future readSettings() async { SharedPreferences prefs = await SharedPreferences.getInstance(); work = (prefs.getInt('workTime') == null ? 30 : prefs.getInt('workTime'))!; shortBreak = (prefs.getInt('shortBreak') == null ? 30 : prefs.getInt('shortBreak'))!; longBreak = (prefs.getInt('longBreak') == null ? 30 : prefs.getInt('longBreak'))!; } } |
Step 5: After adding the timer functionality our home screen code will be like this
class Homepage extends StatelessWidget{ final double defaultPadding = 5.0; final CountDownTimer timer = CountDownTimer(); int selected=0; @override Widget build(BuildContext context) { timer.startWork(); menuItems.add(PopupMenuItem( value: 'Settings', child: Text('Settings'), )); return Scaffold( appBar: AppBar( title: Text('My Work Timer',style: TextStyle(color: Colors.white),), actions: [ PopupMenuButton( itemBuilder: (BuildContext context) { return menuItems.toList(); }, onSelected: (s) { if(s=='Settings') { goToSettings(context); }} ) ], ), body: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { final double availableWidth = constraints.maxWidth; return Column( children: [ Row( children: [ Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.lightGreen, text: "Work", onPressed: () => timer.startWork(), size: 200,)), Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.amber, text: "Short Break", onPressed: () => timer.startBreak(true), size: 200,)), Padding(padding: EdgeInsets.all(defaultPadding),), Expanded(child: CustomButton(color: Colors.blueGrey, text: "Long Break", onPressed: ()=>timer.startBreak(false),size: 200)), Padding(padding: EdgeInsets.all(defaultPadding),), ], ), StreamBuilder( initialData: '00:00', stream: timer.stream(), builder: (BuildContext context, AsyncSnapshot snapshot) { TimerModel timer = (snapshot.data == '00:00') ? TimerModel( '00:00', 1,0) : snapshot.data; var text=""; if(timer.selected==0) text="Work"; else if(timer.selected==1) text="Short Break"; else if(timer.selected==2) text="Long Break"; return Expanded( child: CircularPercentIndicator( radius: availableWidth / 2, lineWidth: 8.0, percent: timer.percent, center: Text( "${timer.time}\n\n $text", style: Theme.of(context).textTheme.headline6), progressColor: Colors.purpleAccent, )); } ), Row(children: [ Expanded( child: CustomButton( color: Colors.red, text: 'Stop', onPressed: () => timer.stopTimer(), size: 200,)), Padding( padding: EdgeInsets.all(defaultPadding), ), Expanded( child: CustomButton( color: Colors.teal, text: 'Restart', onPressed: () => timer.startTimer(), size: 200)),],) ], );} ) ); } final List> menuItems = >[]; void goToSettings(BuildContext context) { Navigator.push( context, MaterialPageRoute(builder: (context) => SettingsScreen())); } } |
Step 6: To Manage Timer settings we will created one more screen settings, where we will set the time for individual periods and save into shared preferences
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SettingsScreen extends StatefulWidget{ @override _SettingsScreenState createState() => _SettingsScreenState(); } typedef CallbackSetting = void Function(String, int); class _SettingsScreenState extends State { late TextEditingController txtWork=TextEditingController(); late TextEditingController txtShort=TextEditingController(); late TextEditingController txtLong=TextEditingController(); static const String WORKTIME = "workTime"; static const String SHORTBREAK = "shortBreak"; static const String LONGBREAK = "longBreak"; late int workTime; late int shortBreak; late int longBreak; late var prefs ; double buttonSize=50; @override void initState() { TextEditingController txtWork = TextEditingController(); TextEditingController txtShort = TextEditingController(); TextEditingController txtLong = TextEditingController(); readSettings(); super.initState(); } @override Widget build(BuildContext context) { TextStyle textStyle = TextStyle(fontSize: 24); return MaterialApp( home: Scaffold( appBar: AppBar(title: Text("Settings",style: TextStyle(color:Colors.white),),), body: Container( child: GridView.count( scrollDirection: Axis.vertical, crossAxisCount: 3, childAspectRatio: 3, crossAxisSpacing: 10, mainAxisSpacing: 10, children: [ Text("Work", style: textStyle), Text(""), Text(""), SettingsButton(Colors.red, "-", buttonSize, -1, WORKTIME, updateText ), TextField( style: textStyle, textAlign: TextAlign.center, keyboardType: TextInputType.number, controller: txtWork ), SettingsButton(Colors.green, "+", buttonSize, 1, WORKTIME, updateText), Text("Short", style: textStyle), Text(""), Text(""), SettingsButton(Colors.red, "-", buttonSize, -1, SHORTBREAK, updateText), TextField( style: textStyle, textAlign: TextAlign.center, keyboardType: TextInputType.number, controller: txtShort, ), SettingsButton(Colors.green, "+", buttonSize, 1, SHORTBREAK, updateText), Text("Long", style: textStyle,), Text(""), Text(""), SettingsButton(Colors.red, "-", buttonSize, -1,LONGBREAK, updateText), TextField( style: textStyle, textAlign: TextAlign.center, keyboardType: TextInputType.number, controller: txtLong), SettingsButton(Colors.green, "+", buttonSize, 1, LONGBREAK, updateText), SizedBox(height: 20,), MaterialButton(onPressed: () =>updateSetting(),child: Text("Save"),color: Colors.blueGrey,), ], padding: const EdgeInsets.all(20.0), ) ), ), ); } readSettings() async { prefs = await SharedPreferences.getInstance(); int workTime = prefs.getInt(WORKTIME); if (workTime==null) { await prefs.setInt(WORKTIME, int.parse('30')); } int shortBreak = prefs.getInt(SHORTBREAK); if (shortBreak==null) { await prefs.setInt(SHORTBREAK, int.parse('5')); } int longBreak = prefs.getInt(LONGBREAK); if (longBreak==null) { await prefs.setInt(LONGBREAK, int.parse('20')); } setState(() { txtWork.text = workTime.toString(); txtShort.text = shortBreak.toString(); txtLong.text = longBreak.toString(); }); } updateText(String key, int value) { switch (key) { case WORKTIME: { var workTime = int.parse(txtWork.text); if(workTime==null) workTime=0; workTime= (workTime)+value; if (workTime >= 1 && workTime <= 180) { setState(() { txtWork.text = workTime.toString(); }); } } break; case SHORTBREAK: { var short = int.parse(txtShort.text); if(short==null) short=0; short = (short)+value; if (short >= 1 && short <= 120) { setState(() { txtShort.text = short.toString(); }); } } break; case LONGBREAK: { var long = int.parse(txtLong.text); if(long==null) long=0; long = (long)+value; if (long >= 1 && long <= 180) { setState(() { txtLong.text = long.toString(); }); } } break; } } void updateSetting()async { print("On Button click"); prefs = await SharedPreferences.getInstance(); var workTime = int.parse(txtWork.text); var short = int.parse(txtShort.text); var long = int.parse(txtLong.text); await prefs.setInt(WORKTIME, workTime); await prefs.setInt(SHORTBREAK, short); await prefs.setInt(LONGBREAK, long); Navigator.pop(context); } } class SettingsButton extends StatelessWidget { final Color color; final String text; final int value; final double size; final String setting; final CallbackSetting callback; SettingsButton(this.color, this.text, this.size, this.value, this.setting, this.callback); @override Widget build(BuildContext context) { return MaterialButton( child:Text( this.text, style: TextStyle(color: Colors.white,fontSize: 18),), onPressed: () => this.callback(this.setting, this.value), color: this.color, minWidth: this.size, ); } } |
![]() |
Conclusion: In this Break Timer application we covered more flutter features like handle implement asynchronous programming with Flutter with stream builder and save local storage with shared Preferences and also learned how to implement timer functionality in flutter and Flutter ProgressIndicator
Article Contributed By :
|
|
|
|
867 Views |