9 KiB
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:
/*
* 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:
StatelessWidgetvsStatefulWidget— explain the difference and why this file uses one over the otherBuildContext context— what it is and why it's passed everywhereconstconstructors — why they exist, what they do for performance@override— what it means in dartsuper.key— what keys are and why the constructor takes oneWidget build(BuildContext context)— what this method is and when flutter calls it- any
initState/disposeusage — explain the lifecycle - any
setState(() {})— explain what it triggers and why it's needed - named parameters with
{}braces vs positional — explain the difference requiredkeyword — 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:
// 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:
-
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
-
go_router deep-link invite handling (
shared/router/app_router.dart)- the
redirectcallback, 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
- the
-
story auto-advance timer + gesture layer (
features/stories/story_viewer_screen.dart)Timer.periodic,AnimationControllerfor the progress bar,GestureDetectorfor 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 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:
lib/main.dartlib/app.dartlib/core/theme/app_colors.dartlib/core/theme/app_theme.dartlib/shared/models/— all model fileslib/core/router/app_router.dart— hard sectionlib/shared/services/api_client.dartlib/shared/services/auth_service.dartlib/shared/services/upload_service.dart— hard sectionlib/core/widgets/bottom_nav_bar.dartlib/shared/widgets/— shared widgets (photo_viewer, emoji_picker_sheet, loading_indicator, error_view)lib/features/feed/— full feature, end to end (reference pattern for all other features)lib/features/onboarding/lib/features/compose/lib/features/posters/lib/features/stories/— hard section (story_viewer_screen.dart)lib/features/profile/lib/features/settings/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