0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】Webviewでガワアプリの画面を作る

Last updated at Posted at 2025-04-27

はじめに

アプリ内でWebページを表示させるWebview画面を作成します。
このようなWebview画面がメインになるアプリを「ガワアプリ」といいます。
低コストでアプリを作成することが目的の場合に選択されます。
すでにあるWebページの流用や多数のモバイルエンジニアの確保が不要になります。(ただネイティブアプリのようなリッチな動きはできない)

パッケージを追加

Webview画面を作るために以下を追加します。

// ステートレスな画面で変数の変化によって画面の表示を変えるためや、初期化処理のため
flutter pub add hooks_riverpod

// Webviewパッケージ
flutter pub add webview_flutter

// 外部ブラウザを開くためのパッケージ
flutter pub add url_launcher

画面を作成

Statelessな画面でWebivew画面を表示させています。
webviewで使用するコントローラーの初期化が終わったら、Webivew画面を表示させます。

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';

/// QiitaのWebView画面
class QiitaWebViewScreen extends HookWidget {
  // 表示するURL
  final String url = 'https://qiita.com';

  // ホワイトリストに登録するドメイン
  final whiteDomainList = ['qiita.com'];

  @override
  Widget build(BuildContext context) {
    final isLoadingPage = useState(false);
    final webViewController = useState<WebViewController?>(null);

    // 初期化処理
    useEffect(() {
      Future<void> initWebView() async {
        final controller =
            WebViewController()
              ..setJavaScriptMode(JavaScriptMode.unrestricted)
              ..setBackgroundColor(Colors.white)
              ..setNavigationDelegate(
                NavigationDelegate(
                  onPageStarted: (String url) {
                    isLoadingPage.value = true;
                  },
                  onPageFinished: (String url) {
                    isLoadingPage.value = false;
                  },
                  onWebResourceError: (WebResourceError error) {
                    isLoadingPage.value = false;
                  },
                  onNavigationRequest: (NavigationRequest request) async {
                    final uri = Uri.parse(request.url);
                    // ホワイトリストに登録されていないドメインは外部ブラウザで開く
                    if (whiteDomainList.contains(uri.host)) {
                      return NavigationDecision.navigate;
                    }
                    final isSuccess = await launchUrl(uri, mode: LaunchMode.externalApplication);
                    if (!isSuccess) {
                      // URLを開けなかった場合はトーストを表示
                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('URLを開けませんでした: $url')));
                    }
                    return NavigationDecision.prevent;
                  },
                ),
              )
              ..loadRequest(Uri.parse(url));
        webViewController.value = controller;
      }

      // WebViewを初期化
      initWebView();

      return null;
    }, []); // 初回のみ実行

    // コントローラーが初期化されていない場合はローディング中のインジケーターを表示
    final _webViewController = webViewController.value;
    if (_webViewController == null) return const Center(child: CircularProgressIndicator(color: Colors.greenAccent));

    return Scaffold(
      appBar: AppBar(
        title: const Text('Qiita'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () {
              // WebViewをリロードする
              _webViewController.reload();
            },
          ),
        ],
      ),
      body: Stack(
        children: [
          WebViewWidget(controller: _webViewController),
          if (isLoadingPage.value) const Center(child: CircularProgressIndicator(color: Colors.greenAccent)),
        ],
      ),
    );
  }
}

スクリーンショット 2025-04-28 8.47.49.png

おわりに

今回はWebview画面を実装しました。
画面のみの実装のためHookWidgetを使用して実装していますが、ViewModelも作成するためConsumerHookWidgetを使用するように変更していくことになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?