Skip to content

Part 2: Folder Structure and Models

Linkbox separates concerns well: core, data, features, providers, sync. We will apply the same for an expense app.

Suggested Folder Structure

text
lib/
  core/
    network/
    providers/
    services/
    utils/
  data/
    local/
      entities/
      isar_instance.dart
    remote/
      expense_remote_data_source.dart
    repositories/
      expense_repository.dart
    sync/
      sync_service.dart
      sync_state_manager.dart
  features/
    expenses/
      models/
      providers/
      ui/
        screens/
        widgets/
  main.dart

Expense Local Entity (Isar)

dart
import 'package:isar/isar.dart';

part 'expense_local.g.dart';

@collection
class ExpenseLocal {
  Id isarId = Isar.autoIncrement;

  @Index(unique: true, replace: true)
  late String id;

  @Index()
  late String userId;

  @Index()
  late DateTime spentAt;

  late String category;
  late double amount;
  String note = '';

  @Index()
  DateTime updatedAt = DateTime.now().toUtc();

  bool isDeleted = false;
  bool isSynced = false;
  DateTime? lastSyncedAt;

  static ExpenseLocal create({
    required String id,
    required String userId,
    required DateTime spentAt,
    required String category,
    required double amount,
    String? note,
  }) {
    return ExpenseLocal()
      ..id = id
      ..userId = userId
      ..spentAt = spentAt
      ..category = category
      ..amount = amount
      ..note = note ?? '';
  }
}

Sync Queue Entity

dart
@collection
class SyncTaskLocal {
  Id isarId = Isar.autoIncrement;

  @Index()
  late String entityId;

  late String entityType; // expense
  late String operation; // upsert | delete
  late String payloadJson;

  @Index()
  DateTime createdAt = DateTime.now().toUtc();

  int retryCount = 0;
}

Previous: Part 1 - Packages and Setup

Next: Part 3 - Isar Repository and Pagination