mobile bluesky app made with flutter
lazurite.stormlightlabs.org/
mobile
bluesky
flutter
1# Development Guide
2
3## Quick Start
4
5```sh
6flutter pub get
7just objectbox-setup # once per machine (macOS/Linux)
8just gen
9flutter run
10```
11
12## Common Tasks
13
14Use `just` for common workflows:
15
16| Command | Description |
17| ---------------------- | ----------------------------------------------------- |
18| `just format` | Run `dart format` |
19| `just lint` | Run `flutter analyze` |
20| `just objectbox-setup` | Install pinned ObjectBox native runtime (macOS/Linux) |
21| `just objectbox-check` | Verify ObjectBox native runtime is present |
22| `just test` | Run the full `flutter test` suite |
23| `just gen` | Run `build_runner` for code generation |
24| `just check` | Format, lint, and test in sequence |
25
26## Architecture
27
28### Stack
29
30| Layer | Technology |
31| ---------------- | ------------------------------------------------------------- |
32| Framework | Flutter (Material 3) |
33| State management | `flutter_bloc` (Bloc + Cubit) |
34| Primary database | Drift (SQLite) — source of truth |
35| Vector store | ObjectBox — embeddings for semantic search |
36| Networking | `bluesky` / `atproto` packages + Dio |
37| Navigation | `go_router` (StatefulShellRoute) |
38| Code generation | `build_runner`, `freezed`, `drift_dev`, `objectbox_generator` |
39
40### Feature Module Layout
41
42Each feature follows this convention:
43
44```sh
45features/<feature>/
46 bloc/ # Bloc or Cubit + State + Event classes
47 cubit/ # Lighter-weight cubits where appropriate
48 data/ # Repository classes; API + database interactions
49 presentation/
50 <feature>_screen.dart
51 widgets/
52```
53
54## Database
55
56### Drift (primary store)
57
58Powered by **Drift** (SQLite). All migrations live in `lib/core/database/app_database.dart`.
59
60Current schema version: **15**
61
62| Table | Version | Purpose |
63| ------------------- | ------- | -------------------------------------------------------- |
64| `accounts` | 1 | Auth sessions (DID, tokens, handle, service URL) |
65| `cached_profiles` | 2 | Profile metadata cache |
66| `cached_posts` | 2 | Post content cache for offline display |
67| `settings` | 1 | Key-value application configuration |
68| `saved_feeds` | 3 | User's pinned and saved feeds |
69| `cached_feed_pages` | 12 | First-page feed cache for cold-start UX |
70| `search_history` | 4 | Persistent local search query history |
71| `drafts` | 5 | Offline-first post drafts with media |
72| `saved_posts` | 6 | Locally saved posts (save type: local or cloud bookmark) |
73| `labeler_cache` | 9 | Cached labeler service policies |
74| `liked_posts` | 15 | Locally persisted liked posts for semantic search |
75
76#### Adding a migration
77
781. Add or modify table definition in `lib/core/database/tables.dart`.
792. Bump `schemaVersion` in `AppDatabase`.
803. Add `if (from < N) { ... }` block in `MigrationStrategy.onUpgrade`.
814. Run `just gen` to regenerate `app_database.g.dart`.
82
83### ObjectBox (vector store)
84
85ObjectBox runs as a secondary store alongside Drift. It holds only embedding vectors and the metadata needed to join back to Drift.
86
87The `EmbeddedPost` entity stores:
88
89- `postUri` (unique AT-URI)
90- `accountDid`
91- `source` (`'saved'` or `'liked'`)
92- `indexedText` — text used to produce the embedding
93- `embedding` — 384-dimensional float32 vector (HNSW cosine index)
94- `embeddedAt`
95
96After modifying `EmbeddedPost`, run `just gen` to regenerate `objectbox.g.dart` and `objectbox-model.json`.
97
98#### Running unit tests (ObjectBox native library)
99
100ObjectBox requires a platform native library to run `flutter test` locally.
101Lazurite standardizes this setup through a pinned installer script.
102
103Supported local platforms: macOS and Linux.
104
105Run this once per machine:
106
107```sh
108just objectbox-setup
109```
110
111`just test` and `just test-quiet` intentionally run a preflight check
112(`just objectbox-check`) and fail fast with setup instructions if the
113runtime is missing.
114
115## Semantic Search
116
117On-device vector search over saved and liked posts using:
118
119- **Model:** `all-MiniLM-L6-v2` quantized INT8 TFLite model (384D output, ~25 MB)
120- **Tokenizer:** WordPiece, max 256 tokens, bundled as Flutter assets
121- **Inference:** Long-lived background `Isolate` via `EmbeddingService`
122- **Storage:** ObjectBox with HNSW cosine index on the embedding vector
123
124The `EmbeddingService.isAvailable` flag gates all semantic search UI entry points. On initialization failure the feature degrades gracefully to unavailable.
125
126## Liked Posts
127
128Liked posts are synced from the Bluesky API and persisted locally in the `liked_posts` Drift table. This feeds the semantic search pipeline.
129
130**`LikedPostsRepository`** (`lib/features/feed/data/liked_posts_repository.dart`):
131
132| Method | Description |
133| -------------------------------------------- | ----------------------------------------------------------------------------- |
134| `syncLikes(accountDid)` | Paginate `getActorLikes` until a known URI is hit or 1000-post cap is reached |
135| | evicts oldest on overflow |
136| `getLikedPosts(accountDid, {limit, offset})` | Paginated query ordered by `likedAt DESC` |
137| `removeLike(accountDid, postUri)` | Delete a single entry |
138
139## Testing
140
141Run the full suite:
142
143```sh
144just test
145# or with failure-only reporter (recommended for large suites):
146just test-quiet
147```
148
149Coverage target: **>99%** (±5% acceptable). All new code must have tests. Widget tests use `NativeDatabase.memory()` for Drift and mocktail for API mocks.
150
151## Code Generation
152
153Run after any change to:
154
155- Drift table definitions (`tables.dart`)
156- ObjectBox entities (`embedded_post.dart`)
157- `freezed` models
158- `json_serializable` classes
159
160```sh
161just gen
162# or:
163dart run build_runner build --delete-conflicting-outputs
164```
165
166## Routes
167
168| Path | Description |
169| ----------- | ------------------------------ |
170| `/login` | Authentication gateway |
171| `/` | Home feed tab |
172| `/search` | Search tab |
173| `/profile` | Current user profile tab |
174| `/settings` | Global settings |
175| `/compose` | Root-level modal for new posts |
176
177## References
178
179- [Bluesky API Documentation](https://docs.bsky.app/)
180- [AT Protocol Specification](https://atproto.com/)
181- [Flutter Documentation](https://flutter.dev/docs)
182- [Drift Documentation](https://drift.simonbinder.eu/)
183- [ObjectBox Flutter](https://docs.objectbox.io/flutter)