Flutter и InAppWebView Как получить высоту?

Во флаттере при использовании Webview иногда требуется отображать содержимое над и под ним.

Проблема - 1? Webview нужны ограничения, иначе он не может быть отображен: вам нужно обернуть Webview в контейнер с фиксированным размером или под столбец с расширяемым или что-то в этом роде.

Проблема - 2? Высота может быть легко получена, но иногда Webview загружает асинхронные данные, такие как встроенный скрипт, который изменяет стиль страницы. В этой ситуации трудно понять, как и когда получить высоту веб-просмотра.

С этой проблемой в большинстве случаев ваше веб-представление будет отображать полосу прокрутки, и при достижении нижней части веб-просмотра у пользователя будут проблемы с прокруткой вашей страницы флаттера. Прокрутка будет конфликтовать с веб-просмотром.

Как лучше всего это исправить?

3 ответа

Решение

Вам нужно будет добавить дополнительный элемент в свой html и использовать обработчик событий javascript.

Вместо того, чтобы передавать необработанный HTML-код в веб-просмотр, мы немного его настроим.

      final htmlFromApi = await getContent();
final html = Uri.dataFromString('''
                        <html lang="fr">
                           <meta name="viewport" content="width=device-width user-scalable=no zoom=1.1">
                           <style>img {max-width: 100%; height: auto}</style>
                           <body>
                            <div class="container" id="_flutter_target_do_not_delete">$htmlFromApi</div>
                            <script>
                            function outputsize() {
                                 window.flutter_inappwebview.callHandler('newHeight');
                            }
                            new ResizeObserver(outputsize).observe(_flutter_target_do_not_delete)
                            </script>
                          </body>
                        </html>
                      ''', mimeType: "text/html", encoding: Encoding.getByName("utf-8")).toString();

Как использовать Webview с этим:

      /// Define it to 1 else the Webview widget will not start loading.
double height = 1;
///some widgets
Container(
  height: height,
  child: InAppWebView(
    initialOptions: InAppWebViewGroupOptions(
      crossPlatform: InAppWebViewOptions(
        supportZoom: false,
        javaScriptEnabled: true,
        disableHorizontalScroll: true,
        disableVerticalScroll: true,
      ),
    ),
    onWebViewCreated: (InAppWebViewController controller) {
      controller.addJavaScriptHandler(
        handlerName: "newHeight",
        callback: (List<dynamic> arguments) async {
          final int height = await controller.getContentHeight();
          setState(() => this.height = height.toDouble());
        });
    },
    initialUrl: html,
  ),
),
///some widgets

Объяснение :

Когда содержимое div изменится, будет отправлено событие. Флаттер InAppWebView будет его слушать и вызывать обратный вызов. Мы просто можем обновить родительский контейнер WebView с текущей высотой представления. И это все, веб-просмотр отображается как виджет во флаттере без каких-либо проблем с размером.

            int? contentHeight = await _controller?.getContentHeight();
            double? zoomScale = await _controller?.getZoomScale();
            double htmlHeight = contentHeight!.toDouble() * zoomScale!;
            double htmlHeightFixed =
                double.parse(htmlHeight.toStringAsFixed(2));
            if (htmlHeightFixed == 0.0) {
              return;
            }
            setState(() {
              _htmlHeight = htmlHeightFixed + 0.1;
            });

ОБНОВЛЕНИЕ 2022 ДЕКАБРЬ!!

              ///
      double webViewHeight = 1;

      WebViewController? controller;


      WebView(
        initialUrl: widget.url,
        onWebViewCreated: (WebViewController c) {
          controller = c;
        },
        onPageFinished: (_) {
          updateHeight();
        },
        javascriptMode: JavascriptMode.unrestricted,
      );

     // This calculates the height
     void updateHeight() async {
       double height = double.parse(await 
          controller!.runJavascriptReturningResult('document.documentElement.scrollHeight;'));
       print(height); // prints height
     }

Основываясь на этом, я создал виджет, который вы можете поместить в список или любую прокручиваемую область. Он может автоматически определять высоту и устанавливать себя.

      class DynamicHeightWebView extends StatefulWidget {
  ///
  final String url;

  ///
  const DynamicHeightWebView({Key? key, required this.url}) : super(key: key);

  @override
  State<DynamicHeightWebView> createState() => _DynamicHeightWebViewState();
}

class _DynamicHeightWebViewState extends State<DynamicHeightWebView>
    with AutomaticKeepAliveClientMixin {
  ///
  double webViewHeight = 1;

  WebViewController? controller;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return SizedBox(
      height: webViewHeight,
      child: WebView(
        initialUrl: widget.url,
        onWebViewCreated: (WebViewController c) {
          controller = c;
        },
        onPageFinished: (_) {
          updateHeight();
        },
        javascriptMode: JavascriptMode.unrestricted,
      ),
    );
  }

  void updateHeight() async {
    double height = double.parse(await controller!.runJavascriptReturningResult(
        'document.documentElement.scrollHeight;'));
    if (webViewHeight != height) {
      setState(() {
        webViewHeight = height;
      });
    }
  }

  @override
  bool get wantKeepAlive => true;
}

Вы можете удалитьAutomaticKeepAliveClientMixinесли ты хочешь.

Другие вопросы по тегам