【Flutter/Dart】WebViewの実装方法!WebViewControllerの使い方

この記事からわかること
- Flutter/DartでWebViewの実装させる方法
- webview_flutterパッケージの使い方
- NavigationDelegateとは?
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Koala
- Xcode:16.0
- Flutter:3.29.2
- Dart:3.7.2
- webview_flutter:4.10.0
- Mac M1:Sonoma 14.6.1
FlutterでWebViewを実装する方法
FlutterでWebサイトをアプリ内から表示できるWebViewを実装する方法はいくつか存在します。
- webview_flutterパッケージ
- url_launcherパッケージ
どちらもアプリ内からWebサイトを表示することが可能ですが、webview_flutter
では表示しているWebViewのサイズやUIをコントロール可能ですが、url_launcher
では「In-App Browser」という仕組みでWebサイトが表示されるため、自動でフルスクリーンかつ周りの装飾も既存のものになります。

url_launcher
の実装は以下の記事を参考にしてください。
webview_flutterの使い方
webview_flutter
パッケージはデフォルトではこのパッケージを組み込まれていないのでflutter pub add webview_flutter
コマンドを実行して導入しておく必要があります。
$ flutter pub add webview_flutter
これでimport
文を追加することで機能が利用できるようになります。
import 'package:webview_flutter/webview_flutter.dart';
プラットフォーム固有のWebViewの持つ機能を利用したい場合はそれぞて別パッケージになっているので別途導入する必要があります。
// Android : webview_flutter_android
import 'package:webview_flutter_android/webview_flutter_android.dart';
// iOS/macOS : webview_flutter_wkwebview
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
WebView Widgetの実装
実際にWebViewを実装してみます。WebViewScreen
という外部から呼び出して使えるカスタムWebView Widgetを実装してみました。「GitHub」に掲載しているので参考にしてください。
/// WebView
class WebViewScreen extends StatefulWidget {
// 対象URL
final String url;
const WebViewScreen({super.key, required this.url});
@override
State<WebViewScreen> createState() => _WebViewScreenState();
}
class _WebViewScreenState extends State<WebViewScreen> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
_controller =
WebViewController()
// JavaScript有効にする
..setJavaScriptMode(JavaScriptMode.unrestricted)
// 初期描画対象のURLを指定
..loadRequest(Uri.parse(widget.url));
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
// WebView表示エリア
child: SafeArea(child: WebViewWidget(controller: _controller)),
);
}
}

ここからは細かく実装のポイントを確認していきます。
WebViewController
WebViewController
はWebViewの設定や操作を行うためのコントローラークラスです。以下の実装ではinitState
でWebViewController
を初期化し設定を行なっています。loadRequest
メソッドで表示したいURLを指定します。
@override
void initState() {
super.initState();
_controller =
WebViewController()
// JavaScript有効にする
..setJavaScriptMode(JavaScriptMode.unrestricted)
// 初期描画対象のURLを指定
..loadRequest(Uri.parse(widget.url));
}
JavaScriptの有効/無効はsetJavaScriptMode
で指定します。JavaScriptMode.unrestricted
で制限なし(=有効)、JavaScriptMode.disabled
で無効にできます。
WebViewWidget
実際にWebView UIを構築しているのはWebViewWidget
です。これは特に変わったことはなく引数にWebViewController
を指定することでWebViewが表示されるようになります。
// WebView表示エリア
child: SafeArea(child: WebViewWidget(controller: _controller)),
進む / 戻る / リロード
WebViewでWebページを表示中の「進む / 戻る / リロード」などの操作もWebViewController
から行うことが可能です。「進む / 戻る」は呼び出す際にawait
が必要になります。
進む
await _controller.goForward();
戻る
await _controller.goBack();
戻る
_controller.reload();
さらに「進む / 戻る」はcanGoForward
/canGoBack
メソッドを使用して有効化どうかを識別できます。これをsetNavigationDelegate
に仕込んでおくことでページ読み込み完了後に進む / 戻るボタンの活性状態を更新するといった仕組みを実装することも可能です。
bool canGoBack = false;
bool canGoForward = false;
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse(widget.url))
..setNavigationDelegate(
NavigationDelegate(
// ページ読み込み完了後に進む / 戻るボタンの状態を更新
onPageFinished: (_) => _updateNavigationState(),
),
);
}
/// 進む / 戻るボタンの状態を更新
Future<void> _updateNavigationState() async {
final back = await _controller.canGoBack();
final forward = await _controller.canGoForward();
setState(() {
canGoBack = back;
canGoForward = forward;
});
}
NavigationDelegate
NavigationDelegate
を使用することで以下のようなことが実装できるようになります。
- Webページの遷移を許可 / ブロック
- 特定のURLのときにカスタム処理(外部ブラウザで開くなど)を実行する
使用方法はWebViewController
の初期セットアップ時にsetNavigationDelegate
メソッドの引数にNavigationDelegate
型を指定します。例えば特定のドメインだけブロックしたい場合はonNavigationRequest
からNavigationRequest
型でリクエスト情報を参照できるのでULRをチェックし遷移をブロックしたい場合はNavigationDecision.prevent
を、遷移を許可したい場合はNavigationDecision.navigate
を返せばハンドリングできます。
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse(widget.url));
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (NavigationRequest request) {
if (request.url.contains("appdev-room.com")) {
// 特定のURLへの遷移をブロック
return NavigationDecision.prevent;
}
// 他のURLは許可
return NavigationDecision.navigate;
},
),
)
}
定義を確認してみると他にもいろいろなイベントを取得することが可能です。
NavigationDelegate({
// ナビゲーション制御
FutureOr<NavigationDecision> Function(NavigationRequest request)? onNavigationRequest,
// ページ読み込み開始
void Function(String url)? onPageStarted,
// ページ読み込み完了
void Function(String url)? onPageFinished,
// 読み込み進捗
void Function(int progress)? onProgress,
// リソースエラー
void Function(WebResourceError error)? onWebResourceError,
// URL変更時の処理
void Function(UrlChange change)? onUrlChange,
// HTTP認証要求
void Function(HttpAuthRequest request)? onHttpAuthRequest,
// HTTPエラー発生時
void Function(HttpResponseError error)? onHttpError,
})
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。