Skip to content

Part 1: Packages and Setup

This guide uses the same local-first mindset as Linkbox: local database first, background sync second, UI always reactive.

Install Packages First

yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.6.1
  riverpod_annotation: ^2.6.1
  isar: ^3.1.0+1
  isar_flutter_libs: ^3.1.0+1
  path_provider: ^2.1.5
  connectivity_plus: ^6.1.0
  dio: ^5.7.0
  uuid: ^4.5.1
  intl: ^0.19.0
  freezed_annotation: ^2.4.4
  json_annotation: ^4.9.0

dev_dependencies:
  build_runner: ^2.4.14
  isar_generator: ^3.1.0+1
  riverpod_generator: ^2.6.3
  freezed: ^2.5.7
  json_serializable: ^6.9.0

Then run:

bash
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs

Bootstrap App (main.dart)

dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await initializeIsar();

  runApp(
    const ProviderScope(
      child: ExpenseTrackerApp(),
    ),
  );
}

Initialize Isar Once

dart
late Isar isar;

Future<void> initializeIsar() async {
  final dir = await getApplicationDocumentsDirectory();
  isar = await Isar.open(
    [ExpenseLocalSchema, SyncTaskLocalSchema],
    directory: dir.path,
    inspector: true,
  );
}

Why this setup works in production

  • Isar gives fast local reads and writes.
  • Riverpod gives predictable state and testability.
  • Connectivity stream helps orchestrate sync timing.
  • Code generation keeps models clean and typed.

Next: Part 2 - Folder Structure and Models