JS window.open() 명령어로 되어있는 웹을 flutter webview에서 실행시키기 위해 얼마나 오랜 기간을 삽질해왔는지 ㅠㅠㅠ

 

!!!!!!!! 드디어 해결책을 찾았다!!!! 

그래서 고생할 지도 모를 지구 어딘가의 flutter 개발자를 위해 글을 남긴다. 

 

새 창을 띄우기 위해서는 flutter webview 라이브러리가 아닌 InAppWebView를 사용해야한다.

그런데 마침 이번에 6.0으로 업데이트를 했다길래 냉큼 새 버전으로 탑승

 

https://inappwebview.dev/docs/intro

 

Getting Started | InAppWebView

Installation

inappwebview.dev:443

 

 

설치방법 생략

 

그리고 대망의 window.open() flutter app 에서 실행하기!!!!!!!!!

라이브러리 기여자들이 굉장히 예제를 성실하게 남겨주었다.

 

예제 참조:

https://github.com/pichillilorenzo/flutter_inappwebview_examples/blob/main/popup_window/lib/main.dart

 

flutter_inappwebview_examples/popup_window/lib/main.dart at main · pichillilorenzo/flutter_inappwebview_examples

A collection of flutter_inappwebview project examples - pichillilorenzo/flutter_inappwebview_examples

github.com

 

이것만있으면 뚝딱이다.

 

하지만 사람의 욕심은 끝이 없다. 아니 새 창을 띄웠으면 닫아야죠... 뒤로가기 버튼을 아무리 눌러도 안되는데 이거 머임ㅠ

안드로이드 뒤로가기 버튼을 백날 누르고 goBack() 함수를 불러도 먹히지 않았다.

그렇게 검색을 해서 찾아보면 willPopScope가 나온다.

 

However, But, Although . . .

willPopScope is deprecated. 

 

참고자료 : 

 

https://api.flutter.dev/flutter/widgets/WillPopScope-class.html

 

WillPopScope class - widgets library - Dart API

Registers a callback to veto attempts by the user to dismiss the enclosing ModalRoute. See also: Inheritance Annotations @Deprecated('Use PopScope instead. ' 'This feature was deprecated after v3.12.0-1.0.pre.') Constructors WillPopScope({Key? key, require

api.flutter.dev

 

그래서 친절하게 migration guide도 준비되어있다.

https://docs.flutter.dev/release/breaking-changes/android-predictive-back#migration-guide

 

Android predictive back

The ability to control back navigation at the time that a back gesture is received has been replaced with an ahead-of-time navigation API in order to support...

docs.flutter.dev

 

 

 

 

 

 

그래서 위의 레퍼런스를 참조해서 해결한 전체 코드:

 

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

//InAppWebViewController Singleton
class WebControllerSingleton {
  WebControllerSingleton._privateConstructor();
  static final WebControllerSingleton _instance = WebControllerSingleton._privateConstructor();
  factory WebControllerSingleton() {
    return _instance;
  }

  InAppWebViewController? _webController;
  InAppWebViewController? get webController => _webController;
  set webController(InAppWebViewController? controller) {
    _webController = controller;
  }
}

//main webView Screen
class InAppWebViewScreen extends StatefulWidget {
  const InAppWebViewScreen({Key? key}):super(key:key);

  @override
  State<InAppWebViewScreen> createState() => _InAppWebViewScreenState();
}

class _InAppWebViewScreenState extends State<InAppWebViewScreen> {
  final GlobalKey webViewKey = GlobalKey();

  final urlController = TextEditingController();
  final String loginUrl = "https://www.naver.com";
  String url = "";

  PullToRefreshController? pullToRefreshController;
  double progress = 0;

  InAppWebViewSettings settings = InAppWebViewSettings(
    isInspectable: kDebugMode,
    javaScriptEnabled: true,
    javaScriptCanOpenWindowsAutomatically: true,
    iframeAllowFullscreen: true,
    useOnDownloadStart: true,
    useOnLoadResource: true,
    clearCache: true,
    allowFileAccessFromFileURLs: true,
    allowUniversalAccessFromFileURLs: true,
  );

  @override
  void initState() {
    super.initState();

    pullToRefreshController = kIsWeb
        ? null
        : PullToRefreshController(
      settings: PullToRefreshSettings(
        color: Colors.black,
      ),
      onRefresh: () async {
        if (defaultTargetPlatform == TargetPlatform.android) {
          WebControllerSingleton().webController?.reload();
        } else if (defaultTargetPlatform == TargetPlatform.iOS) {
          WebControllerSingleton().webController?.loadUrl(
            urlRequest:
            URLRequest(
                url: await WebControllerSingleton().webController?.getUrl()),
          );
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvoked: (didPop) async {
        print("------ try go back");
        await WebControllerSingleton().webController?.goBack();
      },
      child: Scaffold(
        body: Column(
          children: <Widget>[
            Expanded(
              child: InAppWebView(
                key: webViewKey,
                initialUrlRequest: URLRequest(url: WebUri(loginUrl)),
                onWebViewCreated: (controller) {
                //init controller
                  WebControllerSingleton().webController = controller;
                },
                initialSettings: settings,
                pullToRefreshController: pullToRefreshController,
                onCreateWindow: (controller, createWindowAction) async {
                  showDialog(
                    context: context,
                    builder: (context) {
                      return WindowPopup(createWindowAction: createWindowAction);
                    },
                  );
                  return true;
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

}

//popup window class
class WindowPopup extends StatefulWidget {
  final CreateWindowAction createWindowAction;

  const WindowPopup({Key? key, required this.createWindowAction})
      : super(key: key);

  @override
  State<WindowPopup> createState() => _WindowPopupState();
}

class _WindowPopupState extends State<WindowPopup> {
  String title = '';

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      content: SizedBox(
        width: double.maxFinite,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Row(mainAxisSize: MainAxisSize.max, children: [
              Expanded(
                child:
                Text(title, maxLines: 1, overflow: TextOverflow.ellipsis),
              ),
              IconButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  icon: const Icon(Icons.close))
            ]),
            Expanded(
              child: InAppWebView(
                gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
                  Factory<OneSequenceGestureRecognizer>(
                        () => EagerGestureRecognizer(),
                  ),
                },
                windowId: widget.createWindowAction.windowId,
                onTitleChanged: (controller, title) {
                  setState(() {
                    this.title = title ?? '';
                  });
                },
                onCloseWindow: (controller) {
                  Navigator.pop(context);
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

 

 

나는 webViewController를 제어하기 위해 Singleton으로 만들어버렸다. 사실 onCreateWindow에서 새로 호출하는 AlertDialog를 Scaffold로 띄워보려고 시도했던 흔적이기도함.. 안드에서는 전체화면으로 잘 나오는데 ios에서는 정말 팝업으로 작게 나타나서 해결 시도중입니다. 해결책 아시는분 제발 글 써주세요.

 

 

예제로 적은 링크는 네이버. 대한민국 개발자라면 라인 지켜. 라인 망하면 다 망하는거야. 라인 보고있나? 저도 같이 일하게 해주세요. 저 영어도 할 수 있어요.

+ Recent posts