关于 flutter-bloc-development
flutter-bloc-development 是面向 Flutter 代码库的开发技能,规范 BLoC 状态管理、按功能分层、基于 Repository 的数据访问和设计系统常量,涵盖功能结构、界面与数据流。
主要功能
- 01按功能组织项目结构
- 02BLoC 事件与状态模式
- 03Repository 和数据源分层
- 04界面使用设计系统常量
- 05加载、成功与错误状态流
适用场景
- 01实现新的 Flutter 功能
- 02避免在组件中编写业务逻辑
- 03将后端调用隔离到数据源
- 04替换硬编码的界面样式值
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ abdelhakrazi/ flutter-bloc-clean-architecture-skill --skill flutter-bloc-development安装后在 Claude Code 中输入/flutter-bloc-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.txtREADME.mdexamples/earnings/earnings.dartexamples/earnings/bloc/earnings_bloc.dartearnings_state.dartearnings_event.dartexamples/earnings/data/data.dartexamples/earnings/data/datasources/earnings_datasource.dartexamples/earnings/data/models/earnings_summary.dartexamples/earnings/data/repositories/earnings_repository.dartexamples/earnings/view/earnings_page.dartview.dartexamples/earnings/view/widgets/daily_earnings_list.dartearnings_summary_card.dartexamples/shared/data/data.dartexamples/shared/data/datasources/api_client.dartuser_datasource.dartexamples/shared/data/models/api_response.dartuser.dartexamples/shared/widgets/trip_summary_card.dart详细介绍
Flutter BLoC 开发
此技能要求在此代码库的所有 Flutter 开发中使用 BLoC 状态管理、严格分层,并强制使用设计系统常量。
决策树:选择方案
User task → What are they building?
│
├─ New screen/feature → Full feature implementation:
│ 1. Create feature folder (lib/[feature]/)
│ 2. Define BLoC (bloc/[feature]_event.dart, _state.dart, _bloc.dart)
│ 3. Create data layer (data/datasources/, data/repositories/, data/models/)
│ 4. Build UI (view/[feature]_page.dart, view/widgets/)
│ 5. Create barrel files ([feature].dart, data/data.dart, view/view.dart)
│
├─ New widget only → Presentation layer:
│ 1. Feature-specific: feature/view/widgets/
│ 2. Shared/reusable: shared/widgets/
│ 3. Use design system constants (NO hardcoded values)
│ 4. Connect to existing BLoC if needed
│
├─ Data integration → Data layer only:
│ 1. Create datasource (feature/data/datasources/)
│ 2. Create repository (feature/data/repositories/)
│ 3. Wire up in existing or new BLoC
│
└─ Refactoring → Identify violations:
1. Check for hardcoded colors/spacing/typography
2. Check for business logic in UI
3. Check for direct SDK calls outside datasources
4. Check for missing Loading state before async operations
5. Check for missing Equatable on Events/States
6. Check for improper error handling (use SnackBar + AppColors.error)
架构概览
以功能为先的结构(BLoC 官方建议):
lib/
├── [feature]/ # Feature folder (e.g., earnings/, auth/, trips/)
│ ├── bloc/
│ │ ├── [feature]_bloc.dart
│ │ ├── [feature]_event.dart
│ │ └── [feature]_state.dart
│ ├── data/
│ │ ├── datasources/ # Feature-specific API calls
│ │ ├── repositories/ # Data orchestration
│ │ ├── models/ # Feature-specific DTOs
│ │ └── data.dart # Data layer barrel file
│ ├── view/
│ │ ├── [feature]_page.dart # Main screen
│ │ ├── widgets/ # Feature-specific widgets
│ │ └── view.dart # View barrel file
│ └── [feature].dart # Feature barrel file
├── shared/ # Cross-feature code
│ ├── data/
│ │ ├── datasources/ # Shared API clients (ApiClient, UserDataSource)
│ │ ├── models/ # Shared models (User, ApiResponse)
│ │ └── data.dart # Shared data barrel file
│ ├── widgets/ # Reusable UI components
│ └── utils/ # Design system (colors, spacing, typography)
└── app.dart # App entry point
何时使用功能目录或共享数据目录
| 场景 | 位置 | 示例 |
|---|---|---|
| 仅供一个功能使用的 API 端点 | feature/data/ | EarningsDataSource → /api/earnings/... |
| 多个功能使用的 API 客户端/服务 | shared/data/ | ApiClient、UserDataSource |
| 仅供一个功能使用的模型 | feature/data/models/ | EarningsSummary |
| 多个功能使用的模型 | shared/data/models/ | User、ApiResponse |
聚合文件——每层只需一次导入:
// Feature barrel: earnings/earnings.dart
export 'bloc/earnings_bloc.dart';
export 'bloc/earnings_event.dart';
export 'bloc/earnings_state.dart';
export 'data/data.dart';
export 'view/view.dart';
// Data layer barrel: earnings/data/data.dart
export 'datasources/earnings_datasource.dart';
export 'repositories/earnings_repository.dart';
export 'models/earnings_summary.dart';
// Shared data barrel: shared/data/data.dart
export 'datasources/api_client.dart';
export 'datasources/user_datasource.dart';
export 'models/user.dart';
关键规则:
- 所有状态变更都通过 BLoC 流转
- 数据源之外不得直接调用后端 SDK
- 不得使用硬编码值(颜色、间距、排版)
- 所有数据访问都使用仓库模式
- 功能专属代码放在功能目录中
- 共享代码(供两个或更多功能使用)放在
shared/中
BLoC 实现
Event → State → BLoC(每个功能包含三个文件)
事件——用户操作和系统触发:
abstract class FeatureEvent extends Equatable {
const FeatureEvent();
@override
List<Object?> get props => [];
}
class FeatureActionRequested extends FeatureEvent {
final String param;
const FeatureActionRequested({required this.param});
@override
List<Object> get props => [param];
}
状态——所有可能的 UI 状态:
abstract class FeatureState extends Equatable {
const FeatureState();
@override
List<Object?> get props => [];
}
class FeatureInitial extends FeatureState {}
class FeatureLoading extends FeatureState {}
class FeatureSuccess extends FeatureState {
final DataType data;
const FeatureSuccess(this.data);
@override
List<Object> get props => [data];
}
class FeatureError extends FeatureState {
final String message;
const FeatureError(this.message);
@override
List<Object> get props => [message];
}
BLoC——采用 Loading → Success/Error 模式的事件处理器:
class FeatureBloc extends Bloc<FeatureEvent, FeatureState> {
final FeatureRepository _repository;
FeatureBloc({required FeatureRepository repository})
: _repository = repository,
super(FeatureInitial()) {
on<FeatureActionRequested>(_onActionRequested);
}
Future<void> _onActionRequested(
FeatureActionRequested event,
Emitter<FeatureState> emit,
) async {
emit(FeatureLoading());
try {
final result = await _repository.doSomething(event.param);
emit(FeatureSuccess(result));
} catch (e) {
emit(FeatureError(e.toString()));
}
}
}
重要:异步操作前始终先发出 Loading,然后再发出 Success 或 Error。绝不能跳过加载状态。
数据层
数据流:
UI Event → BLoC (emit Loading) → Repository → Datasource (SDK)
↓
Response → Repository (map to entity) → BLoC (emit Success/Error) → UI
数据源——仅调用后端 SDK:
class FeatureDataSource {
final SupabaseClient _supabase;
FeatureDataSource(this._supabase);
Future<Map<String, dynamic>> fetch() async {
return await _supabase.from('table').select().single();
}
}
仓库——负责协调和映射:
class FeatureRepository {
final FeatureDataSource _dataSource;
FeatureRepository(this._dataSource);
Future<DomainEntity> fetchData() async {
final response = await _dataSource.fetch();
return DomainEntity.fromJson(response);
}
}
设计系统(不可妥协)
颜色
✅ AppColors.primary、AppColors.error、AppColors.textPrimary
❌ Color(0xFF...)、Colors.blue、内联十六进制值
间距
✅ AppSpacing.xs(4)、AppSpacing.sm(8)、AppSpacing.md(16)、AppSpacing.lg(24)、AppSpacing.xl(32)
✅ AppSpacing.screenHorizontal(24)、AppSpacing.screenVertical(16)
❌ EdgeInsets.all(16.0)、硬编码的内边距值
边框圆角
✅ AppRadius.sm(8)、AppRadius.md(12)、AppRadius.lg(16)、AppRadius.xl(24)
❌ BorderRadius.circular(12)、内联圆角值
排版
✅ AppTypography.headlineLarge、AppTypography.bodyMedium、theme.textTheme.bodyMedium
❌ TextStyle(fontSize: 16)、内联文本样式
UI 模式
屏幕模板
GradientScaffold(
body: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(AppSpacing.screenHorizontal),
child: HeaderWidget(),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.screenHorizontal),
child: ContentWidget(),
),
),
Padding(
padding: const EdgeInsets.all(AppSpacing.screenHorizontal),
child: ActionButton(
onPressed: () => context.read<FeatureBloc>().add(ActionEvent()),
),
),
],
),
),
)
BLoC Consumer 模式
BlocConsumer<FeatureBloc, FeatureState>(
listener: (context, state) {
if (state is FeatureError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.message), backgroundColor: AppColors.error),
);
}
},
builder: (context, state) {
if (state is FeatureLoading) return const Center(child: CircularProgressIndicator());
if (state is FeatureSuccess) return SuccessWidget(data: state.data);
return const SizedBox.shrink();
},
)
常见问题
❌ 在 Widget 中编写业务逻辑 → 移至 BLoC
❌ 在仓库中直接调用 Supabase/Firebase → 移至数据源
❌ 异步操作前跳过加载状态 → 始终先发出 Loading
❌ 硬编码颜色,例如 Color(0xFF4A90A4) → 使用 AppColors.primary
❌ 使用魔法数字,例如 padding: 16 → 使用 AppSpacing.md
快速参考
| 操作 | 模式 |
|---|---|
| 分发事件 | context.read<Bloc>().add(Event()) |
| 内联监听状态 | context.watch<Bloc>().state |
| 监听并构建 | BlocConsumer |
| 仅监听 | BlocListener |
| 仅构建 | BlocBuilder |
提交前检查清单
- Events/States/BLoC 使用
Equatable - 所有异步操作均遵循 Loading → Success/Error
- UI 中没有业务逻辑
- 数据源之外没有 SDK 调用
- 颜色/间距/排版均无硬编码
- 错误处理使用
AppColors.error显示 SnackBar - 代码已使用
dart format格式化