我想在 flutter web 视图中添加复习内容
 Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: Stack(
        children: <Widget>[
          Container(
            child: Center(
              child: Text(_title),
            ),
          ),
        ],
      )),
      body: SafeArea(
          child: WebView(
              key: _key,
              javascriptMode: JavascriptMode.unrestricted,
              initialUrl: _url)),
    );
  }
}
@peter-koltai:非常感谢!我真的很欣赏你的解决方案,高度工作正常,即使高度来得有点晚(页面内容可见,但滚动高度不存在),但还有其他问题。(抱歉我不能投票给你)
\n问题SingleChildScrollView:
SingleChildScrollView始终具有页面的绝对高度,例如,如果文本框没有从一开始就展开(javascript),则滚动高度超过页面高度。WebView整个滚动区域的高度,但不知道显示大小,因此如果出现底部或顶部模式表,它们不会在屏幕的视图区域中正确渲染,而是在滚动区域的绝对完整高度中渲染,所以你必须上下滚动例如 6000px。完整代码:
\n所以@shalin-shah的解决方案给了我这个很好的工作解决方案:\n如果你从页面的top=0开始,然后显示RefreshIndicator直到,我会计算向下拖动的距离(>屏幕高度的20%)onPageFinished。
webview.dart:\n如果RefreshIndicator达到Completer向下拖动的距离,则获取一个并开始旋转重新加载,如果页面加载完成则完成。
import \'package:flutter/material.dart\';\nimport \'dart:async\';\nimport \'dart:io\';\n\nimport \'package:flutter/foundation.dart\';\nimport \'package:flutter/gestures.dart\';\nimport \'package:flutter_web_refresh/pull_to_refresh.dart\';\nimport \'package:webview_flutter/webview_flutter.dart\';\n\nclass MyWebViewWidget extends StatefulWidget {\n  final String initialUrl;\n\n  const MyWebViewWidget({\n    Key? key,\n    required this.initialUrl,\n  }) : super(key: key);\n\n  @override\n  State<MyWebViewWidget> createState() => _MyWebViewWidgetState();\n}\n\nclass _MyWebViewWidgetState extends State<MyWebViewWidget> with WidgetsBindingObserver {\n\n  late WebViewController _controller;\n\n  // Drag to refresh helpers\n  final DragGesturePullToRefresh pullToRefresh = DragGesturePullToRefresh();\n  final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();\n\n  @override\n  void initState() {\n    super.initState();\n\n    WidgetsBinding.instance!.addObserver(this);\n    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();\n  }\n\n  @override\n  void dispose() {\n    // remove listener\n    WidgetsBinding.instance!.removeObserver(this);\n    super.dispose();\n  }\n\n  @override\n  void didChangeMetrics() {\n    // on portrait / landscape or other change, recalculate height\n    pullToRefresh.setRefreshDistance(MediaQuery.of(context).size.height);\n  }\n\n  @override\n  Widget build(context) {\n    return RefreshIndicator(\n      key: _refreshIndicatorKey,\n      onRefresh: () {\n        Completer<void> completer = pullToRefresh.refresh();\n        _controller.reload();\n        return completer.future;\n      },\n      child: WebView(\n        initialUrl: widget.initialUrl,\n        javascriptMode: JavascriptMode.unrestricted,\n        zoomEnabled: true,\n        gestureNavigationEnabled: true,\n        gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{\n          pullToRefresh.dragGestureRecognizer(_refreshIndicatorKey),\n        },\n        onWebViewCreated: (WebViewController webViewController) {\n          _controller = webViewController;\n          pullToRefresh.setController(_controller);\n        },\n        onPageStarted: (String url) { pullToRefresh.started(); },\n        onPageFinished: (finish) {    pullToRefresh.finished(); },\n        onWebResourceError: (error) {\n          debugPrint(\n              \'MyWebViewWidget:onWebResourceError(): ${error.description}\');\n          pullToRefresh.finished();\n        },\n      ),\n    );\n  }\n}\npull_to_refresh.dart:\n从页面top=0开始拖动并始终向下后,计算移动距离,当超过屏幕尺寸的20%时RefreshIndicator show()调用。
import \'package:flutter/material.dart\';\nimport \'dart:async\';\n\nimport \'package:flutter/foundation.dart\';\nimport \'package:flutter/gestures.dart\';\nimport \'package:webview_flutter/webview_flutter.dart\';\n\n// Fixed issue: https://github.com/flutter/flutter/issues/39389\nclass AllowVerticalDragGestureRecognizer extends VerticalDragGestureRecognizer {\n  @override\n  //override rejectGesture here\n  void rejectGesture(int pointer) {\n    acceptGesture(pointer);\n  }\n}\n\nclass DragGesturePullToRefresh {\n  static const double EXCEEDS_LOADING_TIME = 3000;\n  static const double REFRESH_DISTANCE_MIN = .2;\n\n  late WebViewController _controller;\n\n  // loading\n  Completer<void> completer = Completer<void>();\n  int msLoading = 0;\n  bool isLoading = true;\n\n  // drag\n  bool dragStarted = false;\n  double dragDistance = 0;\n  double refreshDistance = 200;\n\n  Factory<OneSequenceGestureRecognizer> dragGestureRecognizer(final GlobalKey<RefreshIndicatorState> refreshIndicatorKey) {\n    return Factory<OneSequenceGestureRecognizer>(() => AllowVerticalDragGestureRecognizer()\n    // Got the original idea from https://stackoverflow.com/users/15862916/shalin-shah:\n    // https://stackoverflow.com/questions/57656045/pull-down-to-refresh-webview-page-in-flutter\n      ..onDown = (DragDownDetails dragDownDetails) {\n        // if the page is still loading don\'t allow refreshing again\n        if (!isLoading ||\n            (msLoading > 0 && (DateTime.now().millisecondsSinceEpoch - msLoading) > EXCEEDS_LOADING_TIME)) {\n          _controller.getScrollY().then((scrollYPos) {\n            if (scrollYPos == 0) {\n              dragStarted = true;\n              dragDistance = 0;\n            }\n          });\n        }\n      }\n      ..onUpdate = (DragUpdateDetails dragUpdateDetails) {\n        calculateDrag(refreshIndicatorKey, dragUpdateDetails.delta.dy);\n      }\n      ..onEnd = (DragEndDetails dragEndDetails) { clearDrag(); }\n      ..onCancel = () { clearDrag(); });\n  }\n\n  void setController(WebViewController controller){ _controller = controller; }\n  void setRefreshDistance(double height){ refreshDistance = height * REFRESH_DISTANCE_MIN; }\n\n  Completer<void> refresh() {\n    if (!completer.isCompleted) {\n      completer.complete();\n    }\n    completer = Completer<void>();\n    started();\n    return completer;\n  }\n\n  void started() {\n    msLoading = DateTime.now().millisecondsSinceEpoch;\n    isLoading = true;\n  }\n\n  void finished() {\n    msLoading = 0;\n    isLoading = false;\n    // hide the RefreshIndicator\n    if (!completer.isCompleted) {\n      completer.complete();\n    }\n  }\n\n  void clearDrag() {\n    dragStarted = false;\n    dragDistance = 0;\n  }\n\n  void calculateDrag(final GlobalKey<RefreshIndicatorState> refreshIndicatorKey, double dy) async {\n    if (dragStarted && dy >= 0) {\n      dragDistance += dy;\n      // Show the RefreshIndicator\n      if (dragDistance > refreshDistance) {\n        debugPrint(\n            \'DragGesturePullToRefresh:refreshPage(): $dragDistance > $refreshDistance\');\n        clearDrag();\n        unawaited(refreshIndicatorKey.currentState?.show());\n      }\n    /*\n      The web page scrolling is not blocked, when you start to drag down from the top position of\n      the page to start the refresh process, e.g. like in the chrome browser. So the refresh process\n      is stopped if you start to drag down from the page top position and then up before reaching\n      the distance to start the refresh process.\n    */\n    } else {\n      clearDrag();\n    }\n  }\n}\n此修复对于手势事件flutter webview VerticalDragGestureRecognizer 没有回调但只有 onDown 和 onCancel很有帮助。
\n完整的代码也在github上。
\n\n=> 已修复:转到更新
\nRefreshIndicator通过向下拖动直到达到启动刷新过程的距离,不会显示初始动画。(可以不同方式添加)refreshPage()中的方法。pull_to_refresh.dart我发现差异无关 \xe2\x80\x8d\xe2\x99\x80\xef\xb8\x8f 因为这些问题破坏了浏览体验。
我更改了使用ScrollNotificationwhich在设置RefreshIndicator时解释正确。FixedScrollMetrics所以我们有原始动画,例如SingleChildScrollViewchrome 浏览器。
完整代码:
\nwebview.dart:
import \'package:flutter/material.dart\';\nimport \'dart:io\';\n\nimport \'package:flutter/foundation.dart\';\nimport \'package:flutter_web_refresh/pull_to_refresh.dart\';\nimport \'package:webview_flutter/webview_flutter.dart\';\n\nclass MyWebViewWidget extends StatefulWidget {\n  final String initialUrl;\n\n  const MyWebViewWidget({\n    Key? key,\n    required this.initialUrl,\n  }) : super(key: key);\n\n  @override\n  State<MyWebViewWidget> createState() => _MyWebViewWidgetState();\n}\n\nclass _MyWebViewWidgetState extends State<MyWebViewWidget>\n    with WidgetsBindingObserver {\n\n  late WebViewController _controller;\n  late DragGesturePullToRefresh dragGesturePullToRefresh;\n\n  @override\n  void initState() {\n    super.initState();\n\n    dragGesturePullToRefresh = DragGesturePullToRefresh();\n    WidgetsBinding.instance!.addObserver(this);\n    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();\n  }\n\n  @override\n  void dispose() {\n    // remove listener\n    WidgetsBinding.instance!.removeObserver(this);\n    super.dispose();\n  }\n\n  @override\n  void didChangeMetrics() {\n    // on portrait / landscape or other change, recalculate height\n    dragGesturePullToRefresh.setHeight(MediaQuery.of(context).size.height);\n  }\n\n  @override\n  Widget build(context) {\n    return\n        // NotificationListener(\n        // onNotification: (scrollNotification) {\n        //  debugPrint(\'MyWebViewWidget:NotificationListener(): $scrollNotification\');\n        //  return true;\n        // }, child:\n      RefreshIndicator(\n        onRefresh: () => dragGesturePullToRefresh.refresh(),\n        child: Builder(\n          builder: (context) => WebView(\n            initialUrl: widget.initialUrl,\n            javascriptMode: JavascriptMode.unrestricted,\n            zoomEnabled: true,\n            gestureNavigationEnabled: true,\n            gestureRecognizers: {Factory(() => dragGesturePullToRefresh)},\n            onWebViewCreated: (WebViewController webViewController) {\n              _controller = webViewController;\n              dragGesturePullToRefresh\n                  .setContext(context)\n                  .setController(_controller);\n            },\n            onPageStarted: (String url) { dragGesturePullToRefresh.started(); },\n            onPageFinished: (finish) {    dragGesturePullToRefresh.finished();},\n            onWebResourceError: (error) {\n              debugPrint(\n                  \'MyWebViewWidget:onWebResourceError(): ${error.description}\');\n              dragGesturePullToRefresh.finished();\n            },\n          ),\n        ),\n      );\n  }\n}\npull_to_refresh.dart:
import \'package:flutter/cupertino.dart\';\nimport \'package:flutter/material.dart\';\nimport \'dart:async\';\n\nimport \'package:flutter/gestures.dart\';\nimport \'package:webview_flutter/webview_flutter.dart\';\n\n// Fixed issue: https://github.com/flutter/flutter/issues/39389\nclass DragGesturePullToRefresh extends VerticalDragGestureRecognizer {\n  static const double EXCEEDS_LOADING_TIME = 3000;\n\n  late BuildContext _context;\n  late WebViewController _controller;\n\n  // loading\n  Completer<void> completer = Completer<void>();\n  int msLoading = 0;\n  bool isLoading = true;\n\n  // drag\n  double height = 200;\n  bool dragStarted = false;\n  double dragDistance = 0;\n\n  @override\n  //override rejectGesture here\n  void rejectGesture(int pointer) {\n    acceptGesture(pointer);\n  }\n\n  void _clearDrag() {\n    dragStarted = false;\n    dragDistance = 0;\n  }\n\n  DragGesturePullToRefresh setContext(BuildContext context) { _context = context; return this; }\n  DragGesturePullToRefresh setController(WebViewController controller) { _controller = controller; return this; }\n\n  void setHeight(double height) { this.height = height; }\n\n  Future refresh() {\n    if (!completer.isCompleted) {\n      completer.complete();\n    }\n    completer = Completer<void>();\n    started();\n    _controller.reload();\n    return completer.future;\n  }\n\n  void started() {\n    msLoading = DateTime.now().millisecondsSinceEpoch;\n    isLoading = true;\n  }\n\n  void finished() {\n    msLoading = 0;\n    isLoading = false;\n    // hide the RefreshIndicator\n    if (!completer.isCompleted) {\n      completer.complete();\n    }\n  }\n\n  FixedScrollMetrics _getMetrics(double minScrollExtent, double maxScrollExtent,\n      double pixels, double viewportDimension, AxisDirection axisDirection) {\n    return FixedScrollMetrics(\n        minScrollExtent: minScrollExtent,\n        maxScrollExtent: maxScrollExtent,\n        pixels: pixels,\n        viewportDimension: viewportDimension,\n        axisDirection: axisDirection);\n  }\n\n  DragGesturePullToRefresh() {\n    onStart = (DragStartDetails dragDetails) {\n      // debugPrint(\'MyWebViewWidget:onStart(): $dragDetails\');\n      if (!isLoading ||\n          (msLoading > 0 && (DateTime.now().millisecondsSinceEpoch - msLoading) > EXCEEDS_LOADING_TIME)) {\n        _controller.getScrollY().then((scrollYPos) {\n          if (scrollYPos == 0) {\n            dragStarted = true;\n            dragDistance = 0;\n            ScrollStartNotification(\n                    metrics: _getMetrics(0, height, 0, height, AxisDirection.down),\n                    dragDetails: dragDetails,\n                    context: _context)\n                .dispatch(_context);\n          }\n        });\n      }\n    };\n    onUpdate = (DragUpdateDetails dragDetails) {\n      if (dragStarted) {\n        double dy = dragDetails.delta.dy;\n        dragDistance += dy;\n        ScrollUpdateNotification(\n                metrics: _getMetrics(\n                    dy > 0 ? 0 : dragDistance, height,\n                    dy > 0 ? (-1) * dy : dragDistance, height,\n                    dragDistance < 0 ? AxisDirection.up : AxisDirection.down),\n                context: _context,\n                scrollDelta: (-1) * dy)\n            .dispatch(_context);\n        if (dragDistance < 0) {\n          _clearDrag();\n        }\n      }\n    };\n    onEnd = (DragEndDetails dragDetails) {\n      ScrollEndNotification(\n              metrics: _getMetrics(0, height, dragDistance, height, AxisDirection.down),\n              context: _context)\n          .dispatch(_context);\n      _clearDrag();\n    };\n    onCancel = () {\n      ScrollUpdateNotification(\n              metrics: _getMetrics(0, height, 1, height, AxisDirection.up),\n              context: _context,\n              scrollDelta: 0)\n          .dispatch(_context);\n      _clearDrag();\n    };\n  }\n}\n| 归档时间: | 
 | 
| 查看次数: | 3625 次 | 
| 最近记录: |