callPlaygroundFunction method

Future<RPCResponse> callPlaygroundFunction(
  1. Map<String, dynamic> params
)

This function is used as playground for testing.

Returns a list of visual errors in the Flutter application. Each error contains:

  • nodeId: The ID of the DiagnosticsNode with the error
  • description: Description of the error
  • errorType: Type of the error (e.g., "Layout Overflow", "Render Issue")

Implementation

Future<RPCResponse> callPlaygroundFunction(
  final Map<String, dynamic> params,
) async {
  final serviceManager = devtoolsService.serviceManager;
  if (!serviceManager.connectedState.value.connected) {
    return RPCResponse.error('Not connected to VM service');
  }

  final vmService = serviceManager.service;
  if (vmService == null) {
    return RPCResponse.error('VM service not available');
  }
  final objectGroupManager = initObjectGroup(debugName: 'playground');

  // final objectRef = await vmService.getObject(
  //   isolateId,
  //   'RenderFlex#${errors.first.renderFlexId}', // The ID from RenderFlex#f8f6b
  // );
  final group = objectGroupManager.next;
  final response = await devtoolsService.callServiceExtensionRaw(
    'ext.flutter.inspector.'
    '${WidgetInspectorServiceExtensions.widgetLocationIdMap.name}',
    args: {
      'groupName': group.groupName,
      'isSummaryTree': 'false',
      'withPreviews': 'true',
      'fullDetails': 'true',
    },
  );

  final objectGroupApi = inspector_service.ObjectGroup(
    'visual-errors',
    inspector_service.InspectorService(
      dartVmDevtoolsService: devtoolsService,
    ),
  );

  final rootNodes = RemoteDiagnosticsNode(
    response.json!['result'] as Map<String, Object?>,
    objectGroupApi,
    false,
    null,
  );

  // one of children contains in description correct renderFlexId.
  // so we need to find it and use it as rootNode.
  Future<RemoteDiagnosticsNode?> findNodeWithId(
    final RemoteDiagnosticsNode node,
    final String id,
  ) async {
    if (node.toString(minLevel: DiagnosticLevel.debug).contains(id)) {
      return node;
    }
    if (!node.hasChildren) return null;
    final children = await node.children ?? [];
    for (final child in children) {
      final found = await findNodeWithId(child, id);
      if (found != null) return found;
    }
    return null;
  }

  // final rootNode = await findNodeWithId(rootNodes, errors.first.renderFlexId);

  // print(jsonEncode(rootNode?.json));

  // return RPCResponse.successMap({'errors': errors});

  try {
    // Get a new object group for this operation
    final group = objectGroupManager.next;

    try {
      // Get the root widget tree with full details to analyze for errors
      final response = await vmService.callServiceExtension(
        'ext.flutter.inspector.getRootWidgetTree',
        // isolateId: isolateId,
        args: {
          'groupName': group.groupName,
          'isSummaryTree': 'true',
          'withPreviews': 'true',
          'fullDetails': 'false',
        },
      );

      if (response.json == null || response.json!['result'] == null) {
        await objectGroupManager.cancelNext();
        return RPCResponse.error('Root widget tree not available');
      }

      // Parse the root node
      final rootNode = RemoteDiagnosticsNode(
        response.json!['result'] as Map<String, Object?>,
        null, // objectGroupApi not needed for error detection
        false, // not a property
        null, // no parent
      );
      print(jsonEncode(rootNode.json));

      // Find all errors in the tree
      // final errors = await _findErrors(rootNode);

      // Promote the group after successful operation
      await objectGroupManager.promoteNext();

      return RPCResponse.successMap({'errors': []});
    } catch (e) {
      // Cancel the group on error
      await objectGroupManager.cancelNext();
      rethrow;
    }
  } catch (e, stackTrace) {
    return RPCResponse.error('Error getting visual errors: $e', stackTrace);
  }
}