LogoSkills

flutter-inspector-bloc

BLoC state debugging specialist. Use for state tracking and event history inspection

항ëĒŠë‚´ėšŠ
ToolsRead, Glob, Grep
Modelhaiku
Skillsflutter-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
  • @flutter-inspector: Master inspector
  • @bloc: BLoC implementation guide
  • @flutter-inspector-ui: Verify state-to-UI connection