이 영역을 누르면 첫 페이지로 이동
자라자 블로그의 첫 페이지로 이동

자라자

페이지 맨 위로 올라가기

플러터로 웹뷰 세팅하기

자라자

플러터로 웹뷰 세팅하기

  • 2022.08.01 00:33
  • Dev Log

Flutter 앱에 WebView 추가 를 읽으면서 정리한 글입니다.

pubspec.yaml에 의존성 추가

아래 명령어로 플러그인을 추가합니다.

flutter pub add webview_flutter
flutter pub get

main.dart 에 웹뷰 추가

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(
const MaterialApp(
home: WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({Key? key}) : super(key: key);
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: const WebView(
initialUrl: 'https://flutter.dev',
),
);
}
}

하이브리드 컴포지션 설정

import 'dart:io'; // Add this import.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(
const MaterialApp(
home: WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({Key? key}) : super(key: key);
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
// Add from here ...
@override
void initState() {
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
super.initState();
}
// ... to here.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
),
body: const WebView(
initialUrl: 'https://flutter.dev',
),
);
}
}

하이브리드 컴포지션이란 네이티브 웹뷰를 플러터 위젯 안에서 보여주는 것을 의미합니다.

페이지 로드 이벤트 수신 대기

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({Key? key}) : super(key: key);
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebView(
initialUrl: 'https://flutter.dev',
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}

위 코드처럼 웹뷰를 웹뷰스택이라는 Stateful 컴포넌트로 감싸줄 수 있습니다. 그리고 웹뷰스택의 로딩 상태값을 웹뷰의 로딩이벤트에서 조작하는 방식으로 설정하면 웹뷰 가 로드되는 동안 linearProgressIndicator가 움직이는 것을 보여줄 수 있습니다.

WebViewController를 사용하여 작업하기

WebViewStack을 아래와 같이 업데이트합니다.

import 'dart:async'; // Add this import for Completer
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({required this.controller, Key? key}) : super(key: key); // Modify
final Completer<WebViewController> controller; // Add this attribute
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebView(
initialUrl: 'https://flutter.dev',
// Add from here ...
onWebViewCreated: (webViewController) {
widget.controller.complete(webViewController);
},
// ... to here.
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}

컨트롤러를 추가합니다. 컨트롤러는 Completer로, 콜백을 Future(Promise) 로 만듭니다.

탐색 컨트롤 만들기

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NavigationControls extends StatelessWidget {
const NavigationControls({required this.controller, Key? key})
: super(key: key);
final Completer<WebViewController> controller;
@override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: controller.future,
builder: (context, snapshot) {
final WebViewController? controller = snapshot.data;
if (snapshot.connectionState != ConnectionState.done ||
controller == null) {
return Row(
children: const <Widget>[
Icon(Icons.arrow_back_ios),
Icon(Icons.arrow_forward_ios),
Icon(Icons.replay),
],
);
}
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () async {
if (await controller.canGoBack()) {
await controller.goBack();
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No back history item')),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: () async {
if (await controller.canGoForward()) {
await controller.goForward();
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('No forward history item')),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: () {
controller.reload();
},
),
],
);
},
);
}
}

위 코드로 네비게이션 바를 만들 수 있습니다. 컨트롤러를 상속받아서 뒤로/앞으로/새로고침 등을 수행합니다.

import 'dart:async'; // Add this import
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart'; // Add this import back
import 'src/navigation_controls.dart'; // Add this import
import 'src/web_view_stack.dart';
void main() {
runApp(
const MaterialApp(
home: WebViewApp(),
),
);
}
class WebViewApp extends StatefulWidget {
const WebViewApp({Key? key}) : super(key: key);
@override
State<WebViewApp> createState() => _WebViewAppState();
}
class _WebViewAppState extends State<WebViewApp> {
final controller = Completer<WebViewController>(); // Instantiate the controller
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView'),
// Add from here ...
actions: [
NavigationControls(controller: controller),
],
// ... to here.
),
body: WebViewStack(controller: controller), // Add the controller argument
);
}
}

위 코드처럼 메인에서 컨트롤러를 만들고 아래로 전달해줄 수 있습니다.

NavigationDelegate로 탐색 추적

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewStack extends StatefulWidget {
const WebViewStack({required this.controller, Key? key}) : super(key: key);
final Completer<WebViewController> controller;
@override
State<WebViewStack> createState() => _WebViewStackState();
}
class _WebViewStackState extends State<WebViewStack> {
var loadingPercentage = 0;
@override
Widget build(BuildContext context) {
return Stack(
children: [
WebView(
initialUrl: 'https://flutter.dev',
onWebViewCreated: (webViewController) {
widget.controller.complete(webViewController);
},
onPageStarted: (url) {
setState(() {
loadingPercentage = 0;
});
},
onProgress: (progress) {
setState(() {
loadingPercentage = progress;
});
},
onPageFinished: (url) {
setState(() {
loadingPercentage = 100;
});
},
// Add from here ...
navigationDelegate: (navigation) {
final host = Uri.parse(navigation.url).host;
if (host.contains('youtube.com')) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Blocking navigation to $host',
),
),
);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
// ... to here.
),
if (loadingPercentage < 100)
LinearProgressIndicator(
value: loadingPercentage / 100.0,
),
],
);
}
}

NavigationDelegate를 이용하여 탐색을 추적하고 차단할 수 있습니다.

'Dev Log' 카테고리의 다른 글

Lighthouse로 성능 개선하기  (0) 2022.08.30
gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기  (0) 2022.08.07
온룸 파일 다운로드 관련 이슈와 해결한 방법  (0) 2022.02.11
TypeScript Object, object, {} 비교  (0) 2021.11.26
오픈소스에 기여하는 법 : MDN 문서에 기여하기  (0) 2021.11.10

댓글

댓글을 사용할 수 없습니다.

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • Lighthouse로 성능 개선하기

    Lighthouse로 성능 개선하기

    2022.08.30
  • gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기

    gitlab에서 github으로 미러링 과정 중 인증 문제 해결하기

    2022.08.07
  • 온룸 파일 다운로드 관련 이슈와 해결한 방법

    온룸 파일 다운로드 관련 이슈와 해결한 방법

    2022.02.11
  • TypeScript Object, object, {} 비교

    TypeScript Object, object, {} 비교

    2021.11.26
다른 글 더 둘러보기

정보

자라자 블로그의 첫 페이지로 이동

자라자

  • 자라자의 첫 페이지로 이동

검색

메뉴

  • 🏠 HOME
  • 💡 ABOUT
  • 💻 GITHUB

카테고리

  • 분류 전체보기 (91)
    • Tech Note (3)
    • Dev Log (11)
    • Study Log (11)
    • Settings (3)
    • PS (53)
      • Programmers (21)
      • BOJ (32)
    • Diary (10)

인기 글

공지사항

태그

  • ㅣ
  • 리액트
  • 공식문서읽기

정보

자라자의 자라자

자라자

자라자

블로그 구독하기

  • 구독하기
  • RSS 피드

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. © 자라자. Designed by Fraccino.

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.