@ngxtm/devkit 3.3.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/index.js +59 -13
- package/cli/rules.js +248 -0
- package/package.json +2 -1
- package/rules/README.md +141 -0
- package/rules/dart/best-practices/SKILL.md +23 -0
- package/rules/dart/language/SKILL.md +52 -0
- package/rules/dart/tooling/SKILL.md +43 -0
- package/rules/dotnet/aspnet-core/SKILL.md +92 -0
- package/rules/dotnet/aspnet-core/references/REFERENCE.md +335 -0
- package/rules/dotnet/best-practices/SKILL.md +101 -0
- package/rules/dotnet/best-practices/references/REFERENCE.md +256 -0
- package/rules/dotnet/blazor/SKILL.md +146 -0
- package/rules/dotnet/blazor/references/REFERENCE.md +392 -0
- package/rules/dotnet/language/SKILL.md +82 -0
- package/rules/dotnet/language/references/REFERENCE.md +222 -0
- package/rules/dotnet/patterns.rule.md +388 -0
- package/rules/dotnet/razor-pages/SKILL.md +124 -0
- package/rules/dotnet/razor-pages/references/REFERENCE.md +321 -0
- package/rules/dotnet/security/SKILL.md +89 -0
- package/rules/dotnet/security/references/REFERENCE.md +295 -0
- package/rules/dotnet/tooling/SKILL.md +92 -0
- package/rules/dotnet/tooling/references/REFERENCE.md +300 -0
- package/rules/flutter/auto-route-navigation/SKILL.md +43 -0
- package/rules/flutter/auto-route-navigation/references/REFERENCE.md +19 -0
- package/rules/flutter/auto-route-navigation/references/router-config.md +62 -0
- package/rules/flutter/bloc-state-management/SKILL.md +64 -0
- package/rules/flutter/bloc-state-management/references/REFERENCE.md +20 -0
- package/rules/flutter/bloc-state-management/references/auth-bloc-example.md +52 -0
- package/rules/flutter/bloc-state-management/references/equatable-usage.md +56 -0
- package/rules/flutter/bloc-state-management/references/property-based-state.md +68 -0
- package/rules/flutter/bloc.rule.md +76 -0
- package/rules/flutter/cicd/SKILL.md +48 -0
- package/rules/flutter/cicd/references/advanced-workflow.md +66 -0
- package/rules/flutter/cicd/references/fastlane.md +139 -0
- package/rules/flutter/cicd/references/github-actions.md +59 -0
- package/rules/flutter/dependency-injection/SKILL.md +42 -0
- package/rules/flutter/dependency-injection/references/REFERENCE.md +15 -0
- package/rules/flutter/dependency-injection/references/modules.md +37 -0
- package/rules/flutter/error-handling/SKILL.md +32 -0
- package/rules/flutter/error-handling/references/REFERENCE.md +19 -0
- package/rules/flutter/error-handling/references/error-mapping.md +31 -0
- package/rules/flutter/feature-based-clean-architecture/SKILL.md +46 -0
- package/rules/flutter/feature-based-clean-architecture/references/REFERENCE.md +14 -0
- package/rules/flutter/feature-based-clean-architecture/references/folder-structure.md +36 -0
- package/rules/flutter/getx-navigation/SKILL.md +70 -0
- package/rules/flutter/getx-navigation/references/app-pages.md +40 -0
- package/rules/flutter/getx-navigation/references/middleware-example.md +29 -0
- package/rules/flutter/getx-state-management/SKILL.md +76 -0
- package/rules/flutter/getx-state-management/references/binding-example.md +32 -0
- package/rules/flutter/getx-state-management/references/reactive-vs-simple.md +39 -0
- package/rules/flutter/go-router-navigation/SKILL.md +57 -0
- package/rules/flutter/idiomatic-flutter/SKILL.md +20 -0
- package/rules/flutter/layer-based-clean-architecture/SKILL.md +50 -0
- package/rules/flutter/layer-based-clean-architecture/references/REFERENCE.md +60 -0
- package/rules/flutter/layer-based-clean-architecture/references/repository-mapping.md +50 -0
- package/rules/flutter/localization/SKILL.md +50 -0
- package/rules/flutter/localization/references/REFERENCE.md +48 -0
- package/rules/flutter/localization/references/sheet-loader.md +33 -0
- package/rules/flutter/navigator-v1-navigation/SKILL.md +71 -0
- package/rules/flutter/navigator-v1-navigation/references/on-generate-route.md +48 -0
- package/rules/flutter/performance/SKILL.md +24 -0
- package/rules/flutter/retrofit-networking/SKILL.md +51 -0
- package/rules/flutter/retrofit-networking/references/REFERENCE.md +19 -0
- package/rules/flutter/retrofit-networking/references/token-refresh.md +40 -0
- package/rules/flutter/riverpod-state-management/SKILL.md +53 -0
- package/rules/flutter/riverpod-state-management/references/architecture.md +124 -0
- package/rules/flutter/riverpod-state-management/references/best-practices.md +89 -0
- package/rules/flutter/riverpod-state-management/references/testing.md +73 -0
- package/rules/flutter/riverpod.rule.md +78 -0
- package/rules/flutter/security/SKILL.md +33 -0
- package/rules/flutter/security/references/REFERENCE.md +15 -0
- package/rules/flutter/security/references/network-security.md +28 -0
- package/rules/flutter/testing/SKILL.md +44 -0
- package/rules/flutter/testing/references/REFERENCE.md +21 -0
- package/rules/flutter/testing/references/bloc-testing.md +38 -0
- package/rules/flutter/testing/references/integration-testing.md +128 -0
- package/rules/flutter/testing/references/robot-pattern.md +82 -0
- package/rules/flutter/testing/references/unit-testing.md +130 -0
- package/rules/flutter/testing/references/widget-testing.md +120 -0
- package/rules/flutter/widgets/SKILL.md +37 -0
- package/rules/golang/chi-router/SKILL.md +219 -0
- package/rules/golang/chi-router/references/REFERENCE.md +13 -0
- package/rules/golang/chi-router/references/routing-patterns.md +205 -0
- package/rules/golang/cobra-cli/SKILL.md +227 -0
- package/rules/golang/cobra-cli/references/REFERENCE.md +13 -0
- package/rules/golang/cobra-cli/references/command-patterns.md +224 -0
- package/rules/golang/core/SKILL.md +210 -0
- package/rules/golang/core/references/REFERENCE.md +14 -0
- package/rules/golang/core/references/concurrency-patterns.md +114 -0
- package/rules/golang/core/references/error-handling.md +87 -0
- package/rules/golang/echo-framework/SKILL.md +215 -0
- package/rules/golang/echo-framework/references/REFERENCE.md +14 -0
- package/rules/golang/echo-framework/references/middleware-patterns.md +141 -0
- package/rules/golang/echo-framework/references/routing-patterns.md +140 -0
- package/rules/golang/ent-orm/SKILL.md +239 -0
- package/rules/golang/ent-orm/references/REFERENCE.md +13 -0
- package/rules/golang/ent-orm/references/schema-patterns.md +255 -0
- package/rules/golang/fiber-framework/SKILL.md +196 -0
- package/rules/golang/fiber-framework/references/REFERENCE.md +13 -0
- package/rules/golang/fiber-framework/references/routing-patterns.md +191 -0
- package/rules/golang/gin-framework/SKILL.md +205 -0
- package/rules/golang/gin-framework/references/REFERENCE.md +14 -0
- package/rules/golang/gin-framework/references/middleware-patterns.md +119 -0
- package/rules/golang/gorm-orm/SKILL.md +196 -0
- package/rules/golang/gorm-orm/references/REFERENCE.md +14 -0
- package/rules/golang/gorm-orm/references/model-definitions.md +167 -0
- package/rules/golang/gorm-orm/references/query-patterns.md +161 -0
- package/rules/golang/grpc/SKILL.md +231 -0
- package/rules/golang/grpc/references/REFERENCE.md +13 -0
- package/rules/golang/grpc/references/service-patterns.md +276 -0
- package/rules/golang/testify/SKILL.md +239 -0
- package/rules/golang/testify/references/REFERENCE.md +13 -0
- package/rules/golang/testify/references/assert-patterns.md +170 -0
- package/rules/golang/validator/SKILL.md +234 -0
- package/rules/golang/validator/references/REFERENCE.md +13 -0
- package/rules/golang/validator/references/validation-tags.md +211 -0
- package/rules/golang/viper-config/SKILL.md +244 -0
- package/rules/golang/viper-config/references/REFERENCE.md +13 -0
- package/rules/golang/viper-config/references/config-loading.md +181 -0
- package/rules/golang/wire-di/SKILL.md +243 -0
- package/rules/golang/wire-di/references/REFERENCE.md +13 -0
- package/rules/golang/wire-di/references/provider-patterns.md +193 -0
- package/rules/golang/zap-logging/SKILL.md +203 -0
- package/rules/golang/zap-logging/references/REFERENCE.md +13 -0
- package/rules/golang/zap-logging/references/logger-config.md +165 -0
- package/rules/java/build-gradle/SKILL.md +92 -0
- package/rules/java/build-gradle/references/REFERENCE.md +14 -0
- package/rules/java/build-gradle/references/kotlin-dsl.md +118 -0
- package/rules/java/build-gradle/references/task-configuration.md +132 -0
- package/rules/java/build-maven/SKILL.md +86 -0
- package/rules/java/build-maven/references/REFERENCE.md +14 -0
- package/rules/java/build-maven/references/dependency-management.md +111 -0
- package/rules/java/build-maven/references/lifecycle-phases.md +114 -0
- package/rules/java/graalvm-native/SKILL.md +105 -0
- package/rules/java/graalvm-native/references/REFERENCE.md +12 -0
- package/rules/java/java-collections-streams/SKILL.md +148 -0
- package/rules/java/java-collections-streams/references/REFERENCE.md +15 -0
- package/rules/java/java-collections-streams/references/collectors-patterns.md +178 -0
- package/rules/java/java-collections-streams/references/stream-pipelines.md +165 -0
- package/rules/java/java-concurrency/SKILL.md +187 -0
- package/rules/java/java-concurrency/references/REFERENCE.md +17 -0
- package/rules/java/java-concurrency/references/completable-future.md +165 -0
- package/rules/java/java-concurrency/references/executor-patterns.md +176 -0
- package/rules/java/java-concurrency/references/virtual-threads.md +190 -0
- package/rules/java/java-core-language/SKILL.md +121 -0
- package/rules/java/java-core-language/references/REFERENCE.md +15 -0
- package/rules/java/java-core-language/references/jvm-memory-model.md +160 -0
- package/rules/java/java-core-language/references/modern-java-features.md +168 -0
- package/rules/java/java-project-structure/SKILL.md +195 -0
- package/rules/java/java-project-structure/references/REFERENCE.md +15 -0
- package/rules/java/java-project-structure/references/maven-project-layout.md +199 -0
- package/rules/java/java-project-structure/references/module-system.md +159 -0
- package/rules/java/micronaut-core/SKILL.md +99 -0
- package/rules/java/micronaut-core/references/REFERENCE.md +12 -0
- package/rules/java/micronaut-reactive/SKILL.md +68 -0
- package/rules/java/micronaut-reactive/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-core/SKILL.md +85 -0
- package/rules/java/quarkus-core/references/REFERENCE.md +12 -0
- package/rules/java/quarkus-reactive/SKILL.md +67 -0
- package/rules/java/quarkus-reactive/references/REFERENCE.md +12 -0
- package/rules/java/spring-batch/SKILL.md +102 -0
- package/rules/java/spring-batch/references/REFERENCE.md +12 -0
- package/rules/java/spring-boot-architecture/SKILL.md +206 -0
- package/rules/java/spring-boot-architecture/references/REFERENCE.md +15 -0
- package/rules/java/spring-boot-architecture/references/auto-configuration.md +158 -0
- package/rules/java/spring-boot-architecture/references/configuration-properties.md +202 -0
- package/rules/java/spring-boot-web/SKILL.md +217 -0
- package/rules/java/spring-boot-web/references/REFERENCE.md +17 -0
- package/rules/java/spring-cloud/SKILL.md +109 -0
- package/rules/java/spring-cloud/references/REFERENCE.md +13 -0
- package/rules/java/spring-data-jpa/SKILL.md +241 -0
- package/rules/java/spring-data-jpa/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/SKILL.md +161 -0
- package/rules/java/spring-security/references/REFERENCE.md +16 -0
- package/rules/java/spring-security/references/jwt-auth-flow.md +213 -0
- package/rules/java/testing-junit-mockito/SKILL.md +135 -0
- package/rules/java/testing-junit-mockito/references/REFERENCE.md +15 -0
- package/rules/java/testing-junit-mockito/references/junit5-patterns.md +159 -0
- package/rules/java/testing-junit-mockito/references/mockito-patterns.md +148 -0
- package/rules/java/testing-junit-mockito/references/spring-boot-testing.md +152 -0
- package/rules/javascript/best-practices/SKILL.md +64 -0
- package/rules/javascript/best-practices/references/REFERENCE.md +91 -0
- package/rules/javascript/language/SKILL.md +71 -0
- package/rules/javascript/language/references/REFERENCE.md +106 -0
- package/rules/javascript/tooling/SKILL.md +60 -0
- package/rules/javascript/tooling/references/REFERENCE.md +107 -0
- package/rules/metadata.json +54 -0
- package/rules/nestjs/api-standards/SKILL.md +47 -0
- package/rules/nestjs/api-standards/references/pagination-wrapper.md +87 -0
- package/rules/nestjs/architecture/SKILL.md +68 -0
- package/rules/nestjs/architecture/references/dynamic-module.md +53 -0
- package/rules/nestjs/caching/SKILL.md +51 -0
- package/rules/nestjs/caching/references/REFERENCE.md +13 -0
- package/rules/nestjs/caching/references/cache-patterns.md +183 -0
- package/rules/nestjs/configuration/SKILL.md +41 -0
- package/rules/nestjs/configuration/references/REFERENCE.md +13 -0
- package/rules/nestjs/configuration/references/config-patterns.md +184 -0
- package/rules/nestjs/controllers-services/SKILL.md +63 -0
- package/rules/nestjs/controllers-services/references/REFERENCE.md +14 -0
- package/rules/nestjs/controllers-services/references/controller-patterns.md +119 -0
- package/rules/nestjs/controllers-services/references/service-patterns.md +129 -0
- package/rules/nestjs/database/SKILL.md +102 -0
- package/rules/nestjs/database/references/REFERENCE.md +14 -0
- package/rules/nestjs/database/references/typeorm-patterns.md +156 -0
- package/rules/nestjs/deployment/SKILL.md +36 -0
- package/rules/nestjs/deployment/references/REFERENCE.md +13 -0
- package/rules/nestjs/deployment/references/deployment-patterns.md +140 -0
- package/rules/nestjs/documentation/SKILL.md +64 -0
- package/rules/nestjs/documentation/references/REFERENCE.md +13 -0
- package/rules/nestjs/documentation/references/swagger-patterns.md +139 -0
- package/rules/nestjs/error-handling/SKILL.md +55 -0
- package/rules/nestjs/error-handling/references/REFERENCE.md +13 -0
- package/rules/nestjs/error-handling/references/exception-filters.md +152 -0
- package/rules/nestjs/file-uploads/SKILL.md +35 -0
- package/rules/nestjs/file-uploads/references/REFERENCE.md +13 -0
- package/rules/nestjs/file-uploads/references/upload-patterns.md +125 -0
- package/rules/nestjs/observability/SKILL.md +39 -0
- package/rules/nestjs/observability/references/REFERENCE.md +13 -0
- package/rules/nestjs/observability/references/logging-metrics.md +175 -0
- package/rules/nestjs/performance/SKILL.md +60 -0
- package/rules/nestjs/performance/references/REFERENCE.md +13 -0
- package/rules/nestjs/performance/references/performance-patterns.md +107 -0
- package/rules/nestjs/real-time/SKILL.md +45 -0
- package/rules/nestjs/real-time/references/REFERENCE.md +13 -0
- package/rules/nestjs/real-time/references/websocket-patterns.md +121 -0
- package/rules/nestjs/scheduling/SKILL.md +39 -0
- package/rules/nestjs/scheduling/references/REFERENCE.md +13 -0
- package/rules/nestjs/scheduling/references/scheduling-patterns.md +137 -0
- package/rules/nestjs/search/SKILL.md +41 -0
- package/rules/nestjs/search/references/REFERENCE.md +13 -0
- package/rules/nestjs/search/references/search-patterns.md +137 -0
- package/rules/nestjs/security/SKILL.md +87 -0
- package/rules/nestjs/security/references/REFERENCE.md +14 -0
- package/rules/nestjs/security/references/authentication.md +151 -0
- package/rules/nestjs/testing/SKILL.md +40 -0
- package/rules/nestjs/testing/references/REFERENCE.md +14 -0
- package/rules/nestjs/testing/references/unit-testing.md +179 -0
- package/rules/nestjs/transport/SKILL.md +45 -0
- package/rules/nestjs/transport/references/REFERENCE.md +13 -0
- package/rules/nestjs/transport/references/microservices-patterns.md +170 -0
- package/rules/nextjs/app-router/SKILL.md +46 -0
- package/rules/nextjs/app-router/references/REFERENCE.md +14 -0
- package/rules/nextjs/app-router/references/routing-patterns.md +182 -0
- package/rules/nextjs/architecture/SKILL.md +44 -0
- package/rules/nextjs/architecture/references/fsd-structure.md +77 -0
- package/rules/nextjs/authentication/SKILL.md +29 -0
- package/rules/nextjs/authentication/references/auth-implementation.md +73 -0
- package/rules/nextjs/caching/SKILL.md +66 -0
- package/rules/nextjs/caching/references/REFERENCE.md +13 -0
- package/rules/nextjs/caching/references/cache-strategies.md +168 -0
- package/rules/nextjs/data-access-layer/SKILL.md +33 -0
- package/rules/nextjs/data-access-layer/references/patterns.md +66 -0
- package/rules/nextjs/data-fetching/SKILL.md +59 -0
- package/rules/nextjs/data-fetching/references/REFERENCE.md +13 -0
- package/rules/nextjs/data-fetching/references/fetch-patterns.md +160 -0
- package/rules/nextjs/internationalization/SKILL.md +105 -0
- package/rules/nextjs/internationalization/references/REFERENCE.md +13 -0
- package/rules/nextjs/internationalization/references/i18n-patterns.md +180 -0
- package/rules/nextjs/optimization/SKILL.md +64 -0
- package/rules/nextjs/optimization/references/REFERENCE.md +13 -0
- package/rules/nextjs/optimization/references/optimization-patterns.md +190 -0
- package/rules/nextjs/rendering/SKILL.md +91 -0
- package/rules/nextjs/rendering/references/REFERENCE.md +13 -0
- package/rules/nextjs/rendering/references/rendering-modes.md +163 -0
- package/rules/nextjs/server-actions/SKILL.md +46 -0
- package/rules/nextjs/server-actions/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-actions/references/action-patterns.md +188 -0
- package/rules/nextjs/server-components/SKILL.md +52 -0
- package/rules/nextjs/server-components/references/REFERENCE.md +13 -0
- package/rules/nextjs/server-components/references/component-patterns.md +175 -0
- package/rules/nextjs/state-management/SKILL.md +73 -0
- package/rules/nextjs/state-management/references/REFERENCE.md +13 -0
- package/rules/nextjs/state-management/references/state-patterns.md +218 -0
- package/rules/nextjs/styling/SKILL.md +31 -0
- package/rules/nextjs/styling/references/implementation.md +56 -0
- package/rules/react/component-patterns/SKILL.md +66 -0
- package/rules/react/component-patterns/references/REFERENCE.md +126 -0
- package/rules/react/hooks/SKILL.md +60 -0
- package/rules/react/hooks/references/REFERENCE.md +132 -0
- package/rules/react/hooks.rule.md +79 -0
- package/rules/react/performance/SKILL.md +69 -0
- package/rules/react/performance/references/REFERENCE.md +143 -0
- package/rules/react/security/SKILL.md +46 -0
- package/rules/react/security/references/REFERENCE.md +170 -0
- package/rules/react/state-management/SKILL.md +56 -0
- package/rules/react/state-management/references/REFERENCE.md +137 -0
- package/rules/react/testing/SKILL.md +45 -0
- package/rules/react/testing/references/REFERENCE.md +149 -0
- package/rules/react/tooling/SKILL.md +39 -0
- package/rules/react/typescript/SKILL.md +53 -0
- package/rules/rust/actix-web/SKILL.md +160 -0
- package/rules/rust/actix-web/references/REFERENCE.md +13 -0
- package/rules/rust/actix-web/references/handler-patterns.md +198 -0
- package/rules/rust/async-graphql/SKILL.md +228 -0
- package/rules/rust/async-graphql/references/REFERENCE.md +13 -0
- package/rules/rust/async-graphql/references/schema-patterns.md +215 -0
- package/rules/rust/axum/SKILL.md +161 -0
- package/rules/rust/axum/references/REFERENCE.md +14 -0
- package/rules/rust/axum/references/handler-patterns.md +97 -0
- package/rules/rust/bevy/SKILL.md +206 -0
- package/rules/rust/bevy/references/REFERENCE.md +13 -0
- package/rules/rust/bevy/references/ecs-patterns.md +226 -0
- package/rules/rust/clap/SKILL.md +217 -0
- package/rules/rust/clap/references/REFERENCE.md +13 -0
- package/rules/rust/clap/references/derive-patterns.md +205 -0
- package/rules/rust/core/SKILL.md +154 -0
- package/rules/rust/core/references/REFERENCE.md +14 -0
- package/rules/rust/core/references/error-handling.md +92 -0
- package/rules/rust/diesel-orm/SKILL.md +176 -0
- package/rules/rust/diesel-orm/references/REFERENCE.md +13 -0
- package/rules/rust/diesel-orm/references/schema-patterns.md +206 -0
- package/rules/rust/rocket/SKILL.md +182 -0
- package/rules/rust/rocket/references/REFERENCE.md +13 -0
- package/rules/rust/rocket/references/handler-patterns.md +209 -0
- package/rules/rust/sea-orm/SKILL.md +230 -0
- package/rules/rust/sea-orm/references/REFERENCE.md +13 -0
- package/rules/rust/sea-orm/references/entity-patterns.md +221 -0
- package/rules/rust/serde-serialization/SKILL.md +150 -0
- package/rules/rust/serde-serialization/references/REFERENCE.md +13 -0
- package/rules/rust/serde-serialization/references/serialization-patterns.md +199 -0
- package/rules/rust/sqlx-database/SKILL.md +140 -0
- package/rules/rust/sqlx-database/references/REFERENCE.md +13 -0
- package/rules/rust/sqlx-database/references/query-patterns.md +210 -0
- package/rules/rust/tauri/SKILL.md +180 -0
- package/rules/rust/tauri/references/REFERENCE.md +13 -0
- package/rules/rust/tauri/references/command-patterns.md +209 -0
- package/rules/rust/tokio-runtime/SKILL.md +167 -0
- package/rules/rust/tokio-runtime/references/REFERENCE.md +14 -0
- package/rules/rust/tokio-runtime/references/async-patterns.md +137 -0
- package/rules/rust/tokio-runtime/references/synchronization.md +152 -0
- package/rules/rust/tonic/SKILL.md +231 -0
- package/rules/rust/tonic/references/REFERENCE.md +13 -0
- package/rules/rust/tonic/references/service-patterns.md +213 -0
- package/rules/rust/tracing/SKILL.md +214 -0
- package/rules/rust/tracing/references/REFERENCE.md +13 -0
- package/rules/rust/tracing/references/instrumentation.md +187 -0
- package/rules/typescript/best-practices/SKILL.md +108 -0
- package/rules/typescript/best-practices/references/REFERENCE.md +68 -0
- package/rules/typescript/language/SKILL.md +72 -0
- package/rules/typescript/language/references/REFERENCE.md +67 -0
- package/rules/typescript/patterns.rule.md +85 -0
- package/rules/typescript/security/SKILL.md +59 -0
- package/rules/typescript/security/references/REFERENCE.md +113 -0
- package/rules/typescript/tooling/SKILL.md +52 -0
- package/rules/typescript/tooling/references/REFERENCE.md +110 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Localization Reference
|
|
2
|
+
|
|
3
|
+
## Easy Localization Setup
|
|
4
|
+
|
|
5
|
+
Basic implementation in `main.dart`.
|
|
6
|
+
|
|
7
|
+
```dart
|
|
8
|
+
Future<void> main() async {
|
|
9
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
10
|
+
await EasyLocalization.ensureInitialized();
|
|
11
|
+
|
|
12
|
+
runApp(
|
|
13
|
+
EasyLocalization(
|
|
14
|
+
supportedLocales: const [Locale('en'), Locale('vi')],
|
|
15
|
+
path: 'assets/translations', // <-- Path to translations
|
|
16
|
+
fallbackLocale: const Locale('en'),
|
|
17
|
+
child: const MyApp(),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## JSON Translation Format
|
|
24
|
+
|
|
25
|
+
Default format for project assets.
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
// en.json
|
|
29
|
+
{
|
|
30
|
+
"app_title": "My App",
|
|
31
|
+
"welcome": "Welcome, {}!",
|
|
32
|
+
"items_count": {
|
|
33
|
+
"zero": "No items",
|
|
34
|
+
"one": "{} item",
|
|
35
|
+
"other": "{} items"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Google Sheets Integration
|
|
41
|
+
|
|
42
|
+
Use `sheet_loader_localization` to fetch and generate localizations from Google Sheets.
|
|
43
|
+
|
|
44
|
+
1. Add to `pubspec.yaml` under `dev_dependencies`.
|
|
45
|
+
2. Configure sheets URL/ID in `pubspec.yaml` or separate config.
|
|
46
|
+
3. Run `flutter pub run sheet_loader_localization:main`.
|
|
47
|
+
|
|
48
|
+
See [Sheet Loader Example](sheet-loader.md).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Google Sheets Localization Loader
|
|
2
|
+
|
|
3
|
+
Automating translation updates from Google Sheets using `sheet_loader_localization`.
|
|
4
|
+
|
|
5
|
+
## Configuration (`pubspec.yaml`)
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
dev_dependencies:
|
|
9
|
+
sheet_loader_localization: ^0.1.0
|
|
10
|
+
|
|
11
|
+
sheet_loader_localization:
|
|
12
|
+
# Google Sheet ID (find in the URL of your sheet)
|
|
13
|
+
doc_id: 'your_google_sheet_id_here'
|
|
14
|
+
sheet_id: '0' # Usually 0 for first sheet
|
|
15
|
+
output_path: 'assets/translations'
|
|
16
|
+
output_format: 'json' # Can be csv or json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Typical Sheet Format ([example sheet](https://docs.google.com/spreadsheets/d/1v2Y3e0Uvn0JTwHvsduNT70u7Fy9TG43DIcZYJxPu1ZA/edit?gid=1013756643#gid=1013756643))
|
|
20
|
+
|
|
21
|
+
| key | en | vi |
|
|
22
|
+
| :------------- | :------------ | :--------- |
|
|
23
|
+
| welcome | Welcome! | Chào mừng! |
|
|
24
|
+
| login.button | Login | Đăng nhập |
|
|
25
|
+
| errors.network | Network Error | Lỗi mạng |
|
|
26
|
+
|
|
27
|
+
## CLI Command
|
|
28
|
+
|
|
29
|
+
Run this command to synchronize your local asset files with the Google Sheet:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
flutter pub run sheet_loader_localization:main
|
|
33
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Flutter Navigator v1 (Imperative)
|
|
3
|
+
description: Standard Flutter navigation using Navigator 1.0 (push/pop).
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [navigation, navigator, flutter-core]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/app.dart']
|
|
8
|
+
keywords: [Navigator, push, pop, MaterialPageRoute, onGenerateRoute]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Navigator v1 Navigation
|
|
12
|
+
|
|
13
|
+
## **Priority: P0 (CRITICAL)**
|
|
14
|
+
|
|
15
|
+
Standard imperative navigation system built into Flutter using `Navigator` and `MaterialPageRoute`.
|
|
16
|
+
|
|
17
|
+
## Implementation Guidelines
|
|
18
|
+
|
|
19
|
+
- **Standard Push**: Use `Navigator.of(context).push(MaterialPageRoute(builder: (_) => Screen()))`.
|
|
20
|
+
- **Named Routes**: Define `routes` map in `MaterialApp` or use `onGenerateRoute` for dynamic routing.
|
|
21
|
+
- **Passing Arguments**:
|
|
22
|
+
- For named routes: Use `Navigator.pushNamed(context, '/path', arguments: data)`.
|
|
23
|
+
- For `onGenerateRoute`: Extract arguments using `settings.arguments`.
|
|
24
|
+
- **Returning Data**: `final result = await Navigator.push(...)` and `Navigator.pop(context, data)`.
|
|
25
|
+
- **Replacing Screens**: Use `pushReplacement` or `pushAndRemoveUntil` for auth/splash flows.
|
|
26
|
+
|
|
27
|
+
## Code Example
|
|
28
|
+
|
|
29
|
+
```dart
|
|
30
|
+
// Basic Push
|
|
31
|
+
Navigator.push(
|
|
32
|
+
context,
|
|
33
|
+
MaterialPageRoute(builder: (context) => const DetailScreen()),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Named Routes Configuration
|
|
37
|
+
MaterialApp(
|
|
38
|
+
initialRoute: '/',
|
|
39
|
+
onGenerateRoute: (settings) {
|
|
40
|
+
if (settings.name == '/details') {
|
|
41
|
+
final args = settings.arguments as Map;
|
|
42
|
+
return MaterialPageRoute(
|
|
43
|
+
builder: (context) => DetailScreen(id: args['id']),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Navigation with Arguments
|
|
51
|
+
Navigator.pushNamed(
|
|
52
|
+
context,
|
|
53
|
+
'/details',
|
|
54
|
+
arguments: {'id': 123},
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Anti-Patterns
|
|
59
|
+
|
|
60
|
+
- **Deep Nesting**: Avoid anonymous routes for complex apps; use `onGenerateRoute`.
|
|
61
|
+
- **Manual String Paths**: Always use constants for route names.
|
|
62
|
+
- **Context Leaks**: Ensure `BuildContext` is valid when calling `Navigator.of(context)`. Use `ScaffoldMessenger` or global keys if navigation is needed outside `build`.
|
|
63
|
+
|
|
64
|
+
## Reference & Examples
|
|
65
|
+
|
|
66
|
+
For centralized `onGenerateRoute` implementation:
|
|
67
|
+
See [references/on-generate-route.md](references/on-generate-route.md).
|
|
68
|
+
|
|
69
|
+
## Related Topics
|
|
70
|
+
|
|
71
|
+
idiomatic-flutter | widgets
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Navigator v1: Centralized onGenerateRoute
|
|
2
|
+
|
|
3
|
+
Using an abstract class for route names and a centralized router for navigation logic.
|
|
4
|
+
|
|
5
|
+
```dart
|
|
6
|
+
// route_names.dart
|
|
7
|
+
abstract class Routes {
|
|
8
|
+
static const home = '/';
|
|
9
|
+
static const details = '/details';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// app_router.dart
|
|
13
|
+
class AppRouter {
|
|
14
|
+
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
|
|
15
|
+
switch (settings.name) {
|
|
16
|
+
case Routes.home:
|
|
17
|
+
return MaterialPageRoute(builder: (_) => const HomeScreen());
|
|
18
|
+
|
|
19
|
+
case Routes.details:
|
|
20
|
+
// Safe argument extraction
|
|
21
|
+
final args = settings.arguments;
|
|
22
|
+
if (args is int) {
|
|
23
|
+
return MaterialPageRoute(
|
|
24
|
+
builder: (_) => DetailScreen(id: args),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return _errorRoute();
|
|
28
|
+
|
|
29
|
+
default:
|
|
30
|
+
return _errorRoute();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static Route<dynamic> _errorRoute() {
|
|
35
|
+
return MaterialPageRoute(
|
|
36
|
+
builder: (_) => const Scaffold(body: Center(child: Text('Error'))),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// main.dart
|
|
42
|
+
MaterialApp(
|
|
43
|
+
onGenerateRoute: AppRouter.onGenerateRoute,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// Usage
|
|
47
|
+
Navigator.pushNamed(context, Routes.details, arguments: 42);
|
|
48
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Flutter Performance
|
|
3
|
+
description: Optimization standards for rebuilds and memory.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [performance]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['lib/presentation/**', 'pubspec.yaml']
|
|
8
|
+
keywords: [const, buildWhen, ListView.builder, Isolate, RepaintBoundary]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Performance (P1)
|
|
12
|
+
|
|
13
|
+
- **Rebuilds**: Use `const` widgets and `buildWhen` / `select` for granular updates.
|
|
14
|
+
- **Lists**: Always use `ListView.builder` for item recycling.
|
|
15
|
+
- **Heavy Tasks**: Use `compute()` or `Isolates` for parsing/logic.
|
|
16
|
+
- **Repaints**: Use `RepaintBoundary` for complex animations. Use `debugRepaintRainbowEnabled` to debug.
|
|
17
|
+
- **Images**: Use `CachedNetworkImage` + `memCacheWidth`. `precachePicture` for SVGs.
|
|
18
|
+
|
|
19
|
+
```dart
|
|
20
|
+
BlocBuilder<UserBloc, UserState>(
|
|
21
|
+
buildWhen: (p, c) => p.id != c.id,
|
|
22
|
+
builder: (context, state) => Text(state.name),
|
|
23
|
+
)
|
|
24
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Flutter Networking (Retrofit & Dio)
|
|
3
|
+
description: HTTP networking standards using Dio and Retrofit with Auth interceptors.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [networking, retrofit, dio]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**/data_sources/**', '**/api/**']
|
|
8
|
+
keywords: [Retrofit, Dio, RestClient, GET, POST, Interceptor, refreshing]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Retrofit & Dio Networking
|
|
12
|
+
|
|
13
|
+
## **Priority: P0 (CRITICAL)**
|
|
14
|
+
|
|
15
|
+
Type-safe REST API communication using `Dio` and `Retrofit`.
|
|
16
|
+
|
|
17
|
+
## Structure
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
infrastructure/
|
|
21
|
+
├── data_sources/
|
|
22
|
+
│ ├── remote/ # Retrofit abstract classes
|
|
23
|
+
│ └── local/ # Cache/Storage
|
|
24
|
+
└── network/
|
|
25
|
+
├── dio_client.dart # Custom Dio setup
|
|
26
|
+
└── interceptors/ # Auth, Logging, Cache
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Implementation Guidelines
|
|
30
|
+
|
|
31
|
+
- **Retrofit Clients**: Define abstract classes with `@RestApi()`. Use standard HTTP annotations (`@GET`, `@POST`).
|
|
32
|
+
- **DTOs (Data Transfer Objects)**: Use `@freezed` and `json_serializable` for all response/request bodies.
|
|
33
|
+
- **Mapping**: Data sources MUST map DTOs to Domain Entities (e.g., `userDto.toDomain()`).
|
|
34
|
+
- **AuthInterceptor**: Logic for `Authorization: Bearer <token>` injection in `onRequest`.
|
|
35
|
+
- **Token Refresh**: Handle `401 Unauthorized` in `onError` by locking Dio, refreshing, and retrying.
|
|
36
|
+
- **Failures**: Map `DioException` to custom `Failure` objects (ServerFailure, NetworkFailure).
|
|
37
|
+
|
|
38
|
+
## Anti-Patterns
|
|
39
|
+
|
|
40
|
+
- **No Manual JSON Parsing**: Do not use `jsonDecode(response.body)`; use Retrofit's generated mappers.
|
|
41
|
+
- **No Global Dio**: Do not use a static global Dio instance; use dependency injection.
|
|
42
|
+
- **No Try-Catch in API**: Do not put `try-catch` inside the Retrofit interface methods.
|
|
43
|
+
|
|
44
|
+
## Reference & Examples
|
|
45
|
+
|
|
46
|
+
For RestClient definitions and Auth Interceptor implementation:
|
|
47
|
+
See [references/REFERENCE.md](references/REFERENCE.md).
|
|
48
|
+
|
|
49
|
+
## Related Topics
|
|
50
|
+
|
|
51
|
+
feature-based-clean-architecture | error-handling
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Retrofit & Dio Reference
|
|
2
|
+
|
|
3
|
+
Standards for API communication and networking logic.
|
|
4
|
+
|
|
5
|
+
## References
|
|
6
|
+
|
|
7
|
+
- [**RestClient Setup**](client-definition.md) - Standard Retrofit interface examples.
|
|
8
|
+
- [**Auth Interceptors**](auth-interceptor.md) - Handling Bearer tokens and Auth headers.
|
|
9
|
+
- [**Token Refresh Logic**](token-refresh.md) - The 401 Lock-Refresh-Retry pattern.
|
|
10
|
+
|
|
11
|
+
## **Quick Definition**
|
|
12
|
+
|
|
13
|
+
```dart
|
|
14
|
+
@RestApi()
|
|
15
|
+
abstract class ApiClient {
|
|
16
|
+
@GET("/items")
|
|
17
|
+
Future<List<ItemDto>> getItems(@Query("limit") int limit);
|
|
18
|
+
}
|
|
19
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Token Refresh Pattern
|
|
2
|
+
|
|
3
|
+
When a `401 Unauthorized` error occurs, the networking layer should handle the refresh cycle transparently.
|
|
4
|
+
|
|
5
|
+
## **Implementation Flow (Dio Interceptor)**
|
|
6
|
+
|
|
7
|
+
```dart
|
|
8
|
+
class AuthInterceptor extends QueuedInterceptorsWrapper {
|
|
9
|
+
final Dio dio;
|
|
10
|
+
final SecureStorage storage;
|
|
11
|
+
|
|
12
|
+
AuthInterceptor(this.dio, this.storage);
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
|
16
|
+
if (err.response?.statusCode == 401) {
|
|
17
|
+
// 1. Refresh the token
|
|
18
|
+
final newToken = await _performRefresh();
|
|
19
|
+
|
|
20
|
+
if (newToken != null) {
|
|
21
|
+
// 2. Retry the original request with new token
|
|
22
|
+
final options = err.requestOptions;
|
|
23
|
+
options.headers['Authorization'] = 'Bearer $newToken';
|
|
24
|
+
|
|
25
|
+
final response = await dio.fetch(options);
|
|
26
|
+
return handler.resolve(response);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return handler.next(err);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Future<String?> _performRefresh() async {
|
|
33
|
+
// Logic to call refresh endpoint and update storage
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## **Why QueuedInterceptorsWrapper?**
|
|
39
|
+
|
|
40
|
+
Using `QueuedInterceptorsWrapper` ensures that if multiple requests trigger a 401 at the same time, they are queued while the first one performs the token refresh, preventing multiple redundant refresh calls.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Flutter Riverpod State Management
|
|
3
|
+
description: Reactive state management using Riverpod 2.0 with code generation.
|
|
4
|
+
metadata:
|
|
5
|
+
labels: [state-management, riverpod, dependency-injection, reactive]
|
|
6
|
+
triggers:
|
|
7
|
+
files: ['**_provider.dart', '**_notifier.dart']
|
|
8
|
+
keywords: [riverpod, ProviderScope, ConsumerWidget, Notifier, AsyncValue, ref.watch, "@riverpod"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Riverpod State Management
|
|
12
|
+
|
|
13
|
+
## **Priority: P0 (CRITICAL)**
|
|
14
|
+
|
|
15
|
+
Type-safe, compile-time safe reactive state management using `riverpod` and `riverpod_generator`.
|
|
16
|
+
|
|
17
|
+
## Structure
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
lib/
|
|
21
|
+
├── providers/ # Global providers and services
|
|
22
|
+
└── features/
|
|
23
|
+
└── user/
|
|
24
|
+
├── providers/ # Feature-specific providers
|
|
25
|
+
└── models/ # @freezed domain models
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Implementation Guidelines
|
|
29
|
+
|
|
30
|
+
- **Generator First**: Use `@riverpod` annotations and `riverpod_generator`. Avoid manual `Provider` definitions.
|
|
31
|
+
- **Immutability**: Maintain immutable states. Use `Freezed` for all state models.
|
|
32
|
+
- **Provider Methods**:
|
|
33
|
+
- `ref.watch()`: Use inside `build()` to rebuild on changes.
|
|
34
|
+
- `ref.listen()`: Use for side-effects (navigation, dialogs).
|
|
35
|
+
- `ref.read()`: Use ONLY in callbacks (onPressed).
|
|
36
|
+
- **Asynchronous Data**: Use `AsyncNotifier` for complex async logic. Access data via `.when()` or `AsyncValue` pattern-matching.
|
|
37
|
+
- **Architecture**: Enforce 3-layer separation (Data, Domain, Presentation).
|
|
38
|
+
- **Linting**: Enable `riverpod_lint` and `custom_lint` for dependency cycle detection.
|
|
39
|
+
|
|
40
|
+
## Anti-Patterns
|
|
41
|
+
|
|
42
|
+
- **Building Inside Providers**: Don't perform side-effects inside provider initialization.
|
|
43
|
+
- **Context Access**: Never pass `BuildContext` into a Notifier/Provider.
|
|
44
|
+
- **Dynamic Providers**: Avoid local provider instantiation; keep them global.
|
|
45
|
+
|
|
46
|
+
## Reference & Examples
|
|
47
|
+
|
|
48
|
+
For architecture details, best practices, and testing overrides:
|
|
49
|
+
See [references/architecture.md](references/architecture.md), [references/best-practices.md](references/best-practices.md), and [references/testing.md](references/testing.md).
|
|
50
|
+
|
|
51
|
+
## Related Topics
|
|
52
|
+
|
|
53
|
+
feature-based-clean-architecture | dependency-injection | testing
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Architecture & Layers
|
|
2
|
+
|
|
3
|
+
## 1. Data Layer: Repository Interface & Providers
|
|
4
|
+
|
|
5
|
+
Always separate interface from implementation to enable easy mocking.
|
|
6
|
+
|
|
7
|
+
```dart
|
|
8
|
+
// domain/repository/user_repository.dart
|
|
9
|
+
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
10
|
+
import '../../data/repository/api_user_repository.dart';
|
|
11
|
+
import '../models/user.dart';
|
|
12
|
+
|
|
13
|
+
part 'user_repository.g.dart';
|
|
14
|
+
|
|
15
|
+
// 1. Define the Provider returning the INTERFACE
|
|
16
|
+
@riverpod
|
|
17
|
+
UserRepository userRepository(UserRepositoryRef ref) {
|
|
18
|
+
// Return the specific implementation here.
|
|
19
|
+
// Can be swapped for MockUserRepository based on environment flags.
|
|
20
|
+
return ApiUserRepository(ref.watch(apiClientProvider));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 2. Define the Interface
|
|
24
|
+
abstract interface class UserRepository {
|
|
25
|
+
Future<List<User>> getAllUsers();
|
|
26
|
+
Future<void> deleteUser(String id);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
// data/repository/api_user_repository.dart
|
|
32
|
+
class ApiUserRepository implements UserRepository {
|
|
33
|
+
final UserApiClient _client;
|
|
34
|
+
|
|
35
|
+
ApiUserRepository(this._client);
|
|
36
|
+
|
|
37
|
+
@override
|
|
38
|
+
Future<List<User>> getAllUsers() async {
|
|
39
|
+
return _client.fetchUsers();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 2. Presentation Layer: AsyncNotifier Controller
|
|
45
|
+
|
|
46
|
+
Use `AsyncNotifier` (via `@riverpod` class) to handle loading/error/data states naturally.
|
|
47
|
+
|
|
48
|
+
```dart
|
|
49
|
+
// features/users/user_controller.dart
|
|
50
|
+
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
51
|
+
import '../../domain/repository/user_repository.dart';
|
|
52
|
+
import '../../domain/models/user.dart';
|
|
53
|
+
|
|
54
|
+
part 'user_controller.g.dart';
|
|
55
|
+
|
|
56
|
+
@riverpod
|
|
57
|
+
class UserController extends _$UserController {
|
|
58
|
+
@override
|
|
59
|
+
FutureOr<List<User>> build() async {
|
|
60
|
+
// 1. Fetch initial data
|
|
61
|
+
return ref.watch(userRepositoryProvider).getAllUsers();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 2. Mutation Methods
|
|
65
|
+
Future<void> deleteUser(String id) async {
|
|
66
|
+
// Set loading state (optional, for optimisic UI usually prefer local)
|
|
67
|
+
// state = const AsyncLoading();
|
|
68
|
+
|
|
69
|
+
// Perform action
|
|
70
|
+
state = await AsyncValue.guard(() async {
|
|
71
|
+
await ref.read(userRepositoryProvider).deleteUser(id);
|
|
72
|
+
// Refresh logic:
|
|
73
|
+
// Option A: Refetch everything
|
|
74
|
+
return ref.refresh(userRepositoryProvider).getAllUsers();
|
|
75
|
+
|
|
76
|
+
// Option B: Optimistic Update (Better performance)
|
|
77
|
+
// final currentList = state.requireValue;
|
|
78
|
+
// return currentList.where((u) => u.id != id).toList();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 3. UI Layer: ConsumerWidget
|
|
85
|
+
|
|
86
|
+
Handle all 3 states (`data`, `loading`, `error`) using `.when`.
|
|
87
|
+
|
|
88
|
+
```dart
|
|
89
|
+
// features/users/user_list_screen.dart
|
|
90
|
+
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
91
|
+
|
|
92
|
+
class UserListScreen extends ConsumerWidget {
|
|
93
|
+
const UserListScreen({super.key});
|
|
94
|
+
|
|
95
|
+
@override
|
|
96
|
+
Widget build(BuildContext context, WidgetRef ref) {
|
|
97
|
+
// 1. Watch the Notifier Provider
|
|
98
|
+
final state = ref.watch(userControllerProvider);
|
|
99
|
+
|
|
100
|
+
return Scaffold(
|
|
101
|
+
body: state.when(
|
|
102
|
+
data: (users) => ListView.builder(
|
|
103
|
+
itemCount: users.length,
|
|
104
|
+
itemBuilder: (ctx, index) {
|
|
105
|
+
final user = users[index];
|
|
106
|
+
return ListTile(
|
|
107
|
+
title: Text(user.name),
|
|
108
|
+
// 2. Call mutation methods via .read()
|
|
109
|
+
trailing: IconButton(
|
|
110
|
+
icon: const Icon(Icons.delete),
|
|
111
|
+
onPressed: () {
|
|
112
|
+
ref.read(userControllerProvider.notifier).deleteUser(user.id);
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
),
|
|
118
|
+
loading: () => const Center(child: CircularProgressIndicator()),
|
|
119
|
+
error: (err, st) => Center(child: Text('Error: $err')),
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Best Practices & DCM Rules
|
|
2
|
+
|
|
3
|
+
## Code Generation (`riverpod_generator`)
|
|
4
|
+
|
|
5
|
+
Always use code generation. It prevents syntax errors and handles "family" parameters automatically.
|
|
6
|
+
|
|
7
|
+
**❌ Bad (Legacy)**
|
|
8
|
+
|
|
9
|
+
```dart
|
|
10
|
+
final myProvider = Provider<String>((ref) => 'Hello'); // Manual typing, error prone
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**✅ Good (Generated)**
|
|
14
|
+
|
|
15
|
+
```dart
|
|
16
|
+
@riverpod
|
|
17
|
+
String my(MyRef ref) => 'Hello'; // Type-safe, auto-disposal by default
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Immutability & State Updates
|
|
21
|
+
|
|
22
|
+
Riverpod relies on strict object equality (`==`) to detect changes.
|
|
23
|
+
|
|
24
|
+
**❌ Bad (Mutation)**
|
|
25
|
+
|
|
26
|
+
```dart
|
|
27
|
+
state.add(newItem); // Same reference, listeners won't fire
|
|
28
|
+
state = state;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**✅ Good (New Reference)**
|
|
32
|
+
|
|
33
|
+
```dart
|
|
34
|
+
state = [...state, newItem]; // New list, new reference
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## DCM & Linter Rules (`riverpod_lint`)
|
|
38
|
+
|
|
39
|
+
Enable strict rules in `analysis_options.yaml` via `custom_lint`.
|
|
40
|
+
|
|
41
|
+
### 1. `avoid-ref-read-inside-build`
|
|
42
|
+
|
|
43
|
+
**Rule**: Never use `ref.read` inside the `build()` method. It causes bugs where widgets don't update on change.
|
|
44
|
+
**Correct**: Use `ref.watch`.
|
|
45
|
+
|
|
46
|
+
### 2. `avoid-calling-notifier-members-inside-build`
|
|
47
|
+
|
|
48
|
+
**Rule**: Do not trigger side effects (API calls, internal mutations) inside `build`.
|
|
49
|
+
**Correct**: Use `build()` purely for initialization. Trigger actions via user interaction (onPressed) or `useEffect` (flutter_hooks).
|
|
50
|
+
|
|
51
|
+
### 3. `dispose-provided-instances`
|
|
52
|
+
|
|
53
|
+
**Rule**: If a provider creates a `ChangeNotifier` or stream controller, make sure it is disposed.
|
|
54
|
+
**Correct**:
|
|
55
|
+
|
|
56
|
+
```dart
|
|
57
|
+
@riverpod
|
|
58
|
+
StreamController myController(Ref ref) {
|
|
59
|
+
final controller = StreamController();
|
|
60
|
+
ref.onDispose(controller.close); // ✅ Register disposal
|
|
61
|
+
return controller;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 4. `use-ref-and-state-synchronously`
|
|
66
|
+
|
|
67
|
+
**Rule**: Don't use `ref` or setter `state` after an `await` without checking if the provider is still active.
|
|
68
|
+
**Context**: If a provider is disposed (e.g. user leaves screen) while an async task is running, setting state will throw.
|
|
69
|
+
|
|
70
|
+
**✅ Good**:
|
|
71
|
+
|
|
72
|
+
```dart
|
|
73
|
+
final result = await repo.fetch();
|
|
74
|
+
if (ref.context.mounted) { // Or simply rely on `AsyncValue.guard` which handles this somewhat safely
|
|
75
|
+
state = AsyncData(result);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## `keepAlive` Strategy
|
|
80
|
+
|
|
81
|
+
By default, auto-generated providers are `autoDispose`. This is good for memory but bad for caching.
|
|
82
|
+
|
|
83
|
+
- **Use `keepAlive: true`** for global data (User Session, App Config).
|
|
84
|
+
- **Use Default (`keepAlive: false`)** for screen-specific data (Product Details), so it clears to save memory when the screen is popped.
|
|
85
|
+
|
|
86
|
+
```dart
|
|
87
|
+
@Riverpod(keepAlive: true)
|
|
88
|
+
class UserSession extends _$UserSession { ... }
|
|
89
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Testing with Riverpod
|
|
2
|
+
|
|
3
|
+
Riverpod is designed for testability. You do not need complex dependency injection containers; you simply "override" providers in the test scope.
|
|
4
|
+
|
|
5
|
+
## Unit Testing (Notifiers)
|
|
6
|
+
|
|
7
|
+
Test notifiers in isolation using a `ProviderContainer`.
|
|
8
|
+
|
|
9
|
+
```dart
|
|
10
|
+
test('increment adds 1 to state', () {
|
|
11
|
+
// 1. Create container
|
|
12
|
+
final container = ProviderContainer();
|
|
13
|
+
addTearDown(container.dispose);
|
|
14
|
+
|
|
15
|
+
// 2. Listen to provider (required to initialize it)
|
|
16
|
+
container.listen(counterProvider, (_, __) {});
|
|
17
|
+
|
|
18
|
+
// 3. Act
|
|
19
|
+
container.read(counterProvider.notifier).increment();
|
|
20
|
+
|
|
21
|
+
// 4. Assert
|
|
22
|
+
expect(container.read(counterProvider), 1);
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Widget/Integration Testing (Overrides)
|
|
27
|
+
|
|
28
|
+
When testing widgets, wrap them in a `ProviderScope` and override the repository providers with mocks.
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
testWidgets('shows loading then data', (tester) async {
|
|
32
|
+
final mockRepo = MockUserRepository();
|
|
33
|
+
when(() => mockRepo.getAllUsers()).thenAnswer((_) async => [User(id: 1, name: 'Bob')]);
|
|
34
|
+
|
|
35
|
+
await tester.pumpWidget(
|
|
36
|
+
ProviderScope(
|
|
37
|
+
overrides: [
|
|
38
|
+
// 🔑 Replace the REAL repository with the MOCK
|
|
39
|
+
userRepositoryProvider.overrideWithValue(mockRepo),
|
|
40
|
+
],
|
|
41
|
+
child: const MaterialApp(home: UserListScreen()),
|
|
42
|
+
),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Assert Loading
|
|
46
|
+
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
|
47
|
+
|
|
48
|
+
// Assert Data
|
|
49
|
+
await tester.pumpAndSettle();
|
|
50
|
+
expect(find.text('Bob'), findsOneWidget);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Mocking `AsyncNotifiers`
|
|
55
|
+
|
|
56
|
+
Sometimes you want to mock the _entire_ Controller/Notifier state, not just the repository.
|
|
57
|
+
|
|
58
|
+
```dart
|
|
59
|
+
// 1. Extend the generated class (or create a mock)
|
|
60
|
+
class MockUserController extends AutoDisposeAsyncNotifier<List<User>>
|
|
61
|
+
implements UserController {
|
|
62
|
+
@override
|
|
63
|
+
FutureOr<List<User>> build() => [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. Override in ProviderScope
|
|
67
|
+
ProviderScope(
|
|
68
|
+
overrides: [
|
|
69
|
+
userControllerProvider.overrideWith(() => MockUserController()),
|
|
70
|
+
],
|
|
71
|
+
child: ...
|
|
72
|
+
)
|
|
73
|
+
```
|