Flutter Todo app step-by-step

Ahmed Alwaeli
5 min readSep 27, 2023

--

Creating a Flutter Todo app step by step involves several tasks, including setting up the project, creating screens, implementing data models, managing state, and handling navigation. I’ll provide you with a simplified example of building a basic Todo app. Note that this is a simple example, and real-world applications may require additional features and optimizations.

Here’s a step-by-step guide:

Designing the file structure for a Flutter Todo app:
Designing the file structure for a Flutter Todo app involves organizing your project into folders and files to maintain a clean and organized codebase. Here’s a common file structure for a Flutter Todo app:

my_todo_app/
├── lib/
│ ├── main.dart
│ ├── screens/
│ │ ├── home_screen.dart
│ │ ├── add_task_screen.dart
│ │ └── task_detail_screen.dart
│ ├── widgets/
│ │ ├── task_list.dart
│ │ ├── task_tile.dart
│ │ └── ... (other reusable widgets)
│ ├── models/
│ │ ├── task.dart
│ │ └── ... (other data models)
│ ├── providers/
│ │ ├── task_provider.dart
│ │ └── ... (other providers if needed)
│ ├── database/
│ │ ├── database.dart
│ │ └── ... (other database-related files)
│ ├── utils/
│ │ ├── date_utils.dart
│ │ └── ... (other utility functions)
│ ├── routes/
│ │ ├── app_routes.dart
│ │ └── ... (other route-related files)
│ └── constants/
│ ├── app_constants.dart
│ └── ... (other constants)
├── assets/
│ ├── images/
│ │ ├── todo_logo.png
│ │ └── ... (other images)
│ └── ... (other assets)
├── test/
│ ├── unit/
│ │ └── ... (unit test files)
│ ├── widgets/
│ │ └── ... (widget test files)
└── pubspec.yaml

Explanation of the key folders and files:

  • lib/main.dart: The entry point of the Flutter app.
  • lib/screens/: Contains Dart files for different screens of your app, like the home screen, add task screen and task detail screen.
  • lib/widgets/: Contains reusable widgets used across multiple screens.
  • lib/models/: Defines data models, such as the Task class.
  • lib/providers/: Manages state and business logic using providers, including TaskProvider.
  • lib/database/: Contains code for managing the app’s database (if used).
  • lib/utils/: Houses utility functions and helper methods.
  • lib/routes/: Defines the app’s routes and navigation logic.
  • lib/constants/: Stores constant values used throughout the app.
  • assets/: Holds static assets like images.
  • test/: Contains unit and widget tests for your app.

You can further organize and split your code into more folders and subfolders as your app grows. This structure provides a good starting point for a Flutter Todo app, and you can expand and customize it according to your project’s needs.

Creating a Flutter Todo app step by step involves several tasks, including setting up the project, creating screens, implementing data models, managing state, and handling navigation. I’ll provide you with a simplified example of building a basic Todo app. Note that this is a simple example, and real-world applications may require additional features and optimizations.

Step 1: Set Up a Flutter Project

  1. Create a new Flutter project using the Flutter CLI:
flutter create my_todo_app

2. Open the project in your preferred code editor.

Step 2: Define Data Models

In your lib/models/ folder, create a file named task.dart to define the Task data model:

class Task {
final String id;
final String title;
final bool isDone;

Task({
required this.id,
required this.title,
required this.isDone,
});
}

Step 3: Create Screens

Create the following screens in the lib/screens/ folder:

Home Screen (home_screen.dart)

This screen displays the list of tasks.

import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo List'),
),
body: // Implement the task list here
);
}
}

Add Task Screen (add_task_screen.dart)

This screen allows users to add new tasks.

import 'package:flutter/material.dart';

class AddTaskScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Task'),
),
body: // Implement the form to add a task here
);
}
}

Task Detail Screen (task_detail_screen.dart)

This screen shows the details of a specific task.

import 'package:flutter/material.dart';

class TaskDetailScreen extends StatelessWidget {
final String taskId;

TaskDetailScreen({required this.taskId});

@override
Widget build(BuildContext context) {
// Fetch the task details based on taskId and display them
}
}

Step 4: Create Widgets

In the lib/widgets/ folder, create a task_tile.dart widget to represent a task item in the list:

import 'package:flutter/material.dart';

class TaskTile extends StatelessWidget {
final String title;
final bool isDone;
final Function(bool) onChanged;

TaskTile({
required this.title,
required this.isDone,
required this.onChanged,
});

@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
leading: Checkbox(
value: isDone,
onChanged: onChanged,
),
);
}
}

Step 5: Implement State Management

In the lib/providers/ folder, create a task_provider.dart file to manage the state of tasks using the ChangeNotifier pattern:

import 'package:flutter/foundation.dart';
import '../models/task.dart';

class TaskProvider with ChangeNotifier {
List<Task> _tasks = [];

List<Task> get tasks {
return [..._tasks];
}

void addTask(Task task) {
_tasks.add(task);
notifyListeners();
}

// Implement methods to update and delete tasks

// You can also add methods for fetching and managing tasks
}

Step 6: Implement the Home Screen

In the lib/screens/home_screen.dart file, create the HomeScreen widget and use the TaskProvider to display the list of tasks:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/task_provider.dart';
import '../widgets/task_tile.dart';

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tasks = Provider.of<TaskProvider>(context).tasks;

return Scaffold(
appBar: AppBar(
title: Text('Todo List'),
),
body: ListView.builder(
itemCount: tasks.length,
itemBuilder: (ctx, index) {
final task = tasks[index];
return TaskTile(
title: task.title,
isDone: task.isDone,
onChanged: (value) {
// Implement task completion logic
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pushNamed('/add-task');
},
child: Icon(Icons.add),
),
);
}
}

Step 7: Implement Navigation and Add Task Screen

In the lib/main.dart file, define your app's routes, and create the TaskProvider:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/home_screen.dart';
import 'screens/add_task_screen.dart';
import 'providers/task_provider.dart';

void main() {
runApp(MyTodoApp());
}

class MyTodoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TaskProvider(),
child: MaterialApp(
title: 'Todo App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/',
routes: {
'/': (ctx) => HomeScreen(),
'/add-task': (ctx) => AddTaskScreen(),
// Add more routes if needed
},
),
);
}
}

Step 8: Add Task Functionality

In the lib/screens/add_task_screen.dart file, implement the logic for adding a new task:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/task_provider.dart';

class AddTaskScreen extends StatelessWidget {
final TextEditingController _taskController = TextEditingController();

void _addTask(BuildContext context) {
final taskText = _taskController.text;
if (taskText.isNotEmpty) {
Provider.of<TaskProvider>(context, listen: false).addTask(
Task(
id: DateTime.now().toString(),
title: taskText,
isDone: false,
),
);
Navigator.of(context).pop();
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Task'),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
controller: _taskController,
decoration: InputDecoration(labelText: 'Task'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => _addTask(context),
child: Text('Add Task'),
),
],
),
),
);
}
}

Step 9: Running the App

You can now run your Flutter Todo app using the flutter run command. Make sure to connect a device or use an emulator.

This is a simplified example to get you started with a basic Todo app. You can enhance it by adding features like task editing, task deletion, task completion, and data persistence to make it more functional. Additionally, you can apply custom styling to improve the app’s appearance.

--

--