Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations

Hey, Flutter fam! Episode 4 of our Flutter x Serverpod series is here! Last time we integrated authentication: login, registration, reset/forgot password, validation code, and secure Home screen. If you’re signed in and staring at an empty task list ri…


This content originally appeared on DEV Community and was authored by Samuel Adekunle

Hey, Flutter fam! Episode 4 of our Flutter x Serverpod series is here! Last time we integrated authentication: login, registration, reset/forgot password, validation code, and secure Home screen. If you're signed in and staring at an empty task list right now… perfect. Today, we're filling it with secure CRUD operations for our fintech to-do app.

We're creating, reading, updating, and deleting trade alerts that are fully owned by the logged-in user, with validation to prevent anyone from entering a negative amount. By the end, your app will feel production-ready, complete with empty states, loading spinners, error toasts, and more. Let's build! Subscribe and let's code! 🚀

FOR BETTER UNDERSTANDING, REFER TO THE YOUTUBE VIDEO.

First, our Task model 

In fintech_todo_server/lib/src/task.spy.yaml:

class: Task

table: task

fields:
   id: int?
   title: String
   description: String
   amount: double
   dueDate: DateTime?
   userId: int

Create migration:

cd fintech_todo_server
serverpod create-migration

Apply

dart run bin/main.dart --role maintenance --apply-migrations

Done - Postgres now has a task table linked to the auth user table.

Building the Secure TaskEndpoint

File: fintech_todo_server/lib/src/task_endpoint.dart

import 'package:fintech_todo_server/src/exceptions.dart';
import 'package:fintech_todo_server/src/generated/protocol.dart';
import 'package:serverpod/serverpod.dart';

class TaskEndpoint extends Endpoint {
  @override
  bool get requireLogin => true;

  // CREATE
  Future<Task> createTask(Session session, Task request) async {
    final auth = await session.authenticated;

    if (auth == null) {
      throw AuthorizationException(message: "You're not authenticated!");
    }
    final userId = auth.userId;

    // Validation
    if (request.title.isEmpty) {
      throw ValidationException(message: "Title cannot be empty");
    }

    if (request.amount <= 0) {
      throw ValidationException(message: "Amount must be greater than zero");
    }

    final task = Task(
      userId: userId,
      title: request.title,
      amount: request.amount,
      description: request.description,
      dueDate: request.dueDate,
    );

    final inserted = await Task.db.insertRow(session, task);
    return inserted;
  }

  // READ
  Future<List<Task>> getTasks(Session session) async {
    final auth = await session.authenticated;

    if (auth == null) {
      throw AuthorizationException(message: "You're not authenticated!");
    }
    final userId = auth.userId;

    final tasks = await Task.db.find(
      session,
      where: (t) => t.userId.equals(userId),
      orderBy: (t) => t.dueDate,
      orderDescending: true,
    );
    return tasks;
  }

  // UPDATE
  Future<Task> updateTask(Session session, Task request) async {
    final auth = await session.authenticated;

    if (auth == null) {
      throw AuthorizationException(message: "You're not authenticated!");
    }
    final userId = auth.userId;

    if (request.id == null) {
      throw ValidationException(message: "Task ID is required for update");
    }

    if (request.title.isEmpty) {
      throw ValidationException(message: "Title cannot be empty");
    }

    if (request.amount <= 0) {
      throw ValidationException(message: "Amount must be greater than zero");
    }

    final existingTask = await Task.db.findById(session, request.id!);

    if (existingTask == null) {
      throw NotFoundException(message: "Task with ID ${request.id} not found.");
    }
    if (existingTask.userId != userId) {
      throw AuthorizationException(
          message: "You do not have permission to update this task.");
    }

    final updatedTask = existingTask.copyWith(
      title: request.title,
      amount: request.amount,
      description: request.description,
      dueDate: request.dueDate,
    );

    final result = await Task.db.updateRow(session, updatedTask);
    return result;
  }

  // DELETE
  Future<void> deleteTask(Session session, int taskId) async {
    final auth = await session.authenticated;

    if (auth == null) {
      throw AuthorizationException(message: "You're not authenticated!");
    }
    final userId = auth.userId;

    final rowsDeleted = await Task.db.deleteWhere(
      session,
      where: (t) => t.id.equals(taskId) & t.userId.equals(userId),
    );

    if (rowsDeleted.isEmpty) {
      throw NotFoundException(message: "Task with ID $taskId not found.");
    }
  }
}

Regenerate client:

serverpod generate

Flutter Side: Beautiful Task List + Forms

Switch to fintech_todo_flutter

Key screens we built live:
TaskListScreen - Empty state + ListView.builder
TaskFormDialog - Create/Edit modal
Delete confirmation

Code highlights (copy-paste ready in description):

// In HomeScreen
Future<void> _loadTasks() async {
  try {
    final tasks = await client.task.getTasks();
    setState(() => taskList = tasks);
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Load failed: $e')));
  }
}

// FAB → showDialog(TaskFormDialog())
Real demo flow:
Login → Empty state ("No trades yet - add one!")
Tap FAB → Fill form → Save → Task appears instantly
Edit → Update → List refreshes
Delete → Confirmation → Gone 

That's it - you now have a fully secure, user-owned task system! No more public todos - every task belongs to the signed-in trader.
I hope you have learn something incredible. Press that follow button if you're not following me yet. Also, make sure to subscribe to the newsletter so you're notified when I publish a new article. Kindly press the clap button as many times as you want if you enjoy it, and feel free to ask a question.

Source Code 👇 - Show some ❤️ by starring ⭐ the repo and follow me 😄!

GitHub logo techwithsam / fintech_todo_serverpod

Full-Stack Mobile Development (Flutter + Serverpod) - TechWithSam

Full-Stack with Flutter x Serverpod | Tech With Sam

Youtube Serverpod Flutter GitHub stars GitHub TechWithSam

Overview

Top question on Stack Overflow this year: 'How do I build a backend without leaving Dart?' Enter Serverpod—the open-source powerhouse that's making full-stack Dart the 2025 must-have. In this new series, we're building a real-world fintech to-do app from scratch: secure tasks, real-time updates, and cloud-ready deploys. No more half-solutions!

Youtube Banner

[Course] Full-Stack Mobile Development With Flutter and Serverpod - Watch on youtube

Project layout

  • fintech_todo_server/ — Serverpod server
    • bin/main.dart — server entrypoint
    • Dockerfile — for containerized deploys
    • migrations/ — DB migrations
    • lib/src/generated/ — generated protocol & endpoints
  • fintech_todo_flutter/ — Flutter client
    • lib/main.dart — app entrypoint
  • fintech_todo_client/ — generated client package

Quick start

Prereqs: Docker, Flutter, Dart SDK (for server).

  1. Clone:

    git clone https://github.com/techwithsam/fintech_todo_serverpod
    cd fintech_todo
  2. Start local DB & Redis (example using docker-compose):

    cd fintech_todo_server && docker compose up -d
  3. Run the server:

    # from fintech_todo/
    dart pub get
    dart

Drop a like if this helped, comment your experience with Serverpod and this series, and join Discord for the updated repo + bonus snippets: https://discord.gg/NytgTkyw3R

Samuel Adekunle, Tech With Sam YouTube | Part 5Teaser

Happy Building! 🥰👨‍💻


This content originally appeared on DEV Community and was authored by Samuel Adekunle


Print Share Comment Cite Upload Translate Updates
APA

Samuel Adekunle | Sciencx (2025-11-22T07:00:00+00:00) Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations. Retrieved from https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/

MLA
" » Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations." Samuel Adekunle | Sciencx - Saturday November 22, 2025, https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/
HARVARD
Samuel Adekunle | Sciencx Saturday November 22, 2025 » Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations., viewed ,<https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/>
VANCOUVER
Samuel Adekunle | Sciencx - » Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/
CHICAGO
" » Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations." Samuel Adekunle | Sciencx - Accessed . https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/
IEEE
" » Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations." Samuel Adekunle | Sciencx [Online]. Available: https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/. [Accessed: ]
rf:citation
» Full-Stack Mobile Development (Flutter + Serverpod) #4 - Task CRUD Operations | Samuel Adekunle | Sciencx | https://www.scien.cx/2025/11/22/full-stack-mobile-development-flutter-serverpod-4-task-crud-operations/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.