1Beginner Flutter Project - Break Timer

Last updated Sep 21, 2021

In 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)),],)


      ],
    )

 

Flutter Beginner App - Break Timer

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,
    );

  }

}

 

Break Timer Flutter application

 

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 :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

586 Views