LoginSignup
2
1

More than 1 year has passed since last update.

Flutter WebView+PullRefresh

Last updated at Posted at 2022-11-06

この記事は、現状のFlutterSDKで中でAndroid側で発生している問題を回避するだけのものです

課題

・iOSはWebViewのGestureを使用したやり方の記事が散見される
・AndroidはWebView+PullToRefreshがうまく動作しないバグがある

私が行った対応

iOS

WebView+pullToRefresh

android

Stackの中にWebViewとPullToRefreshを別々に入れる

class AllowVerticalDragGestureRecognizer extends VerticalDragGestureRecognizer {
  @override
  void rejectGesture(int pointer) {
    acceptGesture(pointer);
  }
}
// ignore: must_be_immutable
class WebviewPage extends StatelessWidget {
  WebviewPage({Key? key, required this.initialUrl}) : super(key: key);

  final String initialUrl;
  WebViewController? controller;
  final refreshController = RefreshController();

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        // Androidの戻るボタンをwebviewのbackに反映させる
        if (await controller?.canGoBack() ?? false) {
          controller!.goBack();
        }
        return false;
      },
      child: Scaffold(
        body: Platform.isIOS ? _iosWidget : _androidWidget,
      ),
    );
  }

  Widget get _iosWidget {
    return SmartRefresher(
      onRefresh: () async {
        await controller?.reload();
        refreshController.refreshCompleted();
      },
      controller: refreshController,
      header: Platform.isIOS
          ? CustomHeader(
        builder: (_, __) {
          return const CupertinoActivityIndicator(
            color: Colors.white,
          );
        },
      )
          : const MaterialClassicHeader(),
      child: _customWebView(),
    );
  }

  Widget get _androidWidget {
    return Stack(alignment: AlignmentDirectional.topCenter, children: [
      _customWebView(),
      Positioned(
        top: 0,
        left: 0,
        right: 0,
        height: 80,
        child: IgnorePointer(
          ignoring: true,
          child: SmartRefresher(
            onRefresh: () async {
              await controller?.reload();
              Future.delayed(const Duration(milliseconds: 500)).then((_) {
                refreshController.refreshCompleted();
              });
            },
            controller: refreshController,
            header: Platform.isIOS
                ? CustomHeader(
              builder: (_, __) {
                return const CupertinoActivityIndicator(
                  color: Colors.white,
                );
              },
            )
                : const MaterialClassicHeader(),
            child: Container(
              height: 0,
            ),
          ),
        ),
      ),
    ]);
  }

  Widget _customWebView() {
    final gesture = <Factory<AllowVerticalDragGestureRecognizer>>{};
    gesture.add(Factory<AllowVerticalDragGestureRecognizer>(
          () => Platform.isIOS ? _iosGesture : _androidGesture,
    ));

    return WebView(
      userAgent: Const.userAgent,
      backgroundColor: Colors.black,
      javascriptMode: JavascriptMode.unrestricted,
      gestureRecognizers: gesture,
      onWebViewCreated: (controller) {
        this.controller = controller;
      },
    );
  }

  AllowVerticalDragGestureRecognizer get _iosGesture {
    final gesture = AllowVerticalDragGestureRecognizer();
    bool refreshFlag = false;
    gesture.onDown = (_) {
      controller!.getScrollY().then(
            (value) {
          if (value == 0) {
            refreshFlag = true;
          }
        },
      );
    };
    gesture.onEnd = (_) {
      if (refreshFlag) {
        refreshFlag = false;
        controller!.getScrollY().then((value) {
          if (value < 0) {
            refreshController.requestRefresh();
          }
        });
      }
    };
    return gesture;
  }

  AllowVerticalDragGestureRecognizer get _androidGesture {
    final gesture = AllowVerticalDragGestureRecognizer();
    bool refreshFlag = false;
    gesture.onStart = (startDetails) {
      controller!.getScrollY().then(
            (value) {
          if (value == 0) {
            refreshFlag = true;
          }
        },
      );
    };
    gesture.onEnd = (DragEndDetails dragEndDetails) {
      if (refreshFlag) {
        refreshFlag = false;
        controller!.getScrollY().then((value) {
          if (dragEndDetails.primaryVelocity == 0) {
            return;
          }
          if (value == 0) {
            refreshController.requestRefresh();
          }
        });
      }
    };
    return gesture;
  }
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1