# はじめに
使用するパッケージはwebview_flutterです。
SNS埋め込みとかでよく使うWebViewですが、
Columnの中に入れるとA RenderFlex overflowed by Infinity pixels on the bottom
でアプリが落ちます。
これ自体はExpanded
などを使えば解決します。
ただ、WebViewの中身をスクロールするのではなく、Flutter側でWidgetをまるっとスクロールさせたい、みたいな場面ってあると思います。
(今回の話は、その為にColumnに入れて使いんたいんじゃい!という話)
固定の高さを持たせて、かつそれをWebページの高さに合わせれば解決なのですが、パッケージ自体にはそんな機能はついておりません。
(Webページの高さを取得できるようにしたwebview_flutter_plusなどもありますが、今回は使いません。)
では、どうやって高さを取得するかというと、**runJavascriptReturningResult
**を使用します。
runJavascriptReturningResult
とは?
これは、WebViewController
のメソッドで、WebViewの中でJavascriptを実行する事ができます。
似たようなメソッドにrunJavascript
がありますが、こちらは戻り値が**Future<void>**なので今回の用途では使えません。
戻り値がFutureなので、awaitするのを忘れないようにしましょう。
以下サンプルです。
class WebViewTest extends StatefulWidget {
const WebViewTest({Key? key}) : super(key: key);
@override
State<WebViewTest> createState() => _WebViewTestState();
}
class _WebViewTestState extends State<WebViewTest> {
late WebViewController _webviewController;
String _webviewUrl = 'WebViewのURL';
double _height = 0; // WebViewの高さ
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
適当なWidget,
...
SizedBox(
height: _height, // 高さ指定(ここではまだ0)
child: WebView(
initialUrl: _webviewUrl,
onWebViewCreated: (controller) async {
_webviewController = controller; // コントローラ取得
},
// WebViewが読み込みが完了できたら
onPageFinished: (_) async {
double _contentHeight = double.parse(
// JSで高さを取得し、doubleにパース
await _webviewController
.runJavascriptReturningResult('document.documentElement.scrollHeight;'),
);
setState(() {
_height = _contentHeight; // WebViewの高さを更新
});
},
),
),
],
),
);
}
}
これでWebViewの読み込みが終わり、JSが実行できたら高さがWebページの高さに変わります。
注意点
WebページがJSで画面を構築するページの場合、 onPageFinished
のタイミングで画面構築が終わってない場合があります。
その場合、javascriptChannels
を使ってWebページ側から通知してもらうか、Future.delayed
でタイミングをずらしましょう。
(SNSの埋め込みとかだと、通知用のコードを埋め込んでもらうのとかは無理なのでFuture.delayed
を使うことになると思います。)
Future.delayed
の場合だと、Webページ側の読み込みによって指定する時間が大きく異なると思うので、
ストレスが掛からない程度に大きめに取っておくといいと思います。
(SizedBoxをAnimatedContainerに変えてアニメーションさせたり、AnimatedOpacityでフェードさせたりしてもいいかも)