215 lines
9 KiB
Markdown
215 lines
9 KiB
Markdown
|
|
# LEARNING-SPEC.md
|
|||
|
|
|
|||
|
|
context for any agent writing flutter code for eutopic.
|
|||
|
|
|
|||
|
|
## who is reading this code
|
|||
|
|
|
|||
|
|
a junior developer. no flutter or dart experience. basic understanding of frontend concepts (html/css level). goal: understand the code well enough to write business logic, modify screens, and eventually own the codebase. not a throwaway learning exercise — this is the real app.
|
|||
|
|
|
|||
|
|
## annotation requirements
|
|||
|
|
|
|||
|
|
every file must include three layers of annotation. this is non-negotiable.
|
|||
|
|
|
|||
|
|
### 1. file-top architecture comment
|
|||
|
|
|
|||
|
|
a short paragraph (3–6 sentences) at the top of every file, inside a block comment. must explain:
|
|||
|
|
- what this file is and what it does
|
|||
|
|
- why it exists as a separate file (not just "it's a widget")
|
|||
|
|
- how it connects to the rest of the app (what calls it, what it calls)
|
|||
|
|
|
|||
|
|
example format:
|
|||
|
|
```dart
|
|||
|
|
/*
|
|||
|
|
* feed_screen.dart
|
|||
|
|
*
|
|||
|
|
* the main home feed. this is what the user sees after login — a chronological
|
|||
|
|
* list of photo posts from people they follow. it lives at the '/' route and is
|
|||
|
|
* the first tab in the bottom nav bar.
|
|||
|
|
*
|
|||
|
|
* this screen owns fetching the post list from the api and passing individual
|
|||
|
|
* posts down to PostCard widgets. it does not know how to render a post — that
|
|||
|
|
* is PostCard's job. separation of concerns: screen = data, widget = display.
|
|||
|
|
*/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. boilerplate explanation comments
|
|||
|
|
|
|||
|
|
every structural/architectural decision in the boilerplate must have a comment explaining *why*, not just *what*. target: someone who has never seen flutter before can read this file and understand the pattern.
|
|||
|
|
|
|||
|
|
things that always need explanation:
|
|||
|
|
- `StatelessWidget` vs `StatefulWidget` — explain the difference and why this file uses one over the other
|
|||
|
|
- `BuildContext context` — what it is and why it's passed everywhere
|
|||
|
|
- `const` constructors — why they exist, what they do for performance
|
|||
|
|
- `@override` — what it means in dart
|
|||
|
|
- `super.key` — what keys are and why the constructor takes one
|
|||
|
|
- `Widget build(BuildContext context)` — what this method is and when flutter calls it
|
|||
|
|
- any `initState` / `dispose` usage — explain the lifecycle
|
|||
|
|
- any `setState(() {})` — explain what it triggers and why it's needed
|
|||
|
|
- named parameters with `{}` braces vs positional — explain the difference
|
|||
|
|
- `required` keyword — explain what it enforces
|
|||
|
|
- `?` nullable types — explain dart's null safety model briefly on first use per file
|
|||
|
|
|
|||
|
|
### 3. logic TODO blocks
|
|||
|
|
|
|||
|
|
for every section the developer will fill in themselves, write a TODO comment that includes:
|
|||
|
|
- a plain-english description of what needs to happen
|
|||
|
|
- a complete dart syntax example showing the pattern to use (as a comment)
|
|||
|
|
- any gotchas or things to watch out for
|
|||
|
|
|
|||
|
|
format:
|
|||
|
|
```dart
|
|||
|
|
// TODO: [plain english description]
|
|||
|
|
//
|
|||
|
|
// example:
|
|||
|
|
// [complete working dart code snippet]
|
|||
|
|
//
|
|||
|
|
// note: [any gotcha or important thing to know]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
the example must be real, working dart — not pseudocode. it should be close enough to the actual task that the developer can adapt it directly.
|
|||
|
|
|
|||
|
|
## hard sections — write fully, annotate heavily
|
|||
|
|
|
|||
|
|
these three areas are too complex for a junior developer to implement from a TODO. write them completely, but annotate every non-obvious line as if explaining to someone who has never seen async dart or flutter internals:
|
|||
|
|
|
|||
|
|
1. **tus resumable upload** (`shared/services/upload_service.dart`)
|
|||
|
|
- background isolates, tus_client package usage, progress callbacks
|
|||
|
|
- explain what an isolate is, why uploads need one, what happens without it
|
|||
|
|
|
|||
|
|
2. **go_router deep-link invite handling** (`shared/router/app_router.dart`)
|
|||
|
|
- the `redirect` callback, how deep links arrive, how the invite code is extracted from the uri
|
|||
|
|
- explain what a deep link is and the ios/android app link mechanism briefly
|
|||
|
|
|
|||
|
|
3. **story auto-advance timer + gesture layer** (`features/stories/story_viewer_screen.dart`)
|
|||
|
|
- `Timer.periodic`, `AnimationController` for the progress bar, `GestureDetector` for tap-left/tap-right and swipe-down-to-exit
|
|||
|
|
- explain the animation controller lifecycle and why dispose() matters here
|
|||
|
|
|
|||
|
|
## dart syntax reference (include in every file)
|
|||
|
|
|
|||
|
|
every file must include this block, verbatim, near the top after the file-top architecture comment. it is a standing reference so the developer never needs to leave the file to look up basic syntax.
|
|||
|
|
|
|||
|
|
```dart
|
|||
|
|
/*
|
|||
|
|
* dart syntax reference — patterns used in this file
|
|||
|
|
*
|
|||
|
|
* variables
|
|||
|
|
* final String name = 'ada'; // runtime constant — set once, never reassigned
|
|||
|
|
*
|
|||
|
|
* const int max = 10; // compile-time constant — value must be known at build time
|
|||
|
|
*
|
|||
|
|
* String? bio; // nullable — this variable can be null (dart null safety)
|
|||
|
|
*
|
|||
|
|
* late String token; // late — will be assigned before first use, not at declaration
|
|||
|
|
*
|
|||
|
|
* functions
|
|||
|
|
* String greet(String name) { return 'hi $name'; } // regular function
|
|||
|
|
*
|
|||
|
|
* String greet(String name) => 'hi $name'; // arrow shorthand — same as above, one expression only
|
|||
|
|
*
|
|||
|
|
* void log({required String msg}) { ... } // named parameter — caller writes: log(msg: 'x')
|
|||
|
|
*
|
|||
|
|
* void log({String msg = 'hello'}) { ... } // named parameter with default value
|
|||
|
|
*
|
|||
|
|
* void log(String msg) { ... } // positional parameter — caller writes: log('x')
|
|||
|
|
*
|
|||
|
|
* async
|
|||
|
|
* Future<String> fetchName() async { // async function — returns a Future (like a JS Promise)
|
|||
|
|
* final result = await apiCall(); // await pauses here until the Future resolves
|
|||
|
|
* return result;
|
|||
|
|
* }
|
|||
|
|
*
|
|||
|
|
* lists & maps
|
|||
|
|
* final items = <String>['a', 'b']; // typed list
|
|||
|
|
*
|
|||
|
|
* final map = <String, int>{'apples': 3}; // typed map (like a JS object/dict)
|
|||
|
|
*
|
|||
|
|
* for (final item in items) { print(item); } // for-in loop over a list
|
|||
|
|
*
|
|||
|
|
* items.map((x) => x.toUpperCase()).toList() // transform every item (like JS .map())
|
|||
|
|
*
|
|||
|
|
* items.where((x) => x != 'a').toList() // keep items matching condition (like JS .filter())
|
|||
|
|
*
|
|||
|
|
* classes
|
|||
|
|
* class Post {
|
|||
|
|
* final String id;
|
|||
|
|
* final String? caption; // optional field — may be null
|
|||
|
|
* const Post({required this.id, this.caption}); // const constructor, named params
|
|||
|
|
* }
|
|||
|
|
*
|
|||
|
|
* null safety
|
|||
|
|
* bio?.length // safe access — returns null if bio is null, not an error
|
|||
|
|
*
|
|||
|
|
* bio ?? 'no bio' // fallback — use 'no bio' if bio is null
|
|||
|
|
*
|
|||
|
|
* bio! // force-unwrap — crashes if null. avoid unless certain.
|
|||
|
|
*
|
|||
|
|
* control flow
|
|||
|
|
* if (x != null) { ... } else { ... }
|
|||
|
|
*
|
|||
|
|
* final label = isOwn ? 'you' : user.name; // ternary — shorthand if/else (same as JS)
|
|||
|
|
*
|
|||
|
|
* switch (tag) {
|
|||
|
|
* case 'event': ... break;
|
|||
|
|
* default: ...
|
|||
|
|
* }
|
|||
|
|
*
|
|||
|
|
* string interpolation
|
|||
|
|
* 'hello $name' // insert a variable directly
|
|||
|
|
*
|
|||
|
|
* 'count: ${list.length}' // insert an expression — use braces when it's more than a variable
|
|||
|
|
*/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
this block should appear in every file. agents must not remove or shorten it — the developer is learning dart as they go and will refer to it repeatedly.
|
|||
|
|
|
|||
|
|
## style rules
|
|||
|
|
|
|||
|
|
- use lowercase prose in comments (matches the project's communication style)
|
|||
|
|
- no marketing language or enthusiasm ("great!", "easy!", "simply")
|
|||
|
|
- be direct. if something is genuinely complex, say so — don't oversimplify
|
|||
|
|
- prefer short sentences over long ones
|
|||
|
|
- comment *why* over *what* — the code shows what, the comment shows why
|
|||
|
|
|
|||
|
|
## file order
|
|||
|
|
|
|||
|
|
write files in this order so concepts build on each other:
|
|||
|
|
|
|||
|
|
1. `lib/main.dart`
|
|||
|
|
2. `lib/app.dart`
|
|||
|
|
3. `lib/core/theme/app_colors.dart`
|
|||
|
|
4. `lib/core/theme/app_theme.dart`
|
|||
|
|
5. `lib/shared/models/` — all model files
|
|||
|
|
6. `lib/core/router/app_router.dart` — hard section
|
|||
|
|
7. `lib/shared/services/api_client.dart`
|
|||
|
|
8. `lib/shared/services/auth_service.dart`
|
|||
|
|
9. `lib/shared/services/upload_service.dart` — hard section
|
|||
|
|
10. `lib/core/widgets/bottom_nav_bar.dart`
|
|||
|
|
11. `lib/shared/widgets/` — shared widgets (photo_viewer, emoji_picker_sheet, loading_indicator, error_view)
|
|||
|
|
12. `lib/features/feed/` — full feature, end to end (reference pattern for all other features)
|
|||
|
|
13. `lib/features/onboarding/`
|
|||
|
|
14. `lib/features/compose/`
|
|||
|
|
15. `lib/features/posters/`
|
|||
|
|
16. `lib/features/stories/` — hard section (story_viewer_screen.dart)
|
|||
|
|
17. `lib/features/profile/`
|
|||
|
|
18. `lib/features/settings/`
|
|||
|
|
19. `lib/features/feedback/`
|
|||
|
|
|
|||
|
|
## project context
|
|||
|
|
|
|||
|
|
- app: eutopic — photo-first social app, invite-only, no notifications, no DMs, no algorithm
|
|||
|
|
- stack: flutter (dart), go_router, tus_client, lottie animations
|
|||
|
|
- folder structure: feature-first (see `docs/ARCHITECTURE.md` — frontend section)
|
|||
|
|
- full concept: `docs/CONCEPT.md`
|
|||
|
|
- screen/navigation spec: `model-tests/02-flutter-folder-structure.md`
|
|||
|
|
- no notification-related code anywhere — this is a hard constraint
|
|||
|
|
- no push alerts, no unread badges, no FCM/APNs
|
|||
|
|
|
|||
|
|
## what counts as done
|
|||
|
|
|
|||
|
|
a file is complete when:
|
|||
|
|
- it compiles without errors
|
|||
|
|
- all three annotation layers are present
|
|||
|
|
- every TODO block has a working dart example
|
|||
|
|
- hard sections are fully implemented with line-level annotation
|
|||
|
|
- no notification-related imports or widgets exist anywhere
|