implementing-flutter-bloc — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited implementing-flutter-bloc (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Strict separation of UI from business logic via event-driven unidirectional data flow. Choose Cubit (lightweight, no events) for simple state, or full Bloc (events + transformers) for complex flows.
dependencies:
flutter_bloc: ^9.1.1
equatable: ^2.0.5
bloc_concurrency: ^0.3.0 # For event transformers
dev_dependencies:
bloc_test: ^10.0.0| Pattern | When to Use |
|---|---|
| Cubit | Simple state (toggle, counter, form validation) |
| Bloc | Event tracking, transformers, complex flows (login, search debounce) |
class CounterState extends Equatable {
final int value;
const CounterState({required this.value});
@override
List<Object?> get props => [value];
}
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(const CounterState(value: 0));
void increment() => emit(CounterState(value: state.value + 1));
void decrement() => emit(CounterState(value: state.value - 1));
}abstract class CounterEvent extends Equatable {
const CounterEvent();
@override
List<Object?> get props => [];
}
class CounterIncremented extends CounterEvent {}
class CounterDecremented extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState(value: 0)) {
on<CounterIncremented>((e, emit) => emit(CounterState(value: state.value + 1)));
on<CounterDecremented>((e, emit) => emit(CounterState(value: state.value - 1)));
}
}MultiBlocProvider(
providers: [
BlocProvider(create: (context) => CounterCubit()),
BlocProvider(create: (context) => AuthBloc()),
],
child: MyApp(),
)| Widget | Use For |
|---|---|
BlocBuilder | Rebuild UI on state changes |
BlocListener | Side effects only (navigation, snackbars) |
BlocConsumer | Rebuild + side effects together |
// Trigger events
context.read<CounterBloc>().add(CounterIncremented());
context.read<CounterCubit>().increment(); // Cubit: direct method callSee BLoC Best Practices for full widget examples with buildWhen/listenWhen.
| Transformer | Behavior | Use Case |
|---|---|---|
concurrent (default) | All events in parallel | Independent API calls |
sequential | One at a time, queued | Multi-step transactions |
restartable | Cancel previous, start fresh | Search bar, autocomplete |
droppable | Ignore while processing | Submit button deduplication |
import 'package:bloc_concurrency/bloc_concurrency.dart';
on<SearchQueryChanged>(_onQueryChanged, transformer: restartable());
on<FormSubmitted>(_onFormSubmitted, transformer: droppable());See BLoC Best Practices for custom debounce transformer.
Use Freezed for sealed union states with exhaustive when/map pattern matching. See BLoC Best Practices for full Freezed setup and examples.
See Error Handling & Common Pitfalls for solutions to:
emit.isDone guard)try/catch — use onError + addError insteadEquatable or Freezed.BuildContext.UserLoggedIn, not UserLogin).AuthSuccess, not AuthSucceeded).~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.