Code is copied!
Habit Tracker App Using Flutter
In this tutorial, we'll be creating a Habit Tracker App application in Flutter,
Habit app are essential app for keeping Habits and staying organized.
Our app creates a scrollable Habit list , allowing users to save Habit via a text field. Habits are dynamically
displayed within containers and checkboxes. Users can scroll through the list as needed. Users can edit , delete their Habit details on ticking the checkboxes.
By the end of this tutorial, you'll have a functional Habit List app that you can run on both Android and iOS
devices.
Source Code for the main.dart file
Add the following Code inside your main.dart file :
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
theme: ThemeData(primarySwatch: Colors.green)
);
}
}
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
theme: ThemeData(primarySwatch: Colors.green)
);
}
}
Source Code for the home_page.dart file
Add the following Code inside your home_page.dart file :
import 'package:flutter/material.dart';
import 'habit_tile.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State< HomePage> createState() => _HomePageState();
}
class _HomePageState extends State< HomePage> {
final _newHabitNameController = TextEditingController();
List todayHabitList = [
["Morning Run", false],
["Gym", false]
];
// open habit settings to edit
void openHabitSettings(int index) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: TextField(
controller: _newHabitNameController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Enter new Habit....',
hintStyle: TextStyle(color: Colors.grey[600]),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
actions: [
MaterialButton(
onPressed: (){
saveExistingHabit(index);
},
child: Text(
"Update",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
MaterialButton(
onPressed: (){
cancelDialogBox();
},
child: Text(
"Cancel",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
],
);
},
);
}
// save existing habit with a new name
void saveExistingHabit(int index) {
setState(() {
todayHabitList[index][0] = _newHabitNameController.text;
});
_newHabitNameController.clear();
Navigator.pop(context);
}
// delete habit
void deleteHabit(int index) {
setState(() {
todayHabitList.removeAt(index);
});
}
void saveNewHabit() {
// add new habit to todays habit list
setState(() {
todayHabitList.add([_newHabitNameController.text, false]);
});
// clear textfield
_newHabitNameController.clear();
// pop dialog box
Navigator.of(context).pop();
}
// cancel new habit
void cancelDialogBox() {
// clear textfield
_newHabitNameController.clear();
// pop dialog box
Navigator.of(context).pop();
}
void createnewhabit(){
showDialog(context: context, builder: (context){
return
AlertDialog(
content: TextField(
controller: _newHabitNameController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Enter new Habit....',
hintStyle: TextStyle(color: Colors.grey[600]),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
actions: [
MaterialButton(
onPressed: (){
saveNewHabit();
},
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
MaterialButton(
onPressed: (){
cancelDialogBox();
},
child: Text(
"Cancel",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
],
);
},
);
}
void checkBoxtapped(bool? value, int index){
setState(() {
todayHabitList[index][1]=value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: Text('My Habits'),
centerTitle: true,
),
backgroundColor: Colors.grey[300],
floatingActionButton: FloatingActionButton(
onPressed: (){
createnewhabit();
},
child: Icon(Icons.add)
),
body: ListView.builder(
itemCount: todayHabitList.length,
itemBuilder: (context,index){
return HabitTile(
habitName: todayHabitList[index][0],
habitCompleted: todayHabitList[index][1],
onChanged: (value){
checkBoxtapped(value, index);
},
settingsTapped: (context){
openHabitSettings(index);
},
deleteTapped: (context){
deleteHabit(index);
},
);
})
);
}
}
import 'package:flutter/material.dart';
import 'habit_tile.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State< HomePage> createState() => _HomePageState();
}
class _HomePageState extends State< HomePage> {
final _newHabitNameController = TextEditingController();
List todayHabitList = [
["Morning Run", false],
["Gym", false]
];
// open habit settings to edit
void openHabitSettings(int index) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: TextField(
controller: _newHabitNameController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Enter new Habit....',
hintStyle: TextStyle(color: Colors.grey[600]),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
actions: [
MaterialButton(
onPressed: (){
saveExistingHabit(index);
},
child: Text(
"Update",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
MaterialButton(
onPressed: (){
cancelDialogBox();
},
child: Text(
"Cancel",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
],
);
},
);
}
// save existing habit with a new name
void saveExistingHabit(int index) {
setState(() {
todayHabitList[index][0] = _newHabitNameController.text;
});
_newHabitNameController.clear();
Navigator.pop(context);
}
// delete habit
void deleteHabit(int index) {
setState(() {
todayHabitList.removeAt(index);
});
}
void saveNewHabit() {
// add new habit to todays habit list
setState(() {
todayHabitList.add([_newHabitNameController.text, false]);
});
// clear textfield
_newHabitNameController.clear();
// pop dialog box
Navigator.of(context).pop();
}
// cancel new habit
void cancelDialogBox() {
// clear textfield
_newHabitNameController.clear();
// pop dialog box
Navigator.of(context).pop();
}
void createnewhabit(){
showDialog(context: context, builder: (context){
return
AlertDialog(
content: TextField(
controller: _newHabitNameController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Enter new Habit....',
hintStyle: TextStyle(color: Colors.grey[600]),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
),
),
actions: [
MaterialButton(
onPressed: (){
saveNewHabit();
},
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
MaterialButton(
onPressed: (){
cancelDialogBox();
},
child: Text(
"Cancel",
style: TextStyle(color: Colors.white),
),
color: Colors.green[600],
),
],
);
},
);
}
void checkBoxtapped(bool? value, int index){
setState(() {
todayHabitList[index][1]=value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: Text('My Habits'),
centerTitle: true,
),
backgroundColor: Colors.grey[300],
floatingActionButton: FloatingActionButton(
onPressed: (){
createnewhabit();
},
child: Icon(Icons.add)
),
body: ListView.builder(
itemCount: todayHabitList.length,
itemBuilder: (context,index){
return HabitTile(
habitName: todayHabitList[index][0],
habitCompleted: todayHabitList[index][1],
onChanged: (value){
checkBoxtapped(value, index);
},
settingsTapped: (context){
openHabitSettings(index);
},
deleteTapped: (context){
deleteHabit(index);
},
);
})
);
}
}
Source Code for the habit_tile.dart file
Add the following Code inside your habit_tile.dart file :
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class HabitTile extends StatefulWidget {
final String habitName;
final bool habitCompleted;
final Function(bool?)? onChanged;
final Function(BuildContext)? settingsTapped;
final Function(BuildContext)? deleteTapped;
const HabitTile({super.key, required this.habitName, required this.habitCompleted, required this.onChanged,
required this.settingsTapped,
required this.deleteTapped,});
@override
State< HabitTile> createState() => _HabitTileState();
}
class _HabitTileState extends State< HabitTile> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Slidable(
endActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
onPressed: widget.settingsTapped,
backgroundColor: Colors.grey.shade800,
icon: Icons.settings,
borderRadius: BorderRadius.circular(12),
),
// delete option
SlidableAction(
onPressed: widget.deleteTapped,
backgroundColor: Colors.red.shade400,
icon: Icons.delete,
borderRadius: BorderRadius.circular(12),
),
],
),
child: Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(16)
),
child: Row(
children: [
Checkbox(value:widget.habitCompleted,
onChanged: widget.onChanged
),
Text(widget.habitName,style: TextStyle(
fontSize: 17
),),
],
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class HabitTile extends StatefulWidget {
final String habitName;
final bool habitCompleted;
final Function(bool?)? onChanged;
final Function(BuildContext)? settingsTapped;
final Function(BuildContext)? deleteTapped;
const HabitTile({super.key, required this.habitName, required this.habitCompleted, required this.onChanged,
required this.settingsTapped,
required this.deleteTapped,});
@override
State< HabitTile> createState() => _HabitTileState();
}
class _HabitTileState extends State< HabitTile> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Slidable(
endActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
onPressed: widget.settingsTapped,
backgroundColor: Colors.grey.shade800,
icon: Icons.settings,
borderRadius: BorderRadius.circular(12),
),
// delete option
SlidableAction(
onPressed: widget.deleteTapped,
backgroundColor: Colors.red.shade400,
icon: Icons.delete,
borderRadius: BorderRadius.circular(12),
),
],
),
child: Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(16)
),
child: Row(
children: [
Checkbox(value:widget.habitCompleted,
onChanged: widget.onChanged
),
Text(widget.habitName,style: TextStyle(
fontSize: 17
),),
],
),
),
),
);
}
}