| íëĒŠ | ë´ėŠ |
|---|---|
| Tools | Read, Glob, Grep |
| Model | haiku |
| Skills | flutter-inspector |
Flutter Inspector - BLoC Agent#
A specialized agent for tracking and debugging BLoC/Cubit state at runtime.
Triggers#
Automatically activated when @flutter-inspector-bloc is invoked or the following keywords are detected:
- BLoC state, state change
- Event tracking, state history
- Cubit, emit
MCP Tools#
bloc_list_active#
Returns a list of all currently active BLoC/Cubit instances.
{
" name " : " bloc_list_active " ,
" description " : " Active BLoC/Cubit list " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {}
}
}
Response example:
{
" blocs " : [
{
" type " : " AuthBloc " ,
" state " : " Authenticated " ,
" stateType " : " AuthState.authenticated " ,
" instanceId " : " AuthBloc#12345 "
},
{
" type " : " HomeBloc " ,
" state " : " Loaded " ,
" stateType " : " HomeState.loaded " ,
" instanceId " : " HomeBloc#67890 "
},
{
" type " : " ThemeCubit " ,
" state " : " light " ,
" stateType " : " ThemeMode " ,
" instanceId " : " ThemeCubit#11111 "
}
],
" count " : 3
}
bloc_get_state#
Returns the detailed current state of a specific BLoC.
{
" name " : " bloc_get_state " ,
" description " : " Detailed BLoC state query " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" blocType " : {
" type " : " string " ,
" description " : " BLoC type name (e.g., AuthBloc) "
},
" instanceId " : {
" type " : " string " ,
" description " : " Instance ID (optional) "
}
},
" required " : [ " blocType " ]
}
}
Response example:
{
" blocType " : " HomeBloc " ,
" state " : {
" status " : " loaded " ,
" posts " : [
{ " id " : 1, " title " : " First post " },
{ " id " : 2, " title " : " Second post " }
],
" hasMore " : true,
" page " : 1,
" error " : null
},
" stateType " : " HomeState " ,
" lastUpdated " : " 2024-01-01T10:05:00Z "
}
bloc_get_history#
Returns the state change history of a BLoC.
{
" name " : " bloc_get_history " ,
" description " : " State change history " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" blocType " : {
" type " : " string " ,
" description " : " BLoC type name "
},
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 20
}
},
" required " : [ " blocType " ]
}
}
Response example:
{
" blocType " : " AuthBloc " ,
" history " : [
{
" timestamp " : " 2024-01-01T10:00:00Z " ,
" fromState " : " AuthState.initial " ,
" toState " : " AuthState.loading " ,
" trigger " : " AuthEvent.login "
},
{
" timestamp " : " 2024-01-01T10:00:02Z " ,
" fromState " : " AuthState.loading " ,
" toState " : " AuthState.authenticated " ,
" trigger " : " AuthEvent.login "
}
],
" totalTransitions " : 2
}
bloc_get_events#
Returns the event log for a BLoC.
{
" name " : " bloc_get_events " ,
" description " : " Event log query " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" blocType " : {
" type " : " string " ,
" description " : " BLoC type name "
},
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 50
}
},
" required " : [ " blocType " ]
}
}
Response example:
{
" blocType " : " HomeBloc " ,
" events " : [
{
" timestamp " : " 2024-01-01T10:00:00Z " ,
" event " : " HomeEvent.load " ,
" params " : {},
" processed " : true
},
{
" timestamp " : " 2024-01-01T10:00:05Z " ,
" event " : " HomeEvent.refresh " ,
" params " : {},
" processed " : true
},
{
" timestamp " : " 2024-01-01T10:00:10Z " ,
" event " : " HomeEvent.loadMore " ,
" params " : { " page " : 2},
" processed " : false,
" error " : " Network timeout "
}
]
}
App Integration Code#
// lib/debug/mcp_bloc_tools.dart
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MCPBlocObserver extends BlocObserver {
final _history = <String, List<Map<String, dynamic>>>{};
final _events = <String, List<Map<String, dynamic>>>{};
final _activeBlocs = <String, BlocBase>{};
static final instance = MCPBlocObserver._();
MCPBlocObserver._();
@override
void onCreate(BlocBase bloc) {
_activeBlocs[bloc.runtimeType.toString()] = bloc;
super.onCreate(bloc);
}
@override
void onClose(BlocBase bloc) {
_activeBlocs.remove(bloc.runtimeType.toString());
super.onClose(bloc);
}
@override
void onTransition(Bloc bloc, Transition transition) {
final type = bloc.runtimeType.toString();
_history[type] ??= [];
_history[type]!.add({
'timestamp': DateTime.now().toIso8601String(),
'fromState': transition.currentState.runtimeType.toString(),
'toState': transition.nextState.runtimeType.toString(),
'trigger': transition.event.runtimeType.toString(),
});
super.onTransition(bloc, transition);
}
@override
void onEvent(Bloc bloc, Object? event) {
final type = bloc.runtimeType.toString();
_events[type] ??= [];
_events[type]!.add({
'timestamp': DateTime.now().toIso8601String(),
'event': event.runtimeType.toString(),
'processed': true,
});
super.onEvent(bloc, event);
}
Map<String, dynamic> getActiveBlocs() {
return {
'blocs': _activeBlocs.entries.map((e) => {
'type': e.key,
'state': e.value.state.toString(),
'stateType': e.value.state.runtimeType.toString(),
}).toList(),
'count': _activeBlocs.length,
};
}
Map<String, dynamic> getState(String blocType) {
final bloc = _activeBlocs[blocType];
if (bloc == null) return {'error': 'BLoC not found'};
return {
'blocType': blocType,
'state': bloc.state,
'stateType': bloc.state.runtimeType.toString(),
};
}
List<Map<String, dynamic>> getHistory(String blocType, int limit) {
return (_history[blocType] ?? []).take(limit).toList();
}
List<Map<String, dynamic>> getEvents(String blocType, int limit) {
return (_events[blocType] ?? []).take(limit).toList();
}
}
void registerBlocTools() {
if (!kDebugMode) return;
Bloc.observer = MCPBlocObserver.instance;
addMcpTool(MCPCallEntry.tool(
handler: (_) => MCPCallResult(
message: 'Active BLoCs',
parameters: MCPBlocObserver.instance.getActiveBlocs(),
),
definition: MCPToolDefinition(
name: 'bloc_list_active',
description: 'Active BLoC list',
inputSchema: {'type': 'object', 'properties': {}},
),
));
addMcpTool(MCPCallEntry.tool(
handler: (params) {
final blocType = params['blocType'] as String;
return MCPCallResult(
message: 'BLoC State',
parameters: MCPBlocObserver.instance.getState(blocType),
);
},
definition: MCPToolDefinition(
name: 'bloc_get_state',
description: 'BLoC state query',
inputSchema: {
'type': 'object',
'properties': {
'blocType': {'type': 'string'},
},
'required': ['blocType'],
},
),
));
// bloc_get_history and bloc_get_events are implemented similarly
}
Usage Examples#
Check active BLoCs#
Q: What BLoCs are currently active?
A: Run bloc_list_active
- > AuthBloc (Authenticated), HomeBloc (Loaded), ThemeCubit (light)
Detailed BLoC state check#
Q: Show me the current state of HomeBloc in detail
A: Run bloc_get_state blocType= " HomeBloc "
- > status: loaded, posts: 10 items, hasMore: true, page: 1
Track state changes#
Q: Show me the history of how AuthBloc changed
A: Run bloc_get_history blocType= " AuthBloc "
- > initial - > loading - > authenticated
Event debugging#
Q: What events occurred in HomeBloc?
A: Run bloc_get_events blocType= " HomeBloc "
- > load, refresh, loadMore (error: timeout)
Common Problem Diagnosis#
State not updating#
1. Confirm BLoC is active with bloc_list_active
2. Confirm event was fired with bloc_get_events
3. Track state changes with bloc_get_history
Incorrect state#
1. Check current state with bloc_get_state
2. Track where it went wrong with bloc_get_history
3. Review related event handler code
Event ignored#
1. Confirm event was fired with bloc_get_events
2. Find events with processed: false
3. Check error messages
Related Agents#
@flutter-inspector: Master inspector@bloc: BLoC implementation guide@flutter-inspector-ui: Verify state-to-UI connection