2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Flutter モーダルボトムシート(iOS風)でWebView表示

Posted at

はじめに

FlutteriOS風 モーダルボトムシート(モーダルが下から出て、後ろの画面が少し小さくなるあれ)でWebView表示を実装しました。

環境

  • Dart: 2.15.1
  • Flutter: 2.8.1

完成形

modl_bottom_sheet.gif

実装

WebViewの説明に関しては、以下の記事で説明しているのでご覧ください。

Package

modal_bottom_sheetを使用します。(名前そのまま)

pubspec.yaml
dependencies:
  modal_bottom_sheet: ^2.0.0
  webview_flutter: ^3.0.0

コード

今回の実装にあたって、主に3か所の追加修正が必要です。

ルート

homeのままだと画面が小さくならないため、ルーティングの修正をします。
home: ~~~,となっていたところを、
initialRoute: ~~~, onGenerateRoute: (RouteSettings settings) {~~~}
に変更します。

main.dart
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(const MyApp(),);
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: HomeScreen.routeName,
      onGenerateRoute: (RouteSettings settings) {
        switch (settings.name) {
          case HomeScreen.routeName:
            return MaterialWithModalsPageRoute(
                builder: (context) => const HomeScreen(),
                settings: settings,);
        }
      },
    );
  }
}

〜〜〜
続く
〜〜〜

モーダル

パッケージが用意してくれた、showCupertinoModalBottomSheetでiOS風 モーダルを呼び出します。

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  static const routeName = '/home';

  @override
  Widget build(BuildContext context) {

    void _showModalBottomSheet() {
      showCupertinoModalBottomSheet(
        expand: true,
        context: context,
        backgroundColor: Colors.transparent,
        builder: (context) => const WebViewScreen(),);
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          child: const Text('Press'),
          onPressed: () {
            _showModalBottomSheet();
          },
        ),
      ),
    );
  }
}

〜〜〜
続く
〜〜〜

WebView

そのまま表示しても、モーダルシート上だとスクロールが効かなかったため
EagerGestureRecognizer()をWebViewのgestureRecognizersに渡します。
ただWebView上のリンクで遷移した後、左 → 右のスワイプで戻れないです。
方法があれば教えて欲しいです!

class WebViewScreen extends StatefulWidget {
  const WebViewScreen({
    Key? key,
  }) : super(key: key);

  @override
  _WebViewScreenState createState() => _WebViewScreenState();
}

class _WebViewScreenState extends State<WebViewScreen> {
  final Set<Factory<OneSequenceGestureRecognizer>> _gestureRecognizers
      = {Factory(() => EagerGestureRecognizer())}; // タッチ操作用

  final _key = UniqueKey();
  final Completer<WebViewController> _controller = Completer<WebViewController>();
  bool _isLoading = true; // ローディングの有無
  String _title = ''; // Webタイトル
  double _downloadPercentage = 0.0; // 進捗バー用

  @override
  void initState() {
    super.initState();
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  // ローディングの切り替え
  void toggleLoading() {
    setState(() {
      _isLoading = !_isLoading;
    });
  }

  // 進捗計算
  void calculateProgress(int progress) {
    setState(() {
      _downloadPercentage = (progress / 100);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_title),// Web画面のタイトルを表示
        automaticallyImplyLeading: false, // バックボタンの非表示
        actions: [
          IconButton(
            icon: const Icon(Icons.close),
            onPressed: () => Navigator.pop(context),
          ),
        ],
      ),
      body: Column(
        children: [
          // 進捗バー
          _isLoading ? LinearProgressIndicator(value: _downloadPercentage, minHeight: 7.0,) : const SizedBox.shrink(),
          Expanded(
            child: Scrollbar(
              showTrackOnHover: true,
              child: WebView(
                  gestureRecognizers: _gestureRecognizers, // タッチ操作の有効化
                  key: _key,
                  initialUrl: 'https://qiita.com/',
                  javascriptMode: JavascriptMode.unrestricted,
                  onWebViewCreated: _controller.complete,
                  onPageFinished: (String url) async {
                    if (_isLoading) toggleLoading();
                    final controller = await _controller.future;
                    final title = await controller.getTitle();
                    if (title != null) _title = title;
                  },
                  onProgress: (progress) {
                    calculateProgress(progress);
                  }
              ),
            ),
          ),
        ],
      ),
    );
  }
}

以上で動くはずです。

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?