はじめに
以前紹介したFlutterでWebViewが簡単に実装できるwebview_flutterですが、webview内のリンクから外部ブラウザを起動したいなぁと思い、今回調べてみました。
今回紹介するやりかたは特定のリンクのみWebViewからurl_launcherを使って外部ブラウザで開くといった感じです。
環境
- Flutter 2.0.3
- webview_flutter 2.0.2
- url_launcher 6.0.3
準備
プラグインの準備
プラグインの反映方法は過去の記事を参考にしてください。
WebViewから外部ブラウザ開く方法
サンプルコード
下記のサンプルコードはURLのホストが「flutter.dev」だった場合は外部ブラウザで起動し、それ以外はアプリ内WebViewで遷移します。
main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({required this.title});
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final initialUrl = 'https://www.google.com/';
final urlBlackList = <String>[
'flutter.dev',
];
WebViewController? webViewController;
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () async {
webViewController?.loadUrl(initialUrl);
},
),
],
),
body: WebView(
initialUrl: initialUrl,
onWebViewCreated: (controller) {
webViewController = controller;
},
navigationDelegate: (request) async {
if ((!request.isForMainFrame) || (initialUrl == request.url)) {
return NavigationDecision.navigate;
}
final uri = Uri.parse(request.url);
if (urlBlackList.indexOf(uri.host) >= 0) {
if (await canLaunch(request.url)) {
await launch(
request.url,
forceSafariVC: false,
);
}
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
);
}
}
結果
初期のGoogle画面から
Flutterのwikiの場合はWebView内で画面遷移
ホストが「flutter.dev」の場合は外部Webブラウザでリンクを開きます
WebViewから外部Webブラウザで開くために
WebView内でリンクが押された場合、navigationDelegateのデリゲートが呼ばれます。
ここに外部ウェブブラウザで開く処理を実装することで機能をを実現しています。
またこのデリゲード内でNavigationDecision.preventを返すと遷移せず何も起きなくなります。
NavigationDecision.navigateを返すと通常通り遷移します。
WebView(
...
navigationDelegate(NavigationRequest request) {
// 何も起きない.
return NavigationDecision.prevent;
// 通常動作.
return NavigationDecision.navigate;
}
...
),
遷移予定のURLがNavigationRequestのurlパラメーターで取得することができますが、iframeのURLやCSSのlink relの要素などもnavigationDelegateの処理に入ってきます。
それを判別するためにNavigationRequest.isForMainFrameのパラメーターを利用します。
if ((!request.isForMainFrame) || (initialUrl == request.url)) {
return NavigationDecision.navigate;
}
また初期URLも無条件でこの判定に入ってしまうので、念の為追加しています。
最後はホストがブラックリストに存在するかどうかを判定し、外部ブラウザで開いています。
このときWebViewないで遷移させないためにNavigationDecision.preventを返しています。
final uri = Uri.parse(request.url);
if (urlBlackList.indexOf(uri.host) >= 0) {
if (await canLaunch(request.url)) {
await launch(
request.url,
forceSafariVC: false,
);
}
return NavigationDecision.prevent;
}
さいごに
意外と簡単だったので、ぜひ使ってみてください。
コメント