how to detect when inner html changes in flutter webview

Viewed 517

I'm using webview_flutter to view a page from the URL on my Flutter application, what I need to do is to detect when changes happen in the HTML code without changing the URL. as there is some data changing overtime using JS and I need to check for HTML and fetch some data out of it whenever it changes, without the need to use any of

 void Function(String)? onPageStarted,
  void Function(String)? onPageFinished,
  void Function(int)? onProgress,

because the page is not reloading or the URL is not changing, everything is the same but the HTML code. here is what the Web view looks like:

WebView(
                            gestureRecognizers: <
                                Factory<OneSequenceGestureRecognizer>>{
                              Factory<VerticalDragGestureRecognizer>(
                                  () => VerticalDragGestureRecognizer()),
                              Factory<HorizontalDragGestureRecognizer>(
                                  () => HorizontalDragGestureRecognizer()),
                              Factory<ScaleGestureRecognizer>(
                                  () => ScaleGestureRecognizer()),
                            },
                            zoomEnabled: true,
                            debuggingEnabled: true,
                            initialUrl: controller.mapURL.value,
                            javascriptMode: JavascriptMode.unrestricted,
                            onWebViewCreated:
                                (WebViewController webViewController) {
                              controller.mapWebViewController =
                                  webViewController;
                            },
                          ),

and here is how to get the HTML content

                              var html = await controller.mapWebViewController!
                                  .evaluateJavascript(
                                      "window.document.getElementsByTagName('html')[0].outerHTML;");
                              print(HTML);

1 Answers

I somewhat managed to do this by injecting some JavaScript of my own into the webpage and having it send back a message whenever the desired action occur on the page.

In my scenario, I wanted to get notified when a success alert shows up on the web page. I added an event listener for DOMSubtreeModified that gets triggered whenever there's a change in the HTML document.

First I create a Javascript channel on the Flutter side. When the page finishes loading, using the runJavascript method, I inject the JavaScript snippet.

Now when that alert showed up on the page, the listener functions detects that and send a message back to Flutter using the postMessage method.

class _MyWebViewState extends State<MyWebView> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();

  @override
  void initState() {
    if (Platform.isAndroid) {
      WebView.platform = SurfaceAndroidWebView();
    }

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        FutureBuilder<WebViewController>(
          future: _controller.future,
          builder: (context, snapshot) {
            return WebView(
              initialUrl: widget._buildUrl(),
              javascriptMode: JavascriptMode.unrestricted,
              javascriptChannels: _createJavaScriptChannels(context),
              onWebViewCreated: (webViewController) {
                _controller.complete(webViewController);
              },
              onPageFinished: (url) {
                if (snapshot.hasData) {
                  snapshot.data!.runJavascript(js);
                }
              },
            );
          },
        ),
      ],
    );
  }

  Set<JavascriptChannel> _createJavaScriptChannels(BuildContext context) {
    return {
      JavascriptChannel(
        name: 'MobileApp',
        onMessageReceived: (message) {
            if (message.message == 'success') {
              ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(message.message)),
            );
          }
        },
      ),
    };
  }
}

String js = '''    
document.addEventListener("DOMSubtreeModified", onDetection);

function onDetection() {
  const successAlertExists =
    document.querySelectorAll("div.alert-success").length > 0;
  if (successAlertExists) {
    MobileApp.postMessage("success");
    
    document.removeEventListener("DOMSubtreeModified", onDetection);
  }
}
''';
Related