mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 126 lines 7.0 kB view raw view rendered
1--- 2title: Code Patterns 3updated: 2026-05-07 4--- 5 6This document captures recurring architectural and implementation patterns in the Lazurite codebase. 7 8## Code Organization 9 10```sh 11lib/ 12 ├── core/ # cross-cutting app/platform concerns (router, theme, logging, network, database) 13 ├── features/ # feature-first modules 14 │ └── {feature} # one vertical slice per domain 15 │ ├── data/ # repositories, DTO mapping, persistence/network access 16 │ ├── bloc or cubit/ # state orchestration and business flow 17 │ └── presentation/ # screens, widgets, and view-specific helpers 18 └── shared/ # reusable widgets/helpers/utils not tied to one feature 19test/ # mirrors production layout (core/features/shared) for unit/widget tests 20docs/ # specs, tasks, operational docs, and developer references 21``` 22 23## State and UI Patterns 24 25- Prefer immutable `Equatable` state with explicit `copyWith` semantics. 26- Model state transitions in `Bloc`/`Cubit` layers; keep presentation widgets focused on rendering and user interactions. 27- Use `sealed class` events and typed state classes to keep transitions explicit and testable. 28- For repeated UI states, use shared widgets instead of ad-hoc `Center(...)` blocks: 29 - `LoadingState` 30 - `ErrorState` 31 - `EmptyState` 32 33## Extracted Reuse Patterns 34 35### Utilities 36 37`lib/shared/utils/format_utils.dart` is the canonical source for display primitives (initials, compact counts, relative time labels). 38The extraction replaced repeated local formatters from feed/profile/search/notification surfaces with one tested implementation (`test/shared/utils/format_utils_test.dart`). 39 40### Dialogs, Sheets, and Snackbars 41 42Transient UI interaction patterns were consolidated into shared helpers and widgets: 43 44- `showConfirmationDialog` for confirm/cancel flows 45- `showOptionsSheet` for action menus 46- `showAppSnackBar` for user feedback 47 48This keeps behavior and semantics consistent across features and is covered with focused widget tests in `test/shared/presentation/...`. 49 50### Theme Access and Tokens 51 52Theme access should go through `lib/core/theme/theme_extensions.dart` (`context.theme`, `context.colorScheme`, `context.textTheme`) rather than repeated `Theme.of(...)` lookups. 53 54For layout rhythm, reuse spacing tokens from `lib/core/theme/spacing.dart`. 55 56For visual consistency in image treatment, use shared filters from `lib/core/theme/color_filters.dart`. 57 58### Reusable Presentation Widgets 59 60Repeated avatar/name/icon logic from the audit was extracted into shared primitives. Prefer: 61 62- `ProfileAvatar` (`lib/shared/presentation/widgets/profile_avatar.dart`) for all profile/list/feed avatar rendering, including fallback and moderation-aware masking behavior. 63- `ActorNameWidget` (`lib/shared/presentation/widgets/actor_name_widget.dart`) for standardized display-name + handle rows (used in feed cards, embedded records, and conversation items). 64- `NotificationIconMapper` (`lib/shared/presentation/helpers/notification_icon_mapper.dart`) for mapping notification reason to icon + color style, instead of local switch blocks. 65 66### Navigation and Haptics 67 68Navigation/haptic side effects also follow shared helper entry points. 69Use `navigateToProfile` and `navigateToPost` for common entity navigation, and route tactile feedback through `HapticHelper` instead of direct `HapticFeedback` calls in feature widgets. 70 71## Other Recurring Codebase Patterns 72 73- Feature-first vertical slices under `lib/features/{feature}/{data,bloc|cubit,presentation}` keep business logic close to owning UI while preserving cross-feature boundaries. 74- Dependency injection is explicit at composition boundaries (`RepositoryProvider`/`BlocProvider` in app and screen roots), which makes screens and blocs easy to test in isolation. 75- Repository interfaces return typed results (often with cursor pagination) instead of raw maps, keeping API edges explicit and testable. 76- Defensive fallback behavior is preferred for unstable integrations; for example, repository logic that falls back on alternative endpoints when one API path fails is covered by contract tests. 77- Shared moderation-aware rendering is treated as a first-class cross-feature concern (`ModerationUI`, `ModerationBadgeRow`, `ModeratedBlurOverlay`, `ProfileAvatar` moderation hooks). 78- Use fully-qualified package imports (`package:lazurite/...`) for internal modules. 79 80## Moderation-Aware Rendering Pattern 81 82- For actor/content media that can be moderated, request moderation UI state via moderation service helpers and pass it into rendering widgets. 83- `ProfileAvatar` supports moderated fallback/masking through `ModerationUI`. 84- Compose moderation badges/overlays (`ModerationBadgeRow`, `ModeratedBlurOverlay`) at feature widget boundaries. 85 86## Nullable `copyWith` Pattern (Required) 87 88For nullable fields in immutable state, **do not** use `field ?? this.field` when `null` is a meaningful explicit update. 89 90Use a sentinel default to distinguish: 91 92- keep current value 93- set value to `null` 94 95```dart 96static const Object _unset = Object(); 97 98State copyWith({ 99 Object? nullableField = _unset, 100}) { 101 return State( 102 nullableField: identical(nullableField, _unset) 103 ? this.nullableField 104 : nullableField as String?, 105 ); 106} 107``` 108 109### Review Checklist 110 111- For clearable nullable fields, avoid `??` fallback in `copyWith`. 112- Add tests proving nullable fields can be explicitly cleared. 113 114## Testing Patterns 115 116- Per-file setup builders are standard (`buildSubject(...)`, plus helpers like `openSheet(...)`) so test bodies focus on behavior, not setup. 117- Widget tests usually mount through `MaterialApp`/`Scaffold` for component tests, and `MaterialApp.router` + `GoRouter` for navigation tests. 118- Interaction flow follows a consistent shape: 119 `pumpWidget` -> input (`tap`, `enterText`) -> `pump`/`pumpAndSettle` -> assertions on visible UI and side effects. 120- BLoC/Cubit widget tests rely on `mocktail` + `bloc_test` (`MockBloc`, `MockCubit`, `when`, `whenListen`, `verify`, `verifyNever`), with `registerFallbackValue` in `setUpAll` when needed. 121- Router behavior is verified by capturing the pushed route URI and asserting path/query params 122- Complex service/repository tests use fit-for-purpose doubles: 123 - `MockClient` from `http/testing.dart` for HTTP-level contracts 124 - Small local fake implementations when protocol surfaces are complex 125- Async UI timing is made explicit where animations/debounce are relevant (`pump(const Duration(...))` in sheet/search tests), and responsive behavior is exercised with `setSurfaceSize` in router/shell tests. 126- Defensive assertions are common: boundary-value checks in utility tests, null/empty/error-path repository tests, and `expect(tester.takeException(), isNull)` guards in navigation/router tests.