Flutter & Dart Frog¶
KanjiIQ uses Flutter for the frontend and Dart Frog for the backend — a unified Dart ecosystem that simplifies development, testing, and deployment.
Flutter Web¶
Why Flutter?¶
- Single codebase: Web, iOS, Android, and desktop from one source
- Material Design 3: Native-feeling UI with minimal custom styling
- Strong i18n support: Built-in ARB-based localization for 51 languages
- Offline support: SQLite via
sqflitepackage for local data caching
Build Process¶
Flutter Web produces static assets (HTML, JS, CSS, images) that are served by Nginx:
# From Dockerfile.frontend (simplified)
FROM ubuntu:22.04 AS builder
# Install Flutter SDK
RUN flutter gen-l10n # Generate 51 localization files
RUN flutter build web --release
FROM nginx:alpine
COPY --from=builder /app/frontend/build/web /usr/share/nginx/html
Build arguments:
API_URL— Backend API endpoint (defaults to relative/api/)CACHEBUST— Forces fresh builds in CI/CD (timestamp-based)
State Management¶
KanjiIQ uses Provider for state management:
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => StudyProvider()),
// Locale, connectivity, sync providers
],
child: MyApp(),
)
Provider was chosen over alternatives (Riverpod, BLoC) for its simplicity — KanjiIQ's state needs are straightforward (flashcard session state, locale preferences, connectivity status).
Routing¶
GoRouter handles declarative routing with support for:
- Deep linking (URL-based navigation)
- Redirect guards (authentication checks)
- Nested routes for screen hierarchy
Dart Frog Backend¶
Why Dart Frog?¶
- Same language as frontend: Dart models can be shared
- Lightweight: Minimal framework overhead, similar to Express.js
- Middleware-based: Clean request pipeline with composable middleware
- Hot reload: Fast development iteration with
dart_frog dev
Request Lifecycle¶
graph LR
R[HTTP Request] --> MW[Middleware Stack]
MW --> RH[Route Handler]
RH --> DB[(PostgreSQL)]
DB --> RH
RH --> RS[JSON Response]
Each route is a Dart file in the routes/ directory. Dart Frog uses file-based routing:
backend/routes/
├── _middleware.dart # Global middleware
├── api/v1/
│ ├── kanji/
│ │ ├── index.dart # GET /api/v1/kanji
│ │ ├── [id].dart # GET /api/v1/kanji/:id
│ │ └── random/
│ │ └── index.dart # GET /api/v1/kanji/random
│ └── admin/
│ ├── _middleware.dart # Admin auth middleware
│ └── ...
Build Process¶
# From Dockerfile.backend (simplified)
FROM dart:stable AS builder
RUN dart pub global activate dart_frog_cli
RUN dart_frog build
FROM dart:stable
COPY --from=builder /app/build /app
CMD ["./server"]
The build step compiles Dart to a standalone server binary with all routes pre-registered.
Shared Dart Ecosystem¶
Both frontend and backend benefit from the same Dart tooling:
| Tool | Purpose |
|---|---|
dart analyze |
Static analysis across both codebases |
dart format |
Consistent code style |
dart test |
Unit and integration testing |
pub.dev |
Shared package repository |
Mobile Readiness¶
The Flutter frontend is designed for multi-platform deployment:
- Web: Currently deployed (primary target)
- iOS/Android: Build-ready with
flutter build apk/flutter build ipa - Desktop: Supported via
flutter build macos/linux/windows
The offline-first architecture (SQLite cache, connectivity detection, background sync) was designed with mobile in mind from the start.