Flutter - inAppWebView 사용 방법, 메서드 설명, 당겨서 새로고침 기능 구현

반응형

 

Overview

해당 포스팅은 InAppWebView 5.8을 기준으로 작성되었습니다.

현재 버전 InAppwebView 6.0 마이그레이션은 아래을 포스팅을 참고하면 됩니다.

 

Flutter - InAppWebView 5.8.0 > 6.0.0 마이그레이션 가이드

개요인앱웹뷰가 6.0.0으로 버전업이 되었다. 이번에 마이그레이션을 하면서 느낀점은 편의성이 많이 좋아진 것 같은데 자세히는 안써봐서 잘 모르겠다. 우선은 내 프로젝트에 있는 코드들을 마

luvris2.tistory.com


inAppWebView를 이용하여 모바일 화면에 웹페이지를 표시하는 방법을 포스팅합니다.

 

포스팅에서 사용한 예제 프로젝트는 아래의 링크에서 확인 가능합니다.


프로젝트 설정

안드로이드

android/app/src/main/AndroidManifest.xml 에 권한을 추가한다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 네트워크 작업을 수행하기 위한 권한 부여 -->
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

iOS

ios/Runner/Info.plist 에 아래의 키를 추가한다.

<!-- 네트워크 작업을 수행하기 위한 권한 부여 -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>

Flutter Main 함수

main() 함수 내의 WidgetsFlutterBinding.ensureInitialized() 메서드 호출

애플리케이션 실행 전에 반드시 명시적으로 써주어야 한다.

이는 인앱웹뷰와 플러터 엔진과의 상호작용을 위해 바인딩을 해주는 기능을 한다.

Future main() async {
  // 위젯 바인딩 초기화 : 웹뷰와 플러터 엔진과의 상호작용을 위함
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    const MaterialApp(home: MyApp()),
  );
}

인앱웹뷰 초기 설정

컨트롤러 & 인앱웹뷰 초기 설정 정의

  • 옵션은 사용자 정의이므로 아래의 코드를 모두 따를 필요는 없다.
// 인앱웹뷰 컨트롤러
  InAppWebViewController? webViewController;
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
      // 플랫폼 상관없이 동작하는 옵션
      crossPlatform: InAppWebViewOptions(
        useShouldOverrideUrlLoading: true, // URL 로딩 제어
        mediaPlaybackRequiresUserGesture: false, // 미디어 자동 재생
        javaScriptEnabled: true, // 자바스크립트 실행 여부
        javaScriptCanOpenWindowsAutomatically: true, // 팝업 여부
      ),
      // 안드로이드 옵션
      android: AndroidInAppWebViewOptions(
        useHybridComposition: true, // 하이브리드 사용을 위한 안드로이드 웹뷰 최적화
      ),
      // iOS 옵션
      ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true, // 웹뷰 내 미디어 재생 허용
      ));

  late PullToRefreshController pullToRefreshController; // 당겨서 새로고침 컨트롤러

당겨서 새로고침 컨트롤러 초기화

<당겨서 새로고침 예시>

아래로 당길 경우 페이지가 새로고침되는 컨트롤러를 정의한다.

제스처로 새로고침을 필요로 할 경우 정의하며 필요 없으면 정의하지 않아도 된다.

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

    // 당겨서 새로고침 컨트롤러 설정
    pullToRefreshController = PullToRefreshController(
      options: PullToRefreshOptions(
        color: Colors.blue, // 새로고침 아이콘 색상
      ),
      // 플랫폼별 새로고침
      onRefresh: () async {
        if (Platform.isAndroid) {
          webViewController?.reload();
        } else if (Platform.isIOS) {
          webViewController?.loadUrl(
              urlRequest: URLRequest(url: await webViewController?.getUrl()));
        }
      },
    );
  }

인앱웹뷰 위젯 정의

InAppWebView(
    // 시작 페이지
    initialUrlRequest: URLRequest(
        url: Uri.parse("https://luvris2.tistory.com/")),
    // 초기 설정
    initialOptions: options,
    // 당겨서 새로고침 컨트롤러 정의
    pullToRefreshController: pullToRefreshController,
    // 인앱웹뷰 생성 시 컨트롤러 정의
    onWebViewCreated: (controller) {
      webViewController = controller;
    },
    // 페이지 로딩 시 수행 메서드 정의
    onLoadStart: (controller, url) {
      setState(() {
        this.url = url.toString();
      });
    },
    // 안드로이드 웹뷰에서 권한 처리 메서드 정의
    androidOnPermissionRequest:
        (controller, origin, resources) async {
      return PermissionRequestResponse(
          resources: resources,
          action: PermissionRequestResponseAction.GRANT);
    },
    // URL 로딩 제어
    shouldOverrideUrlLoading:
        (controller, navigationAction) async {
      var uri = navigationAction.request.url!;
      // 아래의 키워드가 포함되면 페이지 로딩
      if (![
        "http",
        "https",
        "file",
        "chrome",
        "data",
        "javascript",
        "about"
      ].contains(uri.scheme)) {
        if (await canLaunchUrl(Uri.parse(url))) {
          // Launch the App
          await launchUrl(
            Uri.parse(url),
          );
          // and cancel the request
          return NavigationActionPolicy.CANCEL;
        }
      }

      return NavigationActionPolicy.ALLOW;
    },
    // 페이지 로딩이 정지 시 메서드 정의
    onLoadStop: (controller, url) async {
      // 당겨서 새로고침 중단
      pullToRefreshController.endRefreshing();
      setState(() {
        this.url = url.toString();
      });
    },
    // 페이지 로딩 중 오류 발생 시 메서드 정의
    onLoadError: (controller, url, code, message) {
      // 당겨서 새로고침 중단
      pullToRefreshController.endRefreshing();
    },
    // 로딩 상태 변경 시 메서드 정의
    onProgressChanged: (controller, progress) {
      // 로딩이 완료되면 당겨서 새로고침 중단
      if (progress == 100) {
        pullToRefreshController.endRefreshing();
      }
      // 현재 페이지 로딩 상태 업데이트 (0~100%)
      setState(() {
        this.progress = progress / 100;
      });
    },
),

인앱웹뷰 위젯 / 컨트롤러 메서드 기능 설명

InAppWebView

  • initialUrlRequest : 인앱웹뷰 호출 시 시작 페이지 설정
  • initialOptions : 인앱웹뷰 호출 시 초기 설정
  • pullToRefreshController : 당겨서 새로고침 컨트롤러 정의
  • onWebViewCreated : 인앱웹뷰 생성 시  수행될 코드 정의
  • onLoadStart : 페이지 로딩 시작 시  수행될 코드 정의
  • onLoadStop : 페이지 로딩이 정지 시  수행될 코드 정의
  • onLoadError : 페이지 로딩 중 오류 발생 시  수행될 코드 정의
  • onProgressChanged : 페이지 로딩 상태 변경 시 메서드 정의
  • androidOnPermissionRequest : 안드로이드 웹뷰에서 권한 관련 코드 정의
  • shouldOverriderUrlLoading : URL 로딩 시 제어할 코드 정의

InAppWebViewController

  • .reload() : 새로고침
  • .loadUrl() : 특정 url 페이지 이동
  • .getUrl() : 현재 url 주소 반환
  • .goBack() : 이전 페이지로 이동
  • .goForward() : 이전 페이지에서 다음 페이지로 이동

전체 소스 코드

안드로이드, iOS 초기 설정을 제외하곤 한 번에 확인할 수 있도록 메인 파일 하나에 코드를 모두 담았다.

더보기
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';

Future main() async {
  // 위젯 바인딩 초기화 : 웹뷰와 플러터 엔진과의 상호작용을 위함
  WidgetsFlutterBinding.ensureInitialized();

  if (Platform.isAndroid) {
    await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
  }

  runApp(
    const MaterialApp(home: MyApp()),
  );
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // 인앱웹뷰 컨트롤러
  InAppWebViewController? webViewController;
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
      // 플랫폼 상관없이 동작하는 옵션
      crossPlatform: InAppWebViewOptions(
        useShouldOverrideUrlLoading: true, // URL 로딩 제어
        mediaPlaybackRequiresUserGesture: false, // 미디어 자동 재생
        javaScriptEnabled: true, // 자바스크립트 실행 여부
        javaScriptCanOpenWindowsAutomatically: true, // 팝업 여부
      ),
      // 안드로이드 옵션
      android: AndroidInAppWebViewOptions(
        useHybridComposition: true, // 하이브리드 사용을 위한 안드로이드 웹뷰 최적화
      ),
      // iOS 옵션
      ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true, // 웹뷰 내 미디어 재생 허용
      ));

  late PullToRefreshController pullToRefreshController; // 당겨서 새로고침 컨트롤러
  String url = ""; // url 주소
  double progress = 0; // 페이지 로딩 프로그레스 바

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

    // 당겨서 새로고침 컨트롤러 설정
    pullToRefreshController = PullToRefreshController(
      options: PullToRefreshOptions(
        color: Colors.blue, // 새로고침 아이콘 색상
      ),
      // 플랫폼별 새로고침
      onRefresh: () async {
        if (Platform.isAndroid) {
          webViewController?.reload();
        } else if (Platform.isIOS) {
          webViewController?.loadUrl(
              urlRequest: URLRequest(url: await webViewController?.getUrl()));
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: <Widget>[
            Expanded(
              child: Stack(
                children: [
                  InAppWebView(
                    // 시작 페이지
                    initialUrlRequest: URLRequest(
                        url: Uri.parse("https://luvris2.tistory.com/")),
                    // 초기 설정
                    initialOptions: options,
                    // 당겨서 새로고침 컨트롤러 정의
                    pullToRefreshController: pullToRefreshController,
                    // 인앱웹뷰 생성 시 컨트롤러 정의
                    onWebViewCreated: (controller) {
                      webViewController = controller;
                    },
                    // 페이지 로딩 시 수행 메서드 정의
                    onLoadStart: (controller, url) {
                      setState(() {
                        this.url = url.toString();
                      });
                    },
                    // 안드로이드 웹뷰에서 권한 처리 메서드 정의
                    androidOnPermissionRequest:
                        (controller, origin, resources) async {
                      return PermissionRequestResponse(
                          resources: resources,
                          action: PermissionRequestResponseAction.GRANT);
                    },
                    // URL 로딩 제어
                    shouldOverrideUrlLoading:
                        (controller, navigationAction) async {
                      var uri = navigationAction.request.url!;
                      // 아래의 키워드가 포함되면 페이지 로딩
                      if (![
                        "http",
                        "https",
                        "file",
                        "chrome",
                        "data",
                        "javascript",
                        "about"
                      ].contains(uri.scheme)) {
                        if (await canLaunchUrl(Uri.parse(url))) {
                          // Launch the App
                          await launchUrl(
                            Uri.parse(url),
                          );
                          // and cancel the request
                          return NavigationActionPolicy.CANCEL;
                        }
                      }

                      return NavigationActionPolicy.ALLOW;
                    },
                    // 페이지 로딩이 정지 시 메서드 정의
                    onLoadStop: (controller, url) async {
                      // 당겨서 새로고침 중단
                      pullToRefreshController.endRefreshing();
                      setState(() {
                        this.url = url.toString();
                      });
                    },
                    // 페이지 로딩 중 오류 발생 시 메서드 정의
                    onLoadError: (controller, url, code, message) {
                      // 당겨서 새로고침 중단
                      pullToRefreshController.endRefreshing();
                    },
                    // 로딩 상태 변경 시 메서드 정의
                    onProgressChanged: (controller, progress) {
                      // 로딩이 완료되면 당겨서 새로고침 중단
                      if (progress == 100) {
                        pullToRefreshController.endRefreshing();
                      }
                      // 현재 페이지 로딩 상태 업데이트 (0~100%)
                      setState(() {
                        this.progress = progress / 100;
                      });
                    },
                  ),
                  // 로딩 프로그레스 바 표현 여부
                  progress < 1.0
                      ? LinearProgressIndicator(value: progress)
                      : Container(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

참고

반응형