Flutter_Webview keyboard error


Webview_flutter ^0.3.7+1 pub link

Webview_flutter has no way to pop up the keyboard on Android, the issue on Github has been mentioned for a long time, but the official milestone will not be released until the October issue of 19 #19718,(there is a PR to master branch at the time of writing, but stable may have to wait), but the PR solution is not available on AndroidN before… comment

1. Inspiration from other students

The simple idea of this solution is to inject a javascript code into the Webview during onPageFinish, listen for the focus event input/ Textarea, and send it via JSChannel to the TextField hidden behind the Webview. Call up the soft keyboard via the TextField and then set the input/ Textarea to a new value by listening for the onChange event of the TextField

Here is his JS code implementation: he uses JS to listen for several elements,focus events/FocusOut events of a site that need to pop input methods

inputs = document.getElementsByClassName('search-bar');
                            header=document.getElementsByClassName('site-header');
                            header[0].style.display = 'none';
buttons = document.getElementsByClassName('icon');
                            buttons[0].focus();
                            if(inputs ! =null) {
                              input = inputs[0];
                              InputValue.postMessage(input.value);
                              input.addEventListener('focus', (_) => {
                                Focus.postMessage('focus');
                              }, true);
                              input.addEventListener('focusout', (_) => {
                                Focus.postMessage('focusout');
                              }, true)}Copy the code

JSChannel:

 javascriptChannels: Set.from(
                          [
                            // Listening for Javascript messages to get Notified of Focuschanges and the current input Value of the Textfield.
                            JavascriptChannel(
                              name: 'Focus'.// get notified of focus changes on the input field and open/close the Keyboard.
                              onMessageReceived: (JavascriptMessage focus) {
                                print(focus.message);
                                if (focus.message == 'focus') {
                                  FocusScope.of(context)
                                      .requestFocus(_focusNode);
                                } else if (focus.message == 'focusout') {
                                  _focusNode.unfocus();
                                }
                              },
                            ),
                            JavascriptChannel(
                              name: 'InputValue'.// set the value of the native input field to the one on the website to always make sure they have the same input.onMessageReceived: (JavascriptMessage value) { _textController.value = TextEditingValue(text: value.toString()); },)],),Copy the code

Receive events that change the text of the TextField, and invoke/hide the soft keyboard by focusNode

This scheme looks good, but listening for all classnames by hand is stupid… And if the user closes the keyboard manually after the keyboard is ejected (pressing the close button on the soft keyboard), the soft keyboard will never pop out again…

2. Upgrade your own solution

  1. Use two Textfields to receive events alternately to solve a problem that cannot be revived after manually closing the keyboard
  2. Inject JS to listen for all input/textarea elements, not focus/ focusOut events, but click events. You can obtain the current text selection/cursor position of the user. 2. You can judge the event of clicking again
  3. Record the element of the current focus to determine if a new keyboard needs to be revived

The following simple code implementation:

var inputs = document.getElementsByTagName('input');
var textArea = document.getElementsByTagName('textarea');
var current;

for (var i = 0; i < inputs.length; i++) {
  console.log(i);
  inputs[i].addEventListener('click', (e) => {
    var json = {
      "funcName": "requestFocus"."data": {
        "initText": e.target.value,
        "selectionStart":e.target.selectionStart,
        "selectionEnd":e.target.selectionEnd
      }
    };
    json.data.refocus = (document.activeElement == current);
    
    current = e.target;
    var param = JSON.stringify(json);
    console.log(param); UserState.postMessage(param); })}for (var i = 0; i < textArea.length; i++) {
  console.log(i);
  textArea[i].addEventListener('click', (e) => {
    var json = {
      "funcName": "requestFocus"."data": {
        "initText": e.target.value,
        "selectionStart":e.target.selectionStart,
        "selectionEnd":e.target.selectionEnd
      }
    };
    
    json.data.refocus = (document.activeElement == current);
    
    current = e.target;
    var param = JSON.stringify(json);
    console.log(param); UserState.postMessage(param); })};console.log('===JS CODE INJECTED INTO MY WEBVIEW===');
Copy the code

UserState is the defined jsChannel that accepts a jsonString argument,data.initText is the initial text, and selectEnd SelectStart is the text selection position, and refocus is the mark that determines whether to re-click the Element of focus. When Element is clicked:

* Check if it is refocus and send it to channelCopy the code

ShowAndroidKeyboard is a PlatformChannel used to display the soft keyboard. The code can also be simplified. I will not write here, please contact the author for improvement. Also in the tread pit.

  • The key is that TextField gets focus alternately when TextField – Focusscop.of (context).requestfocus (focusNodeA) cannot invoke the keyboard, so another TextField -b is needed to requestFocus.
  • ShowAndroidKeyboard () is for some cases where the standby focusNode doesn’t get focus either, requiring us to manually invoke focus
  • There is no good solution for hiding the keyboard
bool refocus = data['refocus'] as bool;
    if (_focusNode2.hasFocus) {
      if(! refocus) FocusScope.of(context).requestFocus(_focusNode1);//FocusScope.of(context).requestFocus(_focusNode);
      if(! _focusNode1.hasFocus) {//hideAndroidKeyboard();
        showAndroidKeyboard();
      }
      // Set the initial text to the hidden TextField
      String initText = data['initText'];
      var selectionStart = data['selectionStart'];
      var selectionEnd = data['selectionEnd'];

      int end = initText.length;
      int start = initText.length;
      if (selectionEnd is int) {
        end = selectionEnd;
      }
      if (selectionStart is int) {
        start = selectionEnd;
      }
      print(selectionEnd);
      textController1.value = TextEditingValue(
        text: initText,
        selection: TextSelection(baseOffset: start, extentOffset: end),
      );
      //TextField requests that the keyboard be displayed
      FocusScope.of(context).requestFocus(_focusNode1);
    } else {
      if(! refocus) FocusScope.of(context).requestFocus(_focusNode2);//FocusScope.of(context).requestFocus(_focusNode);
      if(! _focusNode2.hasFocus) {//hideAndroidKeyboard();
        showAndroidKeyboard();
      }
      // Set the initial text to the hidden TextField
      String initText = data['initText'];
      var selectionStart = data['selectionStart'];
      var selectionEnd = data['selectionEnd'];

      int end = initText.length;
      int start = initText.length;
      if (selectionEnd is int) {
        end = selectionEnd;
      }
      if (selectionStart is int) {
        start = selectionEnd;
      }
      print(selectionEnd);
      textController2.value = TextEditingValue(
        text: initText,
        selection: TextSelection(baseOffset: start, extentOffset: end),
      );
      //TextField requests that the keyboard be displayed
      FocusScope.of(context).requestFocus(_focusNode2);
    }
Copy the code

TextField hidden behind webView

return Stack(
                  children: <Widget>[
                    TextField(
                      autofocus: false,
                      focusNode: _focusNode1,
                      controller: textController1,
                      onChanged: (text) {
                        controller.evaluateJavascript(''' if(current ! = null){ current.value = '${textController1.text}';
                          current.selectionStart = ${textController1.selection.start};
                          current.selectionEnd = ${textController1.selection.end}; current.dispatchEvent(new Event('input')); } ' ' ');
                      },
                      onSubmitted: (text) {
                        controller.evaluateJavascript(''' if(current ! = null) current.submit(); ' ' ');
                        _focusNode1.unfocus();
                      },
                    ),
                    TextField(
                      autofocus: false,
                      focusNode: _focusNode2,
                      controller: textController2,
                      onChanged: (text) {
                        controller.evaluateJavascript(''' if(current ! = null){ current.value = '${textController2.text}';
                          current.selectionStart = ${textController2.selection.start};
                          current.selectionEnd = ${textController2.selection.end}; current.dispatchEvent(new Event('input')); } ' ' ');
                      },
                      onSubmitted: (text) {
                        controller.evaluateJavascript(''' if(current ! = null) current.submit(); ' ' ');
                        _focusNode2.unfocus();
                      },
                    ),
                    WebView(....)
                  ]
  );
Copy the code
  • Adapt the text/ Selection of the Element of the current focus in the onChange TextField, and send an input event to trigger the Element changecurrent.dispatchEvent(new Event('input'));I think you all understand this pretty well.

I have to say Google is very sorry that WebView has such a big flaw, but it’s not 1.0.0 yet… The method in this paper is only a temporary solution, such as clicking blank to hide the soft keyboard and other functions have not been realized, only using some simple text input webView is still ok, and some input tags as buttons may need to manually hide the keyboard, above.