TaskFlow is a study project built to practice Flutter, GetX, REST API integration, Git, Clean Code, project architecture and C#.
The project is being developed as a small full-stack task management application, with a Flutter mobile app consuming an ASP.NET Core REST API.
| Task List | Create Task |
|---|---|
![]() |
![]() |
| Edit Task | Delete Confirmation |
|---|---|
![]() |
![]() |
| Swagger | Completed Filter |
|---|---|
![]() |
![]() |
- C#
- ASP.NET Core Web API
- Entity Framework Core
- SQLite
- Swagger / OpenAPI
- REST API
- DTOs
- Service layer
- Dependency Injection
- Async/Await
- Flutter
- Dart
- GetX
The Flutter app will be added after the API is completed.
- Create tasks
- List tasks
- Filter tasks by status
- Get task by ID
- Update tasks
- Mark tasks as completed
- Reopen completed tasks
- Delete tasks
- Request validation
- Standardized not found responses
- Swagger documentation
- SQLite persistence with Entity Framework Core
TaskFlow/
├── TaskFlow.sln
├── README.md
└── TaskFlow.api/
├── Controllers/
│ └── TasksController.cs
├── Data/
│ └── AppDbContext.cs
├── DTOs/
│ ├── CreateTaskRequest.cs
│ ├── UpdateTaskRequest.cs
│ ├── TaskResponse.cs
│ └── ErrorResponse.cs
├── Enums/
│ └── TaskStatusFilter.cs
├── Models/
│ └── TaskItem.cs
├── Services/
│ ├── ITaskService.cs
│ └── TaskService.cs
├── Migrations/
├── Program.cs
├── appsettings.json
└── TaskFlow.api.csproj
- .NET SDK
- Entity Framework Core CLI tool
Check your .NET version:
dotnet --versionInstall the Entity Framework Core CLI tool if needed:
dotnet tool install --global dotnet-efFrom the repository root, enter the API project folder:
cd TaskFlow.apiRestore dependencies:
dotnet restoreApply database migrations:
dotnet ef database updateRun the API:
dotnet runThe API should be available at:
http://localhost:5099
Swagger documentation:
http://localhost:5099/swagger
The API uses SQLite.
The local database file is generated after running:
dotnet ef database updateThe database file is not committed to Git. It can be recreated from the migrations.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/tasks |
List all tasks |
| GET | /api/tasks?status=All |
List all tasks |
| GET | /api/tasks?status=Pending |
List pending tasks |
| GET | /api/tasks?status=Completed |
List completed tasks |
| GET | /api/tasks/{id} |
Get a task by ID |
| POST | /api/tasks |
Create a new task |
| PUT | /api/tasks/{id} |
Update a task |
| PATCH | /api/tasks/{id}/complete |
Mark a task as completed |
| PATCH | /api/tasks/{id}/reopen |
Reopen a completed task |
| DELETE | /api/tasks/{id} |
Delete a task |
POST /api/tasks
Content-Type: application/json{
"title": "Study C#",
"description": "Practice ASP.NET Core Web API"
}PUT /api/tasks/{id}
Content-Type: application/json{
"title": "Study C# and EF Core",
"description": "Practice API persistence with SQLite",
"isCompleted": true
}{
"id": "7e3f42f4-7e6a-41e1-97e3-8e4d25a0f15c",
"title": "Study C#",
"description": "Practice ASP.NET Core Web API",
"isCompleted": false,
"createdAt": "2026-06-15T10:30:00Z",
"updatedAt": null
}{
"message": "Task not found."
}This project follows an incremental development approach:
- Initial API setup
- Swagger configuration
- In-memory CRUD
- DTOs for request and response objects
- Request validation
- Service layer
- SQLite persistence with Entity Framework Core
- Async/Await refactor
- Task status endpoints
- Task filtering
- Standardized error responses
- Swagger documentation improvements
The project uses organized commits such as:
chore: initialize repository
feat(api): create ASP.NET Core Web API project
feat(api): configure Swagger UI
feat(api): add in-memory task CRUD endpoints
refactor(api): use DTOs for task requests and responses
feat(api): add task request validation
refactor(api): move task logic to service layer
feat(api): persist tasks with SQLite and EF Core
refactor(api): use async methods in task service
feat(api): add task completion endpoints
feat(api): add task status filter
feat(api): improve not found error responses
docs(api): improve Swagger documentation
docs: add API setup instructions
The Flutter app is located inside:
taskflow_app/
- Flutter SDK
- Android Studio or VS Code
- Android device/emulator or iOS Simulator
- TaskFlow API running locally
Check your Flutter installation:
flutter doctorInstall Flutter dependencies:
cd taskflow_app
flutter pub getThe app consumes the ASP.NET Core API using Dio.
The API base URL is configured through --dart-define:
flutter run --dart-define=API_BASE_URL=http://YOUR_API_HOST:5099When using a physical Android device, localhost does not point to your computer. It points to the phone itself.
First, find your Mac local IP:
ipconfig getifaddr en0Example output:
192.168.0.25
Run the API allowing external network access:
cd TaskFlow.api
dotnet run --urls "http://0.0.0.0:5099"Then run the Flutter app:
cd taskflow_app
flutter run --dart-define=API_BASE_URL=http://192.168.0.25:5099Replace 192.168.0.25 with your actual Mac IP address.
Use:
flutter run --dart-define=API_BASE_URL=http://10.0.2.2:5099Use:
flutter run --dart-define=API_BASE_URL=http://localhost:5099This project can be launched through VS Code using .vscode/launch.json.
Example configuration for a physical Android device:
{
"name": "taskflow_app - Android físico",
"cwd": "taskflow_app",
"request": "launch",
"type": "dart",
"toolArgs": [
"--dart-define=API_BASE_URL=http://192.168.0.25:5099"
]
}Replace the IP address with your Mac local IP.
Because the project uses local HTTP during development, the Android app allows cleartext traffic in:
taskflow_app/android/app/src/main/AndroidManifest.xml
Inside the <application> tag:
android:usesCleartextTraffic="true"This is used only for local development.
-
List tasks from the API
-
Filter tasks by status:
- All
- Pending
- Completed
-
Create tasks
-
Edit tasks
-
Mark tasks as completed
-
Reopen completed tasks
-
Delete tasks with confirmation dialog
-
Loading states
-
Empty state
-
Error state
-
Pull to refresh
-
REST API integration with Dio
-
State management with GetX
-
Navigation with GetX routes
-
Dependency injection with GetX Bindings
The Flutter app follows a feature-based structure:
lib/
├── app/
│ ├── bindings/
│ ├── core/
│ │ ├── constants/
│ │ ├── errors/
│ │ └── http/
│ └── routes/
├── features/
│ └── tasks/
│ ├── data/
│ │ ├── models/
│ │ └── repositories/
│ └── presentation/
│ ├── bindings/
│ ├── controllers/
│ ├── pages/
│ └── widgets/
└── main.dart
Main flow:
Page
↓
GetX Controller
↓
Repository
↓
Dio Client
↓
ASP.NET Core API
- GetX
- Dio
The app uses String.fromEnvironment to read the API URL:
class ApiConstants {
static const String baseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'http://localhost:5099',
);
}This avoids hardcoding local IP addresses in the source code.





