Android 세팅
android > app > src > main > AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.webview_model_flutter">
/// 이거 추가하기
<uses-permission android:name="android.permission.INTERNET"/>
/// 이거 추가하기
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
<application
android:usesCleartextTraffic="true"
android:label="webview_model_flutter"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
/// 이거 추가하기
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
/// 이거 추가하기
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.webview_model_flutter.flutter_inappwebview.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
iOS 세팅
ios > Runner > info.plist
추가하기
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
코드
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class WebviewScreen extends StatefulWidget {
WebviewScreen({Key? key}) : super(key: key);
@override
State<WebviewScreen> createState() => _WebviewScreenState();
}
class _WebviewScreenState extends State<WebviewScreen> {
InAppWebViewController? webViewController;
bool isLoading = true;
final InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
cacheEnabled: true,
clearCache: true,
transparentBackground: true,
useShouldOverrideUrlLoading: true,
javaScriptEnabled: true,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
mixedContentMode: AndroidMixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
),
ios: IOSInAppWebViewOptions(
useOnNavigationResponse: true,
scrollsToTop: false,
allowsInlineMediaPlayback: true,
),
);
@override
Widget build(BuildContext context) {
return SafeArea(
top: true,
bottom: true,
child: Scaffold(
body: Stack(
children: [
InAppWebView(
initialUrlRequest: URLRequest(iosHttpShouldHandleCookies: true, url: Uri.parse('https://inappwebview.dev/docs/debugging-webviews')),
initialOptions: options,
onWebViewCreated: (controller) async {
webViewController = controller;
webViewController?.addJavaScriptHandler(
handlerName: 'handleUrlChange',
callback: (input) async {
final nextUrl = input.first as String;
debugPrint('웹뷰 [addJavaScriptHandler]: $nextUrl');
},
);
},
onLoadStop: (contoller, url) {
debugPrint('웹뷰 [onLoadStop]: $url');
setState(() {
isLoading = false;
});
},
onUpdateVisitedHistory: (controller, url, check) {
debugPrint('웹뷰 [onUpdateVisitedHistory]: $url');
},
shouldOverrideUrlLoading: (controller, action) async {
final url = action.request.url;
debugPrint('웹뷰 [shouldOverrideUrlLoading]: $url');
return NavigationActionPolicy.ALLOW;
},
),
if (isLoading) const Center(child: RepaintBoundary(child: CircularProgressIndicator())),
],
),
),
);
}
}
새로운 코드
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
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();
Uri myUrl = Uri.parse("https://velog.io/@jhkim0122");
late final InAppWebViewController webViewController;
late final PullToRefreshController pullToRefreshController;
double progress = 0;
@override
void initState() {
super.initState();
pullToRefreshController = (kIsWeb
? null
: PullToRefreshController(
options: PullToRefreshOptions(color: Colors.blue,),
onRefresh: () async {
if (defaultTargetPlatform == TargetPlatform.android) {
webViewController.reload();
} else if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {
webViewController.loadUrl(urlRequest: URLRequest(url: await webViewController.getUrl()));}
},
))!;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: WillPopScope(
onWillPop: () => _goBack(context),
child: Column(children: <Widget>[
progress < 1.0
? LinearProgressIndicator(value: progress, color: Colors.blue)
: Container(),
Expanded(
child: Stack(children: [
InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(url: myUrl),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptCanOpenWindowsAutomatically: true,
javaScriptEnabled: true,
useOnDownloadStart: true,
useOnLoadResource: true,
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: true,
allowFileAccessFromFileURLs: true,
allowUniversalAccessFromFileURLs: true,
verticalScrollBarEnabled: true,
userAgent: 'random',
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
allowContentAccess: true,
builtInZoomControls: true,
thirdPartyCookiesEnabled: true,
allowFileAccess: true,
supportMultipleWindows: true
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
allowsBackForwardNavigationGestures: true,
),
),
pullToRefreshController: pullToRefreshController,
onLoadStart: (InAppWebViewController controller, uri) {
setState(() {myUrl = uri!;});
},
onLoadStop: (InAppWebViewController controller, uri) {
setState(() {myUrl = uri!;});
},
onProgressChanged: (controller, progress) {
if (progress == 100) {pullToRefreshController.endRefreshing();}
setState(() {this.progress = progress / 100;});
},
androidOnPermissionRequest: (controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT);
},
onWebViewCreated: (InAppWebViewController controller) {
webViewController = controller;
},
onCreateWindow: (controller, createWindowRequest) async{
showDialog(
context: context, builder: (context) {
return AlertDialog(
content: SizedBox(
width: MediaQuery.of(context).size.width,
height: 400,
child: InAppWebView(
// Setting the windowId property is important here!
windowId: createWindowRequest.windowId,
initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions(
builtInZoomControls: true,
thirdPartyCookiesEnabled: true,
),
crossPlatform: InAppWebViewOptions(
cacheEnabled: true,
javaScriptEnabled: true,
userAgent: 'random',
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
allowsBackForwardNavigationGestures: true,
),
),
onCloseWindow: (controller) async{
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
},
),
),);
},
);
return true;
},
)
]))
])
)
)
);
}
Future<bool> _goBack(BuildContext context) async{
if(await webViewController.canGoBack()){
webViewController.goBack();
return Future.value(false);
}else{
return Future.value(true);
}
}
}