一键导入
create-feature
Use when asked to create a new feature or sub-feature following the DDD-lite architecture, or set up state, view, and page layers for a domain.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when asked to create a new feature or sub-feature following the DDD-lite architecture, or set up state, view, and page layers for a domain.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Process for creating mock data sources in the infrastructure layer.
Process for building, exporting, and registering reusable UI components in the packages/app_ui package. References design-to-code.md for design tokens (typography, colors, spacing).
Outlines the process for converting Figma designs into a structured, platform-aware Flutter implementation.
Use when completely removing the web platform from the project. Details how to delete directories/configurations, clean up app_ui web components, remove web-specific feature views (*_view_web.dart), prune conditional imports/logic, and run validation.
Use when asked to add, update, or group routes using GoRouter, or manage redirection guards.
Step-by-step workflow for creating, configuring, and updating Flutter golden tests using the Alchemist package.
| name | create-feature |
| description | Use when asked to create a new feature or sub-feature following the DDD-lite architecture, or set up state, view, and page layers for a domain. |
This skill outlines the standard process for creating a new feature in the project, following the DDD-lite architecture. This guide covers both simple domains (like Settings) and complex domains (like Auth).
Features are located in lib/src/features/. The structure depends on the
complexity of the domain.
If the feature is self-contained and doesn't require separate sub-modules,
keep it flat under the feature root folder. No _self folder is used.
lib/src/features/settings/
├── state/ # Domain-specific state
│ └── settings_notifier.dart
├── view/ # UI implementation
│ ├── settings_page.dart # Entry point & Bindings
│ ├── settings_view.dart # Mobile UI
│ └── settings_view_web.dart # Web UI
└── settings.dart # Feature barrel file
If the feature has multiple sub-domains, it is structured as a complex
domain. The _self folder is used for root-level domain control, shared
domain state (e.g., auth status, user metadata, cache), and layout/shell
views that host or wrap the sub-features.
lib/src/features/auth/
├── _self/ # Shared domain logic & state
│ └── state/
│ └── auth_notifier.dart # Global auth status
├── login/ # Sub-domain feature
│ ├── state/ # Local feature state
│ │ └── login_notifier.dart
│ ├── view/ # Local UI
│ │ ├── login_page.dart
│ │ ├── login_view.dart
│ │ └── login_view_web.dart
│ └── login.dart # Sub-domain barrel
└── auth.dart # Domain barrel (exports _self and sub-domains)
| Entity | Convention | Example |
|---|---|---|
| Folder | snake_case | auth_login |
| File | snake_case | login_page.dart |
| Page Class | PascalCase + Page | LoginPage |
| View Class | PascalCase + View | LoginView |
| Web View Class | PascalCase + ViewWeb | LoginViewWeb |
| Props Class | PascalCase + Props | LoginProps |
The Page widget delegates logic to _Bindings, which prepares the
ViewProps.
class MyFeaturePage extends StatelessWidget {
const MyFeaturePage({super.key});
@override
Widget build(BuildContext context) {
return _Bindings(
viewBuilder: (_, props) {
// Simple pages can return the view directly.
// Complex UIs should use MemoizedView for performance.
if (isComplex) {
return MemoizedView(
props: props,
builder: (context, memProps) => kIsWeb
? MyFeatureViewWeb(props: memProps)
: MyFeatureView(props: memProps),
);
}
return kIsWeb
? MyFeatureViewWeb(props: props)
: MyFeatureView(props: props);
},
);
}
}
ViewProps separates Data (used for equality) from Callbacks
(ignored for equality).
[!IMPORTANT] CRITICAL RULE FOR AI: NEVER include callback functions (like
VoidCallback,Function, etc.) in theequalityPropertieslist of aViewPropssubclass. The base class has a runtime assertion that will crash the app if functions are included. ONLY include data fields.
[!CAUTION] STALE CLOSURES: Because
MemoizedViewignores callbacks in equality checks, your UI might hold a reference to an "old" closure. Never capture variables fromref.watchin a callback. Always useref.readinside the closure to ensure you have the fresh state.
final class MyProps extends ViewProps {
const MyProps({required this.data, required this.onAction});
final String data;
final VoidCallback onAction;
// CRITICAL: Only include data fields. Do NOT include functions/callbacks here!
@override
List<Object?> get equalityProperties => [data]; // Exclude onAction
}
// ... inside _Bindings ...
return viewBuilder(
context,
MyProps(
data: ref.watch(myProvider).data, // Data for equality
onAction: () {
// SAFE: ref.read ensures fresh state when callback is executed
ref.read(myProvider.notifier).doSomething();
// DANGEROUS: capturing 'data' here will cause stale closure bugs
// print(data);
},
),
);
Feature state and notifiers live in the state/ directory of a feature.
dart_mappable (@MappableClass()) for state
classes to get built-in immutability (copyWith) and equality.import 'package:dart_mappable/dart_mappable.dart';
import 'package:framework/framework.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'login_notifier.mapper.dart';
part 'login_notifier.g.dart';
/// Local form state tracking for the Auth flow.
@MappableClass()
class LoginState with LoginStateMappable implements FailureInterface {
const LoginState({this.isLoggingIn = false, this.failure});
factory LoginState.loading() => const LoginState(isLoggingIn: true);
final bool isLoggingIn;
@override
final Failure? failure;
}
@riverpod
class LoginNotifier extends _$LoginNotifier {
@override
LoginState build() => const LoginState();
void login() {
state = LoginState.loading();
// Action logic here...
}
}
Follow the Routing Management Skill to define the route
class and register it in the appropriate group (open, unauthenticated,
or authenticated).
_self and all sub-domain barrels.features/features.dart): Export all domain barrels.