Repository: flutter
Issue: [Web] Unable to detect if a native IME panel is opened
Labels: a: text input,has reproducible steps,P2,team-web,triaged-web,found in release: 3.13,found in release: 3.14

In super_editor, we handle arrow key events to move the selection around the text. However, doing that prevents the user from interacting with native input controls, like emoji pickers and japanese input panels:

https://github.com/flutter/flutter/assets/7597082/42386c6d-2baa-4b13-82da-35aac3c4f9c8

To let users interact with those panels, we need to let the key events bubble up to the OS. However, we do want to handle the key events when there isn't an IME panel open.

We need a way to determine whether or not an IME panel is being displayed. Even though an IME panel is opened, the `FocusNode` still has primary focus.

Is there a way to check if there is an IME panel opened?


### Steps to reproduce

1. Run the sample code using Japanese keyboard
2. Type some letters to trigger the IME panel, like "s" and "a"
3. Press DOWN ARROW 

On the `_onKey` method, we'd like to know whether the IME panel is open.

<details><summary>Code sample</summary>

```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: TextInputExample(),
      ),
    ),
  );
}

class TextInputExample extends StatefulWidget {
  const TextInputExample({Key? key}) : super(key: key);

  @override
  State<TextInputExample> createState() => _TextInputExampleState();
}

class _TextInputExampleState extends State<TextInputExample> implements DeltaTextInputClient {
  TextInputConnection? _inputConnection;
  TextEditingValue _currentEditingValue = TextEditingValue(
    text: 'Type "s" then "a" to trigger the IME panel, then press ARRPW DOWN',
    selection: TextSelection.collapsed(offset: 0),
  );
  FocusNode _focusNode = FocusNode();
  GlobalKey _textKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      _attach();
    });
  }

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  @override
  void updateEditingValueWithDeltas(List<TextEditingDelta> textEditingDeltas) {
    TextEditingValue newEditingValue = _currentEditingValue;

    for (final delta in textEditingDeltas) {
      newEditingValue = delta.apply(newEditingValue);
    }

    setState(() {
      _currentEditingValue = newEditingValue;
    });
  }

  KeyEventResult _onKey(FocusNode node, RawKeyEvent event) {
    if ([
      LogicalKeyboardKey.arrowDown,
      LogicalKeyboardKey.arrowUp,
    ].contains(event.logicalKey)) {
      // Simulate us doing something.
      //
      // Here, we'd like to know if the IME panel is opened,
      // so we could avoid doing any operation.
      setState(() {
        _currentEditingValue = TextEditingValue.empty;
        syncWithIme();
      });

      return KeyEventResult.handled;
    }

    return KeyEventResult.ignored;
  }

  void _attach() {
    if (_inputConnection != null) {
      return;
    }
    _inputConnection = TextInput.attach(
      this,
      const TextInputConfiguration(
        enableDeltaModel: true,
        inputType: TextInputType.multiline,
        textCapitalization: TextCapitalization.sentences,
        inputAction: TextInputAction.newline,
      ),
    );

    final renderBox = _textKey.currentContext!.findRenderObject() as RenderBox;
    _inputConnection!
      ..show()
      ..setEditingState(_currentEditingValue)
      ..setEditableSizeAndTransform(renderBox.size, renderBox.getTransformTo(null))
      ..setStyle(
        fontFamily: 'Roboto',
        fontWeight: FontWeight.normal,
        fontSize: 24,
        textDirection: TextDirection.ltr,
        textAlign: TextAlign.left,
      );
  }

  void syncWithIme() {
    _inputConnection!.setEditingState(_currentEditingValue);
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Center(
          child: Focus(
            focusNode: _focusNode,
            onKey: _onKey,
            autofocus: true,
            child: DecoratedBox(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blue),
              ),
              child: Text(
                _currentEditingValue.text,
                key: _textKey,
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontFamily: 'Roboto',
                  fontWeight: FontWeight.normal,
                  fontSize: 24,
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }

  @override
  void connectionClosed() {}

  @override
  AutofillScope? get currentAutofillScope => null;

  @override
  TextEditingValue? get currentTextEditingValue => null;

  @override
  void insertTextPlaceholder(Size size) {}

  @override
  void performAction(TextInputAction action) {}

  @override
  void performPrivateCommand(String action, Map<String, dynamic> data) {}

  @override
  void removeTextPlaceholder() {}

  @override
  void showAutocorrectionPromptRect(int start, int end) {}

  @override
  void showToolbar() {}

  @override
  void updateEditingValue(TextEditingValue value) {}

  @override
  void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {}

  @override
  void insertContent(KeyboardInsertedContent content) {}

  @override
  void performSelector(String selectorName) {}

  @override
  void updateFloatingCursor(RawFloatingCursorPoint point) {}
}
```