Cursor rule
.cursor/rules/dart-patterns.mdcCursor rules
Quality
74/100
Scores the file, not the repository.Length
724 words
11 headings · 9 code blocksRepository
6
— · pushed 80 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Dart/Flutter Patterns78> This file extends [common/patterns.md](../common/patterns.md) with Dart, Flutter, and common ecosystem-specific content.910## Repository Pattern1112```dart13abstract interface class UserRepository {14 Future<User?> getById(String id);15 Future<List<User>> getAll();16 Stream<List<User>> watchAll();17 Future<void> save(User user);18 Future<void> delete(String id);19}2021class UserRepositoryImpl implements UserRepository {22 const UserRepositoryImpl(this._remote, this._local);2324 final UserRemoteDataSource _remote;25 final UserLocalDataSource _local;2627 @override28 Future<User?> getById(String id) async {29 final local = await _local.getById(id);30 if (local != null) return local;31 final remote = await _remote.getById(id);32 if (remote != null) await _local.save(remote);33 return remote;34 }3536 @override37 Future<List<User>> getAll() async {38 final remote = await _remote.getAll();39 for (final user in remote) {40 await _local.save(user);41 }42 return remote;43 }4445 @override46 Stream<List<User>> watchAll() => _local.watchAll();4748 @override49 Future<void> save(User user) => _local.save(user);5051 @override52 Future<void> delete(String id) async {53 await _remote.delete(id);54 await _local.delete(id);55 }56}57```5859## State Management: BLoC/Cubit6061```dart62// Cubit — simple state transitions63class CounterCubit extends Cubit<int> {64 CounterCubit() : super(0);6566 void increment() => emit(state + 1);67 void decrement() => emit(state - 1);68}6970// BLoC — event-driven71@immutable72sealed class CartEvent {}73class CartItemAdded extends CartEvent { CartItemAdded(this.item); final Item item; }74class CartItemRemoved extends CartEvent { CartItemRemoved(this.id); final String id; }75class CartCleared extends CartEvent {}7677@immutable78class CartState {79 const CartState({this.items = const []});80 final List<Item> items;81 CartState copyWith({List<Item>? items}) => CartState(items: items ?? this.items);82}8384class CartBloc extends Bloc<CartEvent, CartState> {85 CartBloc() : super(const CartState()) {86 on<CartItemAdded>((event, emit) =>87 emit(state.copyWith(items: [...state.items, event.item])));88 on<CartItemRemoved>((event, emit) =>89 emit(state.copyWith(items: state.items.where((i) => i.id != event.id).toList())));90 on<CartCleared>((_, emit) => emit(const CartState()));91 }92}93```9495## State Management: Riverpod9697```dart98// Simple provider99@riverpod100Future<List<User>> users(Ref ref) async {101 final repo = ref.watch(userRepositoryProvider);102 return repo.getAll();103}104105// Notifier for mutable state106@riverpod107class CartNotifier extends _$CartNotifier {108 @override109 List<Item> build() => [];110111 void add(Item item) => state = [...state, item];112 void remove(String id) => state = state.where((i) => i.id != id).toList();113 void clear() => state = [];114}115116// ConsumerWidget117class CartPage extends ConsumerWidget {118 const CartPage({super.key});119120 @override121 Widget build(BuildContext context, WidgetRef ref) {122 final items = ref.watch(cartNotifierProvider);123 return ListView(124 children: items.map((item) => CartItemTile(item: item)).toList(),125 );126 }127}128```129130## Dependency Injection131132Constructor injection is preferred. Use `get_it` or Riverpod providers at composition root:133134```dart135// get_it registration (in a setup file)136void setupDependencies() {137 final di = GetIt.instance;138 di.registerSingleton<ApiClient>(ApiClient(baseUrl: Env.apiUrl));139 di.registerSingleton<UserRepository>(140 UserRepositoryImpl(di<ApiClient>(), di<LocalDatabase>()),141 );142 di.registerFactory(() => UserListViewModel(di<UserRepository>()));143}144```145146## ViewModel Pattern (without BLoC/Riverpod)147148```dart149class UserListViewModel extends ChangeNotifier {150 UserListViewModel(this._repository);151152 final UserRepository _repository;153154 AsyncState<List<User>> _state = const Loading();155 AsyncState<List<User>> get state => _state;156157 Future<void> load() async {158 _state = const Loading();159 notifyListeners();160 try {161 final users = await _repository.getAll();162 _state = Success(users);163 } on Exception catch (e) {164 _state = Failure(e);165 }166 notifyListeners();167 }168}169```170171## UseCase Pattern172173```dart174class GetUserUseCase {175 const GetUserUseCase(this._repository);176 final UserRepository _repository;177178 Future<User?> call(String id) => _repository.getById(id);179}180181class CreateUserUseCase {182 const CreateUserUseCase(this._repository, this._idGenerator);183 final UserRepository _repository;184 final IdGenerator _idGenerator; // injected — domain layer must not depend on uuid package directly185186 Future<void> call(CreateUserInput input) async {187 // Validate, apply business rules, then persist188 final user = User(id: _idGenerator.generate(), name: input.name, email: input.email);189 await _repository.save(user);190 }191}192```193194## Immutable State with freezed195196```dart197@freezed198class UserState with _$UserState {199 const factory UserState({200 @Default([]) List<User> users,201 @Default(false) bool isLoading,202 String? errorMessage,203 }) = _UserState;204}205```206207## Clean Architecture Layer Boundaries208209```210lib/211├── domain/ # Pure Dart — no Flutter, no external packages212│ ├── entities/213│ ├── repositories/ # Abstract interfaces214│ └── usecases/215├── data/ # Implements domain interfaces216│ ├── datasources/217│ ├── models/ # DTOs with fromJson/toJson218│ └── repositories/219└── presentation/ # Flutter widgets + state management220 ├── pages/221 ├── widgets/222 └── providers/ (or blocs/ or viewmodels/)223```224225- Domain must not import `package:flutter` or any data-layer package226- Data layer maps DTOs to domain entities at repository boundaries227- Presentation calls use cases, not repositories directly228229## Navigation (GoRouter)230231```dart232final router = GoRouter(233 routes: [234 GoRoute(235 path: '/',236 builder: (context, state) => const HomePage(),237 ),238 GoRoute(239 path: '/users/:id',240 builder: (context, state) {241 final id = state.pathParameters['id']!;242 return UserDetailPage(userId: id);243 },244 ),245 ],246 // refreshListenable re-evaluates redirect whenever auth state changes247 refreshListenable: GoRouterRefreshStream(authCubit.stream),248 redirect: (context, state) {249 final isLoggedIn = context.read<AuthCubit>().state is AuthAuthenticated;250 if (!isLoggedIn && !state.matchedLocation.startsWith('/login')) {251 return '/login';252 }253 return null;254 },255);256```257258## References259260See skill: `flutter-dart-code-review` for the comprehensive review checklist.261See skill: `compose-multiplatform-patterns` for Kotlin Multiplatform/Flutter interop patterns.262
Also in ThanhTrunggDEV/DontBeLazy
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| ThanhTrunggDEV/DontBeLazy.cursor/rules/zh-agents.mdc · 6 | Cursor rules | no sections | 50/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/zh-patterns.mdc · 6 | Cursor rules | api | 30/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.agent/AGENTS.md · 6 | AGENTS.md | buildteststylearch+4 | 77/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/AGENTS.md · 6 | AGENTS.md | buildteststylearch+4 | 77/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-agents.mdc · 6 | Cursor rules | agent-behaviour | 50/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-code-review.mdc · 6 | Cursor rules | styletesting-strategygitsecurity+3 | 65/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-coding-style.mdc · 6 | Cursor rules | style | 54/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-development-workflow.mdc · 6 | Cursor rules | gitagent-behaviour | 39/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-git-workflow.mdc · 6 | Cursor rules | lint-formatgitagent-behaviour | 43/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-hooks.mdc · 6 | Cursor rules | styletypessecuritydo-not | 36/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-patterns.mdc · 6 | Cursor rules | lint-formatstyleapi | 52/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-performance.mdc · 6 | Cursor rules | buildperformance | 48/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-security.mdc · 6 | Cursor rules | security | 39/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/common-testing.mdc · 6 | Cursor rules | testtesting-strategyagent-behaviour | 34/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-coding-style.mdc · 6 | Cursor rules | lint-formatstyle | 52/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-hooks.mdc · 6 | Cursor rules | buildlint-formatdeployment | 60/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-patterns.mdc · 6 | Cursor rules | style | 54/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-security.mdc · 6 | Cursor rules | securityperformancedo-not | 73/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/cpp-testing.mdc · 6 | Cursor rules | testtesting-strategy | 55/100 | 3 days ago | |
| ThanhTrunggDEV/DontBeLazy.cursor/rules/csharp-coding-style.mdc · 6 | Cursor rules | lint-formatstyletypes | 66/100 | 3 days ago |
Diff against .cursor/rules/zh-agents.mdc Diff against .cursor/rules/zh-patterns.mdc Diff against .agent/AGENTS.md Diff against .cursor/AGENTS.md Diff against .cursor/rules/common-agents.mdc Diff against .cursor/rules/common-code-review.mdc Diff against .cursor/rules/common-coding-style.mdc Diff against .cursor/rules/common-development-workflow.mdc Diff against .cursor/rules/common-git-workflow.mdc Diff against .cursor/rules/common-hooks.mdc Diff against .cursor/rules/common-patterns.mdc Diff against .cursor/rules/common-performance.mdc Diff against .cursor/rules/common-security.mdc Diff against .cursor/rules/common-testing.mdc Diff against .cursor/rules/cpp-coding-style.mdc Diff against .cursor/rules/cpp-hooks.mdc Diff against .cursor/rules/cpp-patterns.mdc Diff against .cursor/rules/cpp-security.mdc Diff against .cursor/rules/cpp-testing.mdc Diff against .cursor/rules/csharp-coding-style.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
