Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[webview_flutter] Softkeyboard doesn't appear on when input text is clicked or focused in TV #718

Open
alensh12 opened this issue Aug 16, 2024 · 4 comments

Comments

@alensh12
Copy link

Help Wanted

@alensh12 alensh12 changed the title [webview_flutter] Softkeyboard doesn't appear on when input text is clicked or focus no TV [webview_flutter] Softkeyboard doesn't appear on when input text is clicked or focused in TV Aug 16, 2024
@JSUYA
Copy link
Member

JSUYA commented Aug 20, 2024

Hi Could you please share some more information?
Please let me know a website that I can test. When I tested on google.com in webview_flutter_tizen, the softkeyboard was displayed normally.
Is there a hardware keyboard device connected to the tv device?

@alensh12
Copy link
Author

alensh12 commented Aug 21, 2024

Thanks @JSUYA for your concern. I also tried using [webview_flutter_tizen](https://github.com/flutter-tizen/plugins/tree/master/packages/webview_flutter. I used TV Remote Key-Event(Used Logical KeyEvent) to trigger click on buttons on webview. Button were clickable and it was redirecting to respective contents but when i click on input field they were not clickable or focusable and soft-keyboard were also not appeared

I am using stack for displaying cursor above the webview widget, and also using javascript evaluate function to trigger click and focus of the item in webview

@alensh12
Copy link
Author

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.white,
    body: Stack(
      children: [
        // Focus the WebViewWidget and handle key events
        Focus(
          autofocus: true,
          focusNode: _fNodeWebview,
          onKeyEvent: (node, key) => handleKey(key, context),
          child: WebViewWidget(
            controller: webViewController,
            gestureRecognizers: Set()
              ..add(
                Factory<DragGestureRecognizer>(
                  () => VerticalDragGestureRecognizer(),
                ),
              ),
          ),
        ),
        // Display a LinearProgressPage when needed
        Visibility(
          visible: false,
          child: Container(
            height: MediaQuery.of(context).size.height,
            width: MediaQuery.of(context).size.width,
            child: Center(
              child: LinearProgressPage(displayMessage: 'Checking out.....'),
            ),
          ),
        ),
        // Add a top scroll bar if the user has scrolled up
        if (topScrollBar)
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: Container(
              height: 25,
              child: Center(child: Icon(Icons.keyboard_arrow_up)),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [
                    Colors.blue.withOpacity(0.5),
                    Colors.white.withOpacity(0.5)
                  ],
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                ),
                border: Border(
                  bottom: BorderSide(
                    color: Colors.black.withOpacity(0.5),
                    width: 1,
                  ),
                ),
              ),
            ),
          ),
        // Add a bottom scroll bar if the user has scrolled down
        if (bottomScrollBar)
          Positioned(
            bottom: 0,
            left: 0,
            right: 0,
            child: Container(
              height: 25,
              child: Center(child: Icon(Icons.keyboard_arrow_down)),
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [
                    Colors.blue.withOpacity(0.5),
                    Colors.white.withOpacity(0.5)
                  ],
                  begin: Alignment.bottomCenter,
                  end: Alignment.topCenter,
                ),
                border: Border(
                  top: BorderSide(
                    color: Colors.black.withOpacity(0.5),
                    width: 1,
                  ),
                ),
              ),
            ),
          ),
        // Display a mouse icon at the current position
        Positioned(
          left: posX.toDouble(),
          top: posY.toDouble(),
          child: Icon(
            Icons.mouse,
            color: Colors.black,
          ),
        ),
      ],
    ),
  );
}

// Handle key events and update the mouse position
KeyEventResult handleKey(KeyEvent key, BuildContext context) {
  double height = MediaQuery.of(context).size.height;
  double width = MediaQuery.of(context).size.width;

  if (key is KeyDownEvent || key is KeyRepeatEvent) {
    LogicalKeyboardKey _keyCode = key.logicalKey;

    // Update the mouse position based on arrow keys
    if (_keyCode == LogicalKeyboardKey.arrowUp) {
      setState(() {
        posY = (posY - 5).clamp(0, height.toInt());
      });
    } else if (_keyCode == LogicalKeyboardKey.arrowRight) {
      setState(() {
        posX = (posX + 5).clamp(0, width.toInt());
      });
    } else if (_keyCode == LogicalKeyboardKey.arrowDown) {
      setState(() {
        posY = (posY + 5).clamp(0, height.toInt());
      });
    } else if (_keyCode == LogicalKeyboardKey.arrowLeft) {
      setState(() {
        posX = (posX - 5).clamp(0, width.toInt());
      });
    } else if (_keyCode == LogicalKeyboardKey.enter ||
               _keyCode == LogicalKeyboardKey.select) {
      // Interact with the element under the mouse cursor
      webViewController.runJavaScriptReturningResult("""
        (function() {
          var element = document.elementFromPoint($posX, $posY);
          if (element) {
            return JSON.stringify({
              tagName: element.tagName,
              type: element.type,
              id: element.id,
              className: element.className,
              isContentEditable: element.isContentEditable
            });
          }
          return null;
        })();
      """).then((result) {
        if (result != null) {
          String elementInfo = json.decode(result.toString());
          handleElementInteraction(jsonDecode(elementInfo));
        }
      });
    }

    // Update the visibility of the scroll bars
    setState(() {
      topScrollBar = posY < 5;
      bottomScrollBar = posY > height - 5;
    });

    // Scroll the WebView if necessary
    if (topScrollBar) {
      webViewController.scrollBy(0, -20);
    } else if (bottomScrollBar) {
      webViewController.scrollBy(0, 20);
    }
  }

  // Handle back/escape key events
  if (key is KeyDownEvent) {
    LogicalKeyboardKey _keyCode = key.logicalKey;
    if (_keyCode == LogicalKeyboardKey.goBack || _keyCode == LogicalKeyboardKey.escape) {
      webViewController.goBack();
    }
  }

  return KeyEventResult.handled;
}

// Handle interaction with the element under the mouse cursor
void handleElementInteraction(Map<String, dynamic> elementInfo) {
  print("Element Info: $elementInfo"); // For debugging

  bool isInputField = false;
  String tagName = elementInfo['tagName']?.toLowerCase() ?? '';
  String type = elementInfo['type']?.toLowerCase() ?? '';

  // Check if the element is an input field
  if (tagName == 'input') {
    List<String> inputTypes = ['text', 'password', 'email', 'number', 'tel', 'url', 'search'];
    isInputField = inputTypes.contains(type);
  } else if (tagName == 'textarea') {
    isInputField = true;
  } else if (elementInfo['isContentEditable'] == true) {
    isInputField = true;
  }

  if (isInputField) {
    print("input type :: $type");
    // Handle input field interaction
    webViewController.runJavaScript("""
      (function() {
        var element = document.elementFromPoint($posX, $posY);
        if (element) {
          element.focus();
          element.click();
          element.value = "*********";
          var focusEvent = new Event('focus', { bubbles: true, cancelable: true });
          element.dispatchEvent(focusEvent);
          var inputEvent = new Event('input', { bubbles: true, cancelable: true });
          element.dispatchEvent(inputEvent);
          var changeEvent = new Event('change', { bubbles: true, cancelable: true });
          element.dispatchEvent(changeEvent);
          console.log('Focused and clicked element with class: ' + element.className);
        }
      })();
    """);
  } else {
    // Handle non-input element interaction
    webViewController.runJavaScript("""
      (function() {
        var element = document.elementFromPoint($posX, $posY);
        if (element) {
          element.click();
        }
      })();
    """);
  }
}

@JSUYA
Copy link
Member

JSUYA commented Aug 23, 2024

Thanks @JSUYA for your concern. I also tried using [webview_flutter_tizen](https://github.com/flutter-tizen/plugins/tree/master/packages/webview_flutter. I used TV Remote Key-Event(Used Logical KeyEvent) to trigger click on buttons on webview. Button were clickable and it was redirecting to respective contents but when i click on input field they were not clickable or focusable and soft-keyboard were also not appeared

I am using stack for displaying cursor above the webview widget, and also using javascript evaluate function to trigger click and focus of the item in webview

(Your code is not enough for me to test immediately. Can you share me some testable code? (It would be better if you modified the example code))
The webengine of webview automatically executes sotfkey when the cursor is blink stating in the edit box (probably editable + focused). If the keyboard is not triggered even though the element is editable, it may be a problem with the web engine and may be difficult to resolve.

When calling element.click(), is flutter's focus focusing on the webview?

Does the java script code work fine on other platforms? (maybe android?)

If you are using a custom cursor(?), can you trigger an event through flutter code?
https://stackoverflow.com/a/70873392

Sorry for not being helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants