mcpField
F
flutter-bloc-development

flutter-bloc-development

github.com/abdelhakrazi开发20354 次安装39 周前更新
代码仓库

关于 flutter-bloc-development

flutter-bloc-development 是面向 Flutter 代码库的开发技能,规范 BLoC 状态管理、按功能分层、基于 Repository 的数据访问和设计系统常量,涵盖功能结构、界面与数据流。

主要功能

  1. 01按功能组织项目结构
  2. 02BLoC 事件与状态模式
  3. 03Repository 和数据源分层
  4. 04界面使用设计系统常量
  5. 05加载、成功与错误状态流

适用场景

  1. 01实现新的 Flutter 功能
  2. 02避免在组件中编写业务逻辑
  3. 03将后端调用隔离到数据源
  4. 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.txt
README.md
examples/earnings/
earnings.dart
examples/earnings/bloc/
earnings_bloc.dart
earnings_state.dart
earnings_event.dart
examples/earnings/data/
data.dart
examples/earnings/data/datasources/
earnings_datasource.dart
examples/earnings/data/models/
earnings_summary.dart
examples/earnings/data/repositories/
earnings_repository.dart
examples/earnings/view/
earnings_page.dart
view.dart
examples/earnings/view/widgets/
daily_earnings_list.dart
earnings_summary_card.dart
examples/shared/data/
data.dart
examples/shared/data/datasources/
api_client.dart
user_datasource.dart
examples/shared/data/models/
api_response.dart
user.dart
examples/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 格式化

评论

支持 Markdown