tapWidget function

Future<TapResult> tapWidget(
  1. TapInput input, {
  2. FdbHelperChecker? checkFdbHelperFn,
  3. FdbTapRunner? fdbTapFn,
})

Taps a widget or coordinates in the running Flutter app.

Handles selector-based taps, coordinate taps, and @N describe-ref taps. A ref goes to fdb_helper as is: the helper resolves it to the widget it names, or fails when that widget is gone, so a ref never lands on another widget. The retry loop (500ms poll until deadline) runs inside this function. Never throws; all error conditions are represented as sealed result cases.

Implementation

Future<TapResult> tapWidget(
  TapInput input, {
  FdbHelperChecker? checkFdbHelperFn,
  FdbTapRunner? fdbTapFn,
}) async {
  try {
    final isolateId = await (checkFdbHelperFn ?? checkFdbHelper)();
    if (isolateId == null) return const TapNoFdbHelper();

    final params = <String, dynamic>{'isolateId': isolateId};
    if (input.text != null) params['text'] = input.text;
    if (input.key != null) params['key'] = input.key;
    if (input.type != null) params['type'] = input.type;
    if (input.index != null) params['index'] = input.index.toString();
    if (input.x != null) params['x'] = input.x.toString();
    if (input.y != null) params['y'] = input.y.toString();
    if (input.ref != null) params['ref'] = input.ref.toString();
    if (input.expectText != null) params['expectText'] = input.expectText;
    if (input.expectType != null) params['expectType'] = input.expectType;

    final result = await _tapUntilDeadline(params, fdbTapFn ?? fdbTap, timeoutSeconds: input.timeoutSeconds);
    return switch (result) {
      TapSuccess(:final widgetType, :final x, :final y, :final warning, :final text) => TapSuccess(
          widgetType: input.usedAt ? 'coordinates' : widgetType,
          x: x ?? input.x ?? '',
          y: y ?? input.y ?? '',
          warning: warning,
          text: describeEntryText(text),
        ),
      TapRelayedError(:final message) => TapRelayedError(refAwareError(message, input.ref)),
      _ => result,
    };
  } on AppDiedException catch (e) {
    return TapAppDied(logLines: e.logLines, reason: e.reason);
  } catch (e) {
    return TapError(e.toString());
  }
}