{"id":89479,"date":"2024-10-28T18:00:14","date_gmt":"2024-10-28T12:30:14","guid":{"rendered":"https:\/\/techvidvan.com\/tutorials\/?p=89479"},"modified":"2026-06-03T14:58:06","modified_gmt":"2026-06-03T09:28:06","slug":"flutter-to-do-app","status":"publish","type":"post","link":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/","title":{"rendered":"Flutter Project &#8211;  To Do App"},"content":{"rendered":"<p>In our daily lives, we have to work on several tasks, whether it is picking up groceries, completing homework, or finishing a presentation for work. Sometimes, it is difficult to keep track of all the tasks, and some get missed. To help solve this problem, we will build a Flutter To-Do app to keep track of all the tasks.<\/p>\n<p>In the Flutter To-Do App Project, we will keep track of all the tasks and their deadlines. We will also show how many tasks are done out of the total tasks and how many remain. We can also add a new task or delete a task if it is completed.<\/p>\n<p>Let\u2019s get started!<\/p>\n<h2>Prerequisites For Flutter To-Do App<\/h2>\n<p>Before starting work on building the app, you should have the below-mentioned required software on your computer:<\/p>\n<p><strong>(i) Flutter &#8211;<\/strong> Refer to the link for installing<a href=\"https:\/\/docs.flutter.dev\/get-started\/install\"> Flutter<\/a>, depending on your operating system.<br \/>\n<strong>(ii) Android Studio<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>\u2014<\/strong>You can download <a href=\"https:\/\/developer.android.com\/studio\">Android Studio<\/a>. The app must be run on an<\/span>\u00a0Android emulator.<br \/>\n<strong>(iii) Visual Studio Code &#8211;<\/strong> Although unnecessary, you can also build apps in Android Studio. However, in our case, we used <a href=\"https:\/\/code.visualstudio.com\/download\">Visual Studio Code<\/a> as it is a good code editor.<\/p>\n<p>Now that the setup is ready let\u2019s get started!<\/p>\n<h3>Download Flutter To-Do App Project<\/h3>\n<p>Please download the source code of Flutter To-Do App Project: <a href=\"https:\/\/drive.google.com\/file\/d\/1Ck3jClQs7WaRcj-GM4IyXWiR4391nPZS\/view?usp=drive_link\"><strong>Flutter To-Do App Project Code.<\/strong><\/a><\/p>\n<h3>Creating New Flutter Project<\/h3>\n<p><strong>First, let\u2019s create a new project in Flutter by doing the following steps:-<\/strong><\/p>\n<p>1. Go to the directory where you want to save the project using:-<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">cd  $Project-directory-path\r\n<\/pre>\n<p>2. Then create a new project using the below command:-<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">flutter create to_do_app<\/pre>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Creating-New-Flutter-Project-2.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89683\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Creating-New-Flutter-Project-2.webp\" alt=\"Creating New Flutter Project\" width=\"456\" height=\"60\" \/><\/a><\/p>\n<h3>Changes in pubsec.yaml file<\/h3>\n<p><strong>To build the application, we would need the following library:-<\/strong><\/p>\n<p><strong>intl &#8211;<\/strong> We will use this library to format the date in the required format to show when the news was published. The command mentioned below can be used to install the library:-<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">flutter pub add intl<\/pre>\n<h3>Steps to Build Flutter To-Do App<\/h3>\n<h4>1. Editing the Main File and Specifying the Theme for the App<\/h4>\n<p>Let\u2019s start by creating the app&#8217;s main layout. In the main function, we are returning MaterialApp, which will add the app&#8217;s material design and basic styling. We are also specifying a theme for the app. Here, we have specified primaryColor, textTheme for various sizes of titles, and appBarTheme. In the home argument, we are returning the Scaffold widget with appBar, and in the body argument of the Scaffold, we are returning the HomePage widget, which we will create below in the article.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\n\r\nimport '.\/home_page.dart';\r\n\r\nvoid main() {\r\n  runApp(MaterialApp(\r\n      theme: ThemeData(\r\n        primaryColor: const Color.fromARGB(221, 255, 174, 60),\r\n        textTheme: const TextTheme(\r\n          titleLarge: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),\r\n          titleMedium: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),\r\n          displayMedium: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\r\n        ),\r\n        appBarTheme: const AppBarTheme(\r\n          backgroundColor: Color.fromARGB(\r\n            221,\r\n            234,\r\n            150,\r\n            33,\r\n          ),\r\n        ),\r\n      ),\r\n      home: Scaffold(\r\n        appBar: AppBar(\r\n          title: const Text('To-Do App (By TechVidvan)'),\r\n        ),\r\n        body: const HomePage(),\r\n      )));\r\n}<\/pre>\n<h4>2. Creating Task Class<\/h4>\n<p>Here, we are creating a class called Task, which will store the details\/parameters of a Task, such as its title, description, and whether it is completed or not, in a variable called is completed, date, and category.<\/p>\n<p>We have also defined a getter function called the formattedDate, which will format the date in the required format. For this, we are using the method DateFormat, which we imported from the intl package.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\nimport 'package:intl\/intl.dart';\r\n\r\nvar formatter = DateFormat.yMd();\r\n\r\nenum Category { home, shopping, study, work, contact }\r\n\r\nvar categoryIcons = {\r\n  Category.home: Icons.home,\r\n  Category.shopping: Icons.shopping_cart,\r\n  Category.study: Icons.menu_book_outlined,\r\n  Category.work: Icons.work,\r\n  Category.contact: Icons.contact_phone_outlined \r\n};\r\n\r\nclass Task {\r\n  String title;\r\n  String description;\r\n  bool isCompleted;\r\n  DateTime date;\r\n  Category category;\r\n\r\n  Task(\r\n      {required this.title,\r\n      required this.description,\r\n      required this.isCompleted,\r\n      required this.date,\r\n      required this.category});\r\n\r\n  String get formattedDate {\r\n    return formatter.format(date);\r\n  }\r\n}<\/pre>\n<h4>3. Building the Home Page for the App<\/h4>\n<p>Let\u2019s start building the HomePage of the app. Here, we have imported Task model, which we created above as well as some other custom widgets, which we will create in the next sections below.<\/p>\n<p>HomePage is a Stateful Widget as the content of the page needs to be updated as the user enters new tasks<\/p>\n<p><strong>Here, we have defined variables like:-<\/strong><\/p>\n<p><strong>a. _totalTasks &#8211;<\/strong> To keep track of the total number of tasks added by the user<br \/>\n<strong>b. _tasksCompleted &#8211;<\/strong> This shows the number of tasks that the user has completed<br \/>\n<strong>c. _tasksRemaining &#8211;<\/strong> This shows the tasks left or not completed by the user.<\/p>\n<p>We have also defined a variable, \u2018tasks, &#8216; as a list to store user-added tasks.<\/p>\n<p><strong>We have also defined functions for the user interaction with the app. These are:-<\/strong><\/p>\n<p><strong>a. addNewTask &#8211;<\/strong> This is used to add new Tasks to the list of \u2018tasks\u2019. It accepts the task as an argument of the function.<br \/>\n<strong>b. openModelSheet<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>\u2014<\/strong>It is used to show the ModalBottomSheet and add a new task. In the\u00a0<strong>showModalBottomSheet,<\/strong>\u00a0we have set the\u00a0<strong>useSafeArea parameter<\/strong> to true. In the builder function, we are returning the NewTask widget to add a new task to<\/span>\u00a0the list, which we will create in the next step.<br \/>\n<strong>c. updateTasksCompletion &#8211;<\/strong> This function helps update the _totalTasks, _tasksCompleted, _tasksRemaining, which we created above when the user adds or deletes a task.<br \/>\n<strong>d. removeTask<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>\u2014<\/strong>It executes when the user deletes a task. It removes the task from the list of tasks and executes the updateTasksCompletion function. After that, it shows a\u00a0<strong>SnackBar<\/strong> using which the user can Undo the deletion. In the content argument, it shows \u2018Task Deleted\u2019 using a Text widget,<\/span>\u00a0and in the action argument, it shows a SnackBarAction to undo the task deleted and add it again in the list.<\/p>\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">We have also overridden the<strong>\u00a0initState function<\/strong> so that the updateTasksCompletion function is also called when the app is built.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\n\r\nimport '.\/models\/task.dart';\r\nimport 'widgets\/tasks_list.dart';\r\nimport '.\/new_task.dart';\r\n\r\nclass HomePage extends StatefulWidget {\r\n  const HomePage({super.key});\r\n  @override\r\n  State&lt;HomePage&gt; createState() {\r\n    return _HomePageState();\r\n  }\r\n}\r\n\r\nclass _HomePageState extends State&lt;HomePage&gt; {\r\n  var _totalTasks = 0;\r\n  var _tasksCompleted = 0;\r\n  var _tasksRemaining = 0;\r\n\r\n  List&lt;Task&gt; tasks = [\r\n    Task(\r\n        title: 'Get Groceries',\r\n        description:\r\n            'Need to go to the market and buy milk, fruits and vegetables',\r\n        isCompleted: false,\r\n        date: DateTime.now(),\r\n        category: Category.shopping),\r\n    Task(\r\n        title: 'Homework',\r\n        description: 'Submit the problems of Math',\r\n        isCompleted: false,\r\n        date: DateTime.now(),\r\n        category: Category.study)\r\n  ];\r\n\r\n  void addNewTask(Task task) {\r\n    setState(() {\r\n      tasks.add(task);\r\n    });\r\n  }\r\n\r\n  void openModelSheet() {\r\n    showModalBottomSheet(\r\n        useSafeArea: true,\r\n        isScrollControlled: true,\r\n        context: context,\r\n        builder: (ctx) {\r\n          return NewTask(\r\n              updateTotalTasksStatus: updateTasksCompletion,\r\n              onAddTask: addNewTask);\r\n        });\r\n  }\r\n\r\n  void updateTasksCompletion() {\r\n    setState(() {\r\n      _totalTasks = tasks.length;\r\n      _tasksCompleted =\r\n          tasks.where((element) =&gt; element.isCompleted == true).length;\r\n      _tasksRemaining = _totalTasks - _tasksCompleted;\r\n    });\r\n  }\r\n\r\n  void removeTask(Task task) {\r\n    final taskIndex = tasks.indexOf(task);\r\n    setState(() {\r\n      tasks.remove(task);\r\n      updateTasksCompletion();\r\n    });\r\n    ScaffoldMessenger.of(context).clearSnackBars();\r\n    ScaffoldMessenger.of(context).showSnackBar(\r\n      SnackBar(\r\n        duration: const Duration(seconds: 3),\r\n        content: const Text('Task Deleted!'),\r\n        action: SnackBarAction(\r\n            label: 'Undo',\r\n            onPressed: () {\r\n              setState(() {\r\n                tasks.insert(taskIndex, task);\r\n              });\r\n              updateTasksCompletion();\r\n            }),\r\n      ),\r\n    );\r\n  }\r\n\r\n  @override\r\n  void initState() {\r\n    super.initState();\r\n    updateTasksCompletion();\r\n  }<\/pre>\n<p>In the build function, we are returning the Column widget to show the content on the page. In the children, we have added a SizedBox to maintain a gap from the above for the content. We are using Text widget to show the totalTasks and using the style from the textTheme we defined in the main file. Below it, we show the tasksCompleted and tasksRemaining horizontally using the Row widget. We are adding a line after it using the Divider widget to separate the content.<\/p>\n<p>After that, we return the tasks list using the TasksList widget, which we will create in the helper widgets section inside the Expanded widget. We are using the Spacer widget to take as much height as possible, and at the bottom of the screen, we are returning an Elevated Button showing text as \u2018Add New Task\u2019. openModelSheet function gets executed when the user presses the button, which shows a ModelBottomSheet through which the user can add a new task.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">@override\r\n  Widget build(BuildContext context) {\r\n    return Column(\r\n      children: [\r\n        const SizedBox(\r\n          height: 15,\r\n        ),\r\n        Text(\r\n          'Total Tasks - $_totalTasks',\r\n          style: Theme.of(context).textTheme.titleLarge,\r\n        ),\r\n        const SizedBox(\r\n          height: 25,\r\n        ),\r\n        Row(\r\n          children: [\r\n            const Spacer(),\r\n            Text(\r\n              'Completed - $_tasksCompleted',\r\n              style: Theme.of(context).textTheme.titleMedium,\r\n            ),\r\n            const SizedBox(\r\n              width: 45,\r\n            ),\r\n            Text(\r\n              'Remaining - $_tasksRemaining',\r\n              style: Theme.of(context).textTheme.titleMedium,\r\n            ),\r\n            const Spacer()\r\n          ],\r\n        ),\r\n        const SizedBox(\r\n          height: 15,\r\n        ),\r\n        const Divider(\r\n          thickness: 1,\r\n          color: Colors.grey,\r\n        ),\r\n        Expanded(\r\n          flex: 4,\r\n          child: TasksList(\r\n              onRemoveTask: removeTask,\r\n              tasks: tasks,\r\n              updateTaskStatus: updateTasksCompletion),\r\n        ),\r\n        const Spacer(),\r\n        ElevatedButton(\r\n          onPressed: openModelSheet,\r\n          style: ElevatedButton.styleFrom(\r\n            backgroundColor: Theme.of(context).primaryColor,\r\n            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),\r\n            shape: const RoundedRectangleBorder(\r\n              borderRadius: BorderRadius.all(\r\n                Radius.circular(10),\r\n              ),\r\n            ),\r\n          ),\r\n          child: Text(\r\n            \"Add New Task\",\r\n            style: Theme.of(context).textTheme.titleMedium,\r\n          ),\r\n        ),\r\n        const SizedBox(\r\n          height: 45,\r\n        )\r\n      ],\r\n    );\r\n  }\r\n}<\/pre>\n<h4>4. Creating New Task<\/h4>\n<p>Let\u2019s build the widget for New Task, which shows when the user clicks the \u201cAdd New Task\u201d button on the home page. It is a Statewide widget, as the page content needs to change as the user enters the task details.<\/p>\n<p>Here, we accept the function updateTotalTasksStatus, onAddTask, which we created in the above step through the constructor function.<\/p>\n<p><strong>We have defined variables such as:-<\/strong><\/p>\n<p><strong>a. _titleController &#8211;<\/strong> It is a TextEditingController that controls the title entered by the user.<br \/>\n<strong>b. _descriptionController &#8211;<\/strong> It is a TextEditingController that controls the description entered by the user.<br \/>\n<strong>c. _selectedDate &#8211;<\/strong> It stores the date chosen by the user.<br \/>\n<strong>d. _selectedCategory &#8211;<\/strong> It is the category for the task the user selects.<\/p>\n<p><strong>We have also created a few functions that we will be using here:-<\/strong><\/p>\n<p><strong>a. _presentDayPicker<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>\u2014This<\/strong> is used to display the showDatePicker widget, from which the user can select a date. Here,<\/span>\u00a0the initialDate is set to the current date. As the user picks a date, the value of variable_selectedDate is changed to the picked date.<\/p>\n<p><strong>b. addNewTask<\/strong><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\"><strong>\u2014<\/strong>This function is used to add the new task created by the user to the list. It checks whether the details entered by the user, such as title or selectedDate, are valid. If they are<\/span>, it adds the task to the list of tasks using the onAddTask, which we accepted through the constructor function. Otherwise, it shows an Alert dialog mentioning that the details entered by the user are invalid.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\n\r\nimport '.\/models\/task.dart';\r\nimport '.\/widgets\/custom_textfield.dart';\r\n\r\nclass NewTask extends StatefulWidget {\r\n  const NewTask(\r\n      {required this.updateTotalTasksStatus,\r\n      required this.onAddTask,\r\n      super.key});\r\n  final Function(Task) onAddTask;\r\n  final Function updateTotalTasksStatus;\r\n  @override\r\n  State&lt;NewTask&gt; createState() {\r\n    return _NewTaskState();\r\n  }\r\n}\r\n\r\nclass _NewTaskState extends State&lt;NewTask&gt; {\r\n  final _titleController = TextEditingController();\r\n  final _descriptionController = TextEditingController();\r\n\r\n  DateTime? _selectedDate;\r\n  Category _selectedCategory = Category.work;\r\n\r\n  void _presentDayPicker() async {\r\n    var now = DateTime.now();\r\n    final initialDate = DateTime(now.year - 1, now.month, now.day);\r\n    var _pickedDate = await showDatePicker(\r\n        context: context,\r\n        initialDate: now,\r\n        firstDate: initialDate,\r\n        lastDate: now);\r\n    setState(() {\r\n      _selectedDate = _pickedDate;\r\n    });\r\n  }\r\n\r\n  void addNewTask() {\r\n    var isValidTitle = _titleController.text.isNotEmpty;\r\n\r\n    if (isValidTitle &amp;&amp; _selectedDate != null) {\r\n      widget.onAddTask(Task(\r\n          title: _titleController.text,\r\n          description: _descriptionController.text,\r\n          isCompleted: false,\r\n          date: _selectedDate!,\r\n          category: _selectedCategory));\r\n      widget.updateTotalTasksStatus();\r\n      Navigator.pop(context);\r\n    } else {\r\n      showDialog(\r\n        context: context,\r\n        builder: (context) {\r\n          return AlertDialog(\r\n            title: const Text('Invalid Input'),\r\n            content: const Text(\r\n                'Please make sure a valid title, date and category was enterd!'),\r\n            actions: [\r\n              TextButton(\r\n                  onPressed: () {\r\n                    Navigator.pop(context);\r\n                  },\r\n                  child: const Text('Cancel'))\r\n            ],\r\n          );\r\n        },\r\n      );\r\n    }\r\n  }<\/pre>\n<p>In the build function, we have defined\u00a0<span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">a variable, keyboardSpace, to store the height of the keyboard that pops up when a user tries to enter any detail. We have also defined categoryDropDown, which is a widget that shows the\u00a0<strong>Dropdown Button<\/strong><\/span>\u00a0from which the user can select the category for the task.<\/p>\n<p>Finally, in the build function, we are returning a Column widget inside the SingleChildScrollView widget. In the column widget, we show the task\u2019s title using a custom field, which we will build in the next step. Below it, we display the categoryDropDown widget we just created and an IconButton, which executes the _presentDayPicker function through which the user can select the date. Below it, we show a TextField through which the user can enter the note\/description about the task.<\/p>\n<p>Finally, at the bottom, we are showing an Elevated button, which, when pressed, executes the addNewTask function and adds the newly created task to the list.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">@override\r\n  Widget build(BuildContext context) {\r\n    final keyboardSpace = MediaQuery.of(context).viewInsets.bottom;\r\n    Widget categoryDropDown = Container(\r\n      width: 150,\r\n      decoration: BoxDecoration(\r\n          border: Border.all(color: Colors.grey),\r\n          borderRadius: BorderRadius.circular(5)),\r\n      child: DropdownButton(\r\n        padding: const EdgeInsets.fromLTRB(8, 0, 10, 0),\r\n        value: _selectedCategory,\r\n        items: Category.values\r\n            .map(\r\n              (category) =&gt; DropdownMenuItem(\r\n                value: category,\r\n                child: Text(\r\n                  category.name.toUpperCase(),\r\n                ),\r\n              ),\r\n            )\r\n            .toList(),\r\n        onChanged: (value) {\r\n          setState(\r\n            () {\r\n              if (value == null) {\r\n                return;\r\n              }\r\n              setState(\r\n                () {\r\n                  _selectedCategory = value;\r\n                },\r\n              );\r\n            },\r\n          );\r\n        },\r\n      ),\r\n    );\r\n    return SingleChildScrollView(\r\n      child: Padding(\r\n        padding: EdgeInsets.fromLTRB(16, 48, 16, keyboardSpace + 16),\r\n        child: Column(\r\n          children: [\r\n            CustomTextField(\r\n              hintText: 'Title',\r\n              title: 'Title',\r\n              maxLines: 1,\r\n              controller: _titleController,\r\n            ),\r\n            const SizedBox(\r\n              height: 15,\r\n            ),\r\n            Row(\r\n              children: [\r\n                categoryDropDown,\r\n                const SizedBox(\r\n                  width: 50,\r\n                ),\r\n                Expanded(\r\n                  child: Padding(\r\n                    padding: const EdgeInsets.all(8),\r\n                    child: Row(\r\n                      children: [\r\n                        Text(\r\n                          _selectedDate == null\r\n                              ? 'No Date Selected!'\r\n                              : formatter.format(_selectedDate!),\r\n                        ),\r\n                        IconButton(\r\n                            onPressed: _presentDayPicker,\r\n                            icon: const Icon(Icons.calendar_month)),\r\n                      ],\r\n                    ),\r\n                  ),\r\n                ),\r\n              ],\r\n            ),\r\n            const SizedBox(\r\n              height: 25,\r\n            ),\r\n            CustomTextField(\r\n              hintText: 'Notes',\r\n              title: 'Notes',\r\n              maxLines: 6,\r\n              controller: _descriptionController,\r\n            ),\r\n            const SizedBox(\r\n              height: 20,\r\n            ),\r\n            ElevatedButton(\r\n              style: ElevatedButton.styleFrom(\r\n                  backgroundColor: Theme.of(context).primaryColor),\r\n              onPressed: addNewTask,\r\n              child: Padding(\r\n                padding:\r\n                    const EdgeInsets.symmetric(horizontal: 20, vertical: 8),\r\n                child: Text(\r\n                  'Save',\r\n                  style: Theme.of(context).textTheme.titleMedium,\r\n                ),\r\n              ),\r\n            ),\r\n            const SizedBox(\r\n              height: 10,\r\n            )\r\n          ],\r\n        ),\r\n      ),\r\n    );\r\n  }\r\n}<\/pre>\n<h4>5. Helper Widgets Used in the App<\/h4>\n<p><strong>A. Building Task Card<\/strong><\/p>\n<p>This widget shows individual tasks on the home screen. It is a Stateful Widget, as the user can mark it completed when it is done.<\/p>\n<p>Here, we accept the function <strong>updateTaskStatus<\/strong>, which updates the variables when the user marks the task completed, and currentTask, which is the Task to be displayed on the card, through the constructor function.<\/p>\n<p>In the build function, we are returning the Card widget to give it a nice look. Inside it, we are using the Padding widget to add padding to the content. We are returning the Row widget as the child to the Padding widget. Inside the Row widget, we are returning an Icon for the task category, a Column widget to show the title and date of the task and a Checkbox through which the user can mark the task as completed or remaining.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\nimport 'package:to_do_app\/models\/task.dart';\r\n\r\nclass TaskCard extends StatefulWidget {\r\n  const TaskCard(\r\n      {required this.updateTaskStatus, required this.currentTask, super.key});\r\n\r\n  final Task currentTask;\r\n  final Function updateTaskStatus;\r\n\r\n  @override\r\n  State&lt;TaskCard&gt; createState() =&gt; _TaskCardState();\r\n}\r\n\r\nclass _TaskCardState extends State&lt;TaskCard&gt; {\r\n  @override\r\n  Widget build(BuildContext context) {\r\n    return Card(\r\n      color: Theme.of(context).primaryColor,\r\n      child: Padding(\r\n        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),\r\n        child: Row(\r\n          children: [\r\n            Icon(\r\n              categoryIcons[widget.currentTask.category],\r\n              color: Color.fromARGB(255, 235, 232, 232),\r\n            ),\r\n            const SizedBox(\r\n              width: 10,\r\n            ),\r\n            Column(\r\n              crossAxisAlignment: CrossAxisAlignment.start,\r\n              children: [\r\n                Text(\r\n                  widget.currentTask.title,\r\n                  style: Theme.of(context).textTheme.displayMedium,\r\n                ),\r\n                const SizedBox(\r\n                  height: 10,\r\n                ),\r\n                Text(widget.currentTask.formattedDate)\r\n              ],\r\n            ),\r\n            const Spacer(),\r\n            Checkbox(\r\n              value: widget.currentTask.isCompleted,\r\n              onChanged: (value) {\r\n                setState(() {\r\n                  widget.currentTask.isCompleted = value!;\r\n                });\r\n                widget.updateTaskStatus();\r\n              },\r\n            ),\r\n          ],\r\n        ),\r\n      ),\r\n    );\r\n  }\r\n}<\/pre>\n<p><strong>B. Creating a Tasks List<\/strong><\/p>\n<p>This widget shows the list of tasks on the home page. Here, we are importing the Task model we created above and the TaskCard widget. It is a Stateful widget, as the number of tasks can vary, and the list needs to be updated.<\/p>\n<p>In the builder function, we are returning the ListView.builder, where the itemCount parameter is set to the number of tasks. Through the itemBuilder parameter, we are returning the TaskCard we created above wrapped around the Dismissible widget so that the user can swipe to delete the task.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\n\r\nimport '..\/models\/task.dart';\r\nimport 'task_card.dart';\r\n\r\nclass TasksList extends StatefulWidget {\r\n  const TasksList(\r\n      {required this.onRemoveTask,\r\n      required this.updateTaskStatus,\r\n      required this.tasks,\r\n      super.key});\r\n  final List&lt;Task&gt; tasks;\r\n  final Function updateTaskStatus;\r\n  final Function(Task) onRemoveTask;\r\n\r\n  @override\r\n  State&lt;TasksList&gt; createState() {\r\n    return _TasksListState();\r\n  }\r\n}\r\n\r\nclass _TasksListState extends State&lt;TasksList&gt; {\r\n  @override\r\n  Widget build(BuildContext context) {\r\n    return ListView.builder(\r\n      itemCount: widget.tasks.length,\r\n      itemBuilder: (ctx, index) {\r\n        return Container(\r\n          margin: const EdgeInsets.symmetric(horizontal: 30, vertical: 5),\r\n          child: Dismissible(\r\n            key: ValueKey(widget.tasks[index]),\r\n            onDismissed: (direction) {\r\n              widget.onRemoveTask(widget.tasks[index]);\r\n            },\r\n            child: TaskCard(\r\n                updateTaskStatus: widget.updateTaskStatus,\r\n                currentTask: widget.tasks[index]),\r\n          ),\r\n        );\r\n      },\r\n    );\r\n  }\r\n}<\/pre>\n<p><strong>C. Building Custom TextField Widget<\/strong><\/p>\n<p>Here, we are creating a custom TextField. We accept controller, hintText, title, maxLines, suffixIcon, and readOnly, which are the parameters for TextField through the constructor function.<\/p>\n<p>In the build function, we are returning a Column widget, inside which there is a Text widget showing the title and a TextField with the parameters we accepted in the constructor function. Both of these are separated using a SizedBox.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">import 'package:flutter\/material.dart';\r\n\r\nclass CustomTextField extends StatelessWidget {\r\n  const CustomTextField({\r\n    super.key,\r\n    this.controller,\r\n    required this.hintText,\r\n    required this.title,\r\n    this.maxLines,\r\n    this.suffixIcon,\r\n    this.readOnly = false,\r\n  });\r\n  final TextEditingController? controller;\r\n  final String hintText;\r\n  final String title;\r\n  final int? maxLines;\r\n  final Widget? suffixIcon;\r\n  final bool readOnly;\r\n\r\n  @override\r\n  Widget build(BuildContext context) {\r\n    return Column(\r\n      crossAxisAlignment: CrossAxisAlignment.stretch,\r\n      children: [\r\n        Text(\r\n          title,\r\n          style: Theme.of(context).textTheme.titleMedium,\r\n        ),\r\n        const SizedBox(height: 10),\r\n        TextField(\r\n          readOnly: readOnly,\r\n          onTapOutside: (event) {\r\n            FocusManager.instance.primaryFocus?.unfocus();\r\n          },\r\n          autocorrect: false,\r\n          controller: controller,\r\n          decoration: InputDecoration(\r\n            border: const OutlineInputBorder(),\r\n            focusedBorder: OutlineInputBorder(\r\n              borderSide:\r\n                  BorderSide(color: Theme.of(context).primaryColor, width: 2),\r\n            ),\r\n            hintText: hintText,\r\n            suffixIcon: suffixIcon,\r\n          ),\r\n          maxLines: maxLines,\r\n        ),\r\n      ],\r\n    );\r\n  }\r\n}<\/pre>\n<h3>Flutter To-Do App Output<\/h3>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-To-Do-App-Output-2.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89689\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-To-Do-App-Output-2.webp\" alt=\"Flutter To Do App Output\" width=\"400\" height=\"828\" \/><\/a><\/p>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-To-Do-App-.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89690\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-To-Do-App-.webp\" alt=\"Flutter To Do App\" width=\"400\" height=\"833\" \/><\/a><\/p>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/output-Flutter-To-Do-App-.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89691\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/output-Flutter-To-Do-App-.webp\" alt=\"output Flutter To Do App \" width=\"400\" height=\"817\" \/><\/a><\/p>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-output-To-Do-App.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89692\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/Flutter-output-To-Do-App.webp\" alt=\"Flutter Output To Do App \" width=\"400\" height=\"825\" \/><\/a><\/p>\n<p><a href=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/flutter-to-do-app-invalid-input-1-1.webp\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-89695\" src=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/11\/flutter-to-do-app-invalid-input-1-1.webp\" alt=\"flutter to do app invalid input\" width=\"400\" height=\"849\" \/><\/a><\/p>\n<h3>Conclusion<\/h3>\n<p>Finally, you have your Flutter To-Do App Project ready! Now, you can easily manage your tasks. Through this Flutter To-Do App Project, we learned about many exciting widgets. We used the CheckBox widget to oversee the completion of tasks, the DropDown button to select a category, the Dismissible widget to delete functions with a swipe and much more. We also saw how to dynamically update the variables shown on the home page, like total tasks, completed tasks, etc., as the user adds or deletes a task.<\/p>\n<p>I hope you enjoyed working on this project!<br \/>\nThank you for reading! Keep Learning Flutter!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our daily lives, we have to work on several tasks, whether it is picking up groceries, completing homework, or finishing a presentation for work. Sometimes, it is difficult to keep track of all&#46;&#46;&#46;<\/p>\n","protected":false},"author":6,"featured_media":447279,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5557],"tags":[5547,5546,5542,484,431,432,433,485,5540,486,5549,5548],"class_list":["post-89479","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-flutter-tutorials","tag-create-flutter-to-do-app","tag-create-to-do-app-using-flutter","tag-flutter","tag-flutter-project","tag-flutter-project-for-beginners","tag-flutter-project-for-practice","tag-flutter-project-ideas","tag-flutter-to-do-app-project","tag-learn-flutter","tag-learn-flutter-project","tag-to-do-app-in-flutter","tag-to-do-application"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Flutter Project - To Do App - TechVidvan<\/title>\n<meta name=\"description\" content=\"The Flutter To-Do App will track all the tasks and their deadlines and show how many have been completed and how many remain.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Flutter Project - To Do App - TechVidvan\" \/>\n<meta property=\"og:description\" content=\"The Flutter To-Do App will track all the tasks and their deadlines and show how many have been completed and how many remain.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/\" \/>\n<meta property=\"og:site_name\" content=\"TechVidvan\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/TechVidvan\/\" \/>\n<meta property=\"article:published_time\" content=\"2024-10-28T12:30:14+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-03T09:28:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"TechVidvan Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@vidvantech\" \/>\n<meta name=\"twitter:site\" content=\"@vidvantech\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"TechVidvan Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Flutter Project - To Do App - TechVidvan","description":"The Flutter To-Do App will track all the tasks and their deadlines and show how many have been completed and how many remain.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/","og_locale":"en_US","og_type":"article","og_title":"Flutter Project - To Do App - TechVidvan","og_description":"The Flutter To-Do App will track all the tasks and their deadlines and show how many have been completed and how many remain.","og_url":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/","og_site_name":"TechVidvan","article_publisher":"https:\/\/www.facebook.com\/TechVidvan\/","article_published_time":"2024-10-28T12:30:14+00:00","article_modified_time":"2026-06-03T09:28:06+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp","type":"image\/webp"}],"author":"TechVidvan Team","twitter_card":"summary_large_image","twitter_creator":"@vidvantech","twitter_site":"@vidvantech","twitter_misc":{"Written by":"TechVidvan Team","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#article","isPartOf":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/"},"author":{"name":"TechVidvan Team","@id":"https:\/\/techvidvan.com\/tutorials\/#\/schema\/person\/dde481bb412350cde1ed6e389bc0deaf"},"headline":"Flutter Project &#8211; To Do App","datePublished":"2024-10-28T12:30:14+00:00","dateModified":"2026-06-03T09:28:06+00:00","mainEntityOfPage":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/"},"wordCount":1839,"commentCount":0,"publisher":{"@id":"https:\/\/techvidvan.com\/tutorials\/#organization"},"image":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#primaryimage"},"thumbnailUrl":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp","keywords":["create flutter to do app","create to do app using flutter","flutter","flutter project","flutter project for beginners","flutter project for practice","flutter project ideas","flutter to-do app project","learn flutter","learn flutter project","to do app in flutter","to do application"],"articleSection":["Flutter Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/","url":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/","name":"Flutter Project - To Do App - TechVidvan","isPartOf":{"@id":"https:\/\/techvidvan.com\/tutorials\/#website"},"primaryImageOfPage":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#primaryimage"},"image":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#primaryimage"},"thumbnailUrl":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp","datePublished":"2024-10-28T12:30:14+00:00","dateModified":"2026-06-03T09:28:06+00:00","description":"The Flutter To-Do App will track all the tasks and their deadlines and show how many have been completed and how many remain.","breadcrumb":{"@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#primaryimage","url":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp","contentUrl":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/11\/todo-flutter.webp","width":1200,"height":628,"caption":"todo flutter"},{"@type":"BreadcrumbList","@id":"https:\/\/techvidvan.com\/tutorials\/flutter-to-do-app\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/techvidvan.com\/tutorials\/"},{"@type":"ListItem","position":2,"name":"Flutter Project &#8211; To Do App"}]},{"@type":"WebSite","@id":"https:\/\/techvidvan.com\/tutorials\/#website","url":"https:\/\/techvidvan.com\/tutorials\/","name":"TechVidvan Blogs","description":"","publisher":{"@id":"https:\/\/techvidvan.com\/tutorials\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/techvidvan.com\/tutorials\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/techvidvan.com\/tutorials\/#organization","name":"TechVidvan","url":"https:\/\/techvidvan.com\/tutorials\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/techvidvan.com\/tutorials\/#\/schema\/logo\/image\/","url":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/03\/techvidvan-logo-200x50-1.webp","contentUrl":"https:\/\/techvidvan.com\/tutorials\/wp-content\/uploads\/2024\/03\/techvidvan-logo-200x50-1.webp","width":200,"height":50,"caption":"TechVidvan"},"image":{"@id":"https:\/\/techvidvan.com\/tutorials\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/TechVidvan\/","https:\/\/x.com\/vidvantech"]},{"@type":"Person","@id":"https:\/\/techvidvan.com\/tutorials\/#\/schema\/person\/dde481bb412350cde1ed6e389bc0deaf","name":"TechVidvan Team"}]}},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/posts\/89479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/comments?post=89479"}],"version-history":[{"count":5,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/posts\/89479\/revisions"}],"predecessor-version":[{"id":448007,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/posts\/89479\/revisions\/448007"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/media\/447279"}],"wp:attachment":[{"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/media?parent=89479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/categories?post=89479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techvidvan.com\/tutorials\/wp-json\/wp\/v2\/tags?post=89479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}