はじめに
FlutterでiOS風 モーダルボトムシート(モーダルが下から出て、後ろの画面が少し小さくなるあれ)でWebView表示を実装しました。
環境
- Dart: 2.15.1
- Flutter: 2.8.1
完成形
実装
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);
}
),
),
),
],
),
);
}
}
以上で動くはずです。