일정 작업이 완료되면 실행되는 함수이며, 특정 조건이 성립될 때 실행되기 때문에 이름이 콜백 함수이다. 예를 들어 유저가 화면에 터치 했을 때 실행할 함수나 웹 뷰의 로딩이 완료됏을 때 실행항 콜백 함수는 다음과 같다.
WebView(
initialUrl: "https://blog.codefactory.ai",
javascriptMode: JavascriptMode.unrestricted,
onPageFinished: (String url) {
print(url);
},
)
웹뷰는 프레임워크에 내장된 브라우저를 앱의 네이트브 컴포넌트에 임베딩하는 기능이다. 다시 말해 앱에서 웹브라우저 기능을 구현해주는 기술이다.
속성 | 설명 |
---|---|
initalUrl | 처음 실행할 웹사이트의 주소입니다. |
javascriptMode | 웹뷰에서 자바스크립트 실행을 허용할지 여부를 결정할 수 있다. |
onWebViewCreated | 웹뷰 위젯이 생성되면 실행할 콜백 함수이다.(뒤로가기, 앞으로가기, 새로운 URL 실행하기 등 기능을 조작할 수 있다) |
onPageStarted | 웹뷰가 처음 생성되거나 페이지를 이동했을 때 웹페이지가 로딩되기 시작하면 실행할 콜백 함수이다. |
onPageFinished | 웹페이지 로딩이 끝나면 실행된다. |
onProgress | 웹페이지가 로딩 중일 때 지속적으로 실행되며 페이지의 로딩이 끝날 때 까지 실행된다. 매개변수에 int값으로 페이지 로딩 상태를 0부터 100사이의 숫자로 제공해준다. |
각 네이티브 플랫폼으로 코드가 컴파일되므로 최소한의 네이티브 설정은 필요하다. 인터넷 접근권한, 카메라, 사진첩, 푸쉬 권한 등 보안에 민감한 사항이나 하드웨어에 접근할 때도 네이티브 설정을 해야한다.
네이티브 설저이 필요한 경우는 일반적으로 해당 플러그인의 pub.dev 소개 페이지에서 확인할 수 있다.
pubspec.yaml 파일은 플러터 프로젝트와 관련된 설정을 하는 파일이다. 프로젝트에서 사용할 이미지 및 폰트를 지정하거나 사용할 오픈 소스 프로젝트들을 명시할 때 사용된다.
# webview_flutter 설치하기
dependencies:
cupertino_icons: ^1.0.6
flutter:
sdk: flutter
webview_flutter: ^4.4.2
명령어 | 설명 |
---|---|
flutter pub get | pubspec.yaml 파일에 등록한 플ㄹ그인들을 내려받습니다. |
flutter pub add [플러그인 이름] | pubspec.yaml에 플러그인을 추가합니다. |
flutter pub upgrade | pubspec.yaml에 등록된 플러그인들을 모두 최신 버전으로 업데이트 합니다. |
flutter pub run | 현재 프로젝트를 실행합니다. |
웹뷰를 사용하기 위해서는 몇 가지 네이티브 설정이 필요하다. 인터넷 사용 권한을 추가하고 https 프로토콜뿐만 아니라 http 프로토콜도 이용할 수 있도록 수정해야한다.
android/app/src/main/AndroidManifest.xml 파일에서 각종 권한을 설정할 수 있다.
아래와 같이 인터넷 권한을 추가해준다.
<!-- 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는 기본적으로 http 웹사이트를 사용할 수 없도록 설정되어있는데 이를 허용해주려면 아래코드를 추가해주면 된다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/ >
<application
android:label="blog_web_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true" // 추가
>
<!-- 생략 --!>
</application>
</manifest>
코드 | 설명 |
---|---|
INTERNET | 인터넷 사용 권한 |
CAMERA | 카메라 사용 권한 |
WRITE_EXTERNAL_STORAGE | 앱 외부에 파일을 저장할 수 있는 권한 |
READ_EXTERNAL_STORAGE | 앱 외부의 파일을 읽을 수 있는 권한 |
VIBRATE | 진동을 일으킬 수 있는 권한 |
ACCESS_FINE_LOCATION | GPS와 네트워크를 모두 사용해서 정확한 현재 위치 정보를 가져올 수있는 권한 |
ACCESS_COARSE_LOCATION | 네트워크만 사용해서 대략적인 위치 정보를 가져올 수 있는 권한 |
ACCESS_BACKGROUND_LOCATION | 앱이 배경에 있을 때 위치 정보를 얻을 수 있는 권한 |
BILLING | 인앱 결제를 할 수 있는 권한 |
CALL_PHONE | 전화기 앱을 사용하지 않고 전화를 할 수 있는 권한 |
NETWORK_STATE | 네트워크 상태를 가져올 수 있는 권한 |
RECORD_AUDIO | 음성 녹음 할 수 있는 권한 |
android/app/build.gradle 파일은 안드로이드의 빌드 툴인 그레이들 설정파일이다.
android/build.gradle과 android/app/build.gradle은 서로 다른 파일이니 유의해야한다.
android/build.gradle : 프로젝트 파일이며 주로 클래스패스나 레포지토리 정보를 입력한다.
android/app/build.gradle : 모듈 파일이며 의존성이나 버전 정보를 관리한다. ****
아래와 같이 변경해준다.
android {
// 생략
compileSdk 32
defaultConfig {
// 생략
minSdkVersion 20
}
}
compileSdk는 앱을 빌드할 때 사용할 SDK 버전이다.
minSdkVersion은 안드로이드 운영체제의 최소 SDK 버전을 설정할 수 있는 위치이다.
웹뷰를 사용하기 위해서는 최소 20이상으로 설정해야 한다.
ios/Runner/Info.plist파일은 IOS의 런타임을 설정하는 파일이다. 권한 요청을 할 때 보여줄 메시지를 정의할때 사용하기도 한다.
<key>NSAppleMusicUsageDescription</key>
<string>음악을 재생하는 권한이 필요합니다.</string>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 생략 -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
</dict>
</plist>
키값 | 설명 |
---|---|
NSCalendarsUsageDescription | 달력 사용 권한 메시지 |
NSCameraUsageDescription | 카메라 사용 권한 메시지 |
NSContactsUsageDescription | 연락처 사용 권한 메시지 |
NSLocationUsageDescription | 위치 정보 사용 권한 메시지 |
NSPhotoLibraryUsageDescription | 사진 접근 권한 메시지 |
NSFacelDUsageDescription | FaceID 사용 권한 메시지 |
NSMicrophoneUsageDescription | 마이크 사용 권한 메시지 |
NSMotionUsageDescription | Accelerometer 사용 권한 메시지 |
NSSiriUsageDescription | siri 사용 권한 메시지 |
폴더와 파일 정리가 중요하기 때문에 화면관 관련된 모든 위젯을 screen 폴더에 모아서 관린한다.
// lib/screen/home_screen.dart
import "package:flutter/material.dart";
class HomeScreen extends StatelessWidget {
// const 생성자
const HomeScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return (Scaffold(
body: Text("Home Screen"),
));
}
}
import 'package:blog_web_app/screen/home_screen.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HomeScreen(),
));
}
💡Tips: import할때 경로 “package:[프로젝트 이름]/[lib 폴더로부터의 위치]/파일명.dart” 단 private로 선언된 속성들은 불러오기에 제외된다.
앱바와 웹뷰로 이루어져 있음
import "package:flutter/material.dart";
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return (Scaffold(
appBar: AppBar(
backgroundColor: Colors.orange,
title: Text(
"Code Factory",
style: TextStyle(color: Colors.white),
),
centerTitle: true,
),
body: Text("Home Screen"),
));
}
}
책에서는 WebView() 사용해서 처리했지만 webview_flutter 버전이 많이 변경됨에 따라 아래 코드처럼 구현하면 정상적으로 보임을 확인할 수 있다.
나는 webview_flutter 4.4.2버전을 설치해 적용했다.
import "package:flutter/material.dart";
import "package:webview_flutter/webview_flutter.dart";
class HomeScreen extends StatelessWidget {
// const 생성자
const HomeScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return (Scaffold(
appBar: AppBar(
backgroundColor: Colors.orange,
title: Text(
"Code Factory",
style: TextStyle(color: Colors.white),
),
centerTitle: true,
),
body: WebViewWidget(controller: controller)));
}
}
WebViewController controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(NavigationDelegate(
onProgress: (int progress) {
// Update loading Bar
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
}))
..loadRequest(Uri.parse("https://blog.codefactory.ai/"));
홈 아이콘을 눌렀을 때 웹뷰 화면을 변경하려면 위젯 제어 기능이 필요하다. 플러터는 위젯을 코드로 제어하는 기능을 제공한다.
import "package:flutter/material.dart";
import "package:webview_flutter/webview_flutter.dart";
class HomeScreen extends StatelessWidget {
// const 생성자
HomeScreen({Key? key}) : super(key: key);
WebViewController controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(NavigationDelegate(
onProgress: (int progress) {
// Update loading Bar
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
}))
..loadRequest(Uri.parse("https://blog.codefactory.ai/"));
Widget build(BuildContext context) {
return (Scaffold(
appBar: AppBar(
backgroundColor: Colors.orange,
title: Text(
"Code Factory",
style: TextStyle(color: Colors.white),
),
centerTitle: true,
actions: [
IconButton(
onPressed: () {
controller.goBack();
},
icon: Icon(
Icons.arrow_back,
color: Colors.white,
)),
IconButton(
onPressed: () {
if (controller != null) {
controller!.loadRequest(
Uri.parse("https://blog.codefactory.ai/"));
}
},
icon: Icon(
Icons.home,
color: Colors.white,
)),
IconButton(
onPressed: () {
controller.goForward();
},
icon: Icon(
Icons.arrow_forward,
color: Colors.white,
)),
]),
body: WebViewWidget(controller: controller)));
}
}
💡Tips: 현재 flutter_webview 4.0부터 Webview사용은 더 이상 사용하지 않는다고 한다. 따라서 WebviewController를 만들고 WebviewWidget 매개변수에 넣어줘야한다.