LoginSignup
2
2

More than 1 year has passed since last update.

FlutterでQiitaのアプリを作ってみる その4

Last updated at Posted at 2023-03-07

前回まではこちら

repository:

前回の続きで記事をWebViewに表示させるところからやります
pluginのwebview_flutterというものがあり、こちらを導入します。

またこちらはcodelabもあったのですが, 3.x系の内容で現在の4.x系ではなかったのでREADMEを読みながら導入していきたいと思います

webview_flutter

まずは以下コマンドで追加

flutter pub add webview_flutter

追加したらios/androidのMinimumOSVersion/minSdkVersionを確認する
webview_flutterの現在の最新(4.0.6)ではiosは11以上、androidは19以上が必須になっているので必要であれば更新する
まずはios
該当箇所はios/Flutter/AppFrameworkInfo.plistの中のMinimumOSVersion
このアプリの場合は11.0になっていたので特に変更なくこのまま
続いてandroid
該当箇所はandroid/app/build.gradleminSdkVersion
flutter.minSdkVersionとなっている。具体的な値は何になっているのだろうか
local.propertiesの中身を確認しても定義されていない。分からなかったので無難に20に変更
flutterのsdk側で定義されてるのかな?
とりあえずこれで使えるようになったはず

README通りcontrollerを定義してWebViewWidgetに渡す
以下の感じになった

class ArticleWebView extends StatelessWidget {
  const ArticleWebView({super.key});

  static const routeName = '/article';

  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as ArticleArguments;
    final controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse(args.url));
    return Scaffold(
      appBar: AppBar(
        title: Text('article'),
      ),
      body: WebViewWidget(controller: controller),
    );
  }
}

必要最小限だとこんな感じですかね

表示は出来ましたが、もう少しwebviewを触ってみます
表示までに読み込みが結構かかります。progressを出したい
CircularProgressIndicatorLinearProgressIndicatorなどが検索すると出てきました
WebViewなのでLinearProgressIndicatorの方がっぽい気がするのでこちらを採用します
webviewのREADMEにあったonProgressが使えそうなので試してみる
controllerに追加してどのような値が入ってくるのか確認します

..setNavigationDelegate(NavigationDelegate(
        onProgress: (int progress) {
          print(progress);
        },
      ))

実行しwebviewを開くと以下のログが

flutter: 10
flutter: 30
flutter: 48
flutter: 89
flutter: 90
flutter: 90
flutter: 90
flutter: 90
flutter: 90
flutter: 90
flutter: 100

やはりLoadingの進捗度合いが取れそうなのでこれを使う
またこのwebviewを使ってるwidgetがstatelessなのでstatefulに変更する

class ArticleWebView extends StatefulWidget {
  const ArticleWebView({super.key});
  static const routeName = '/article';

  @override
  State<ArticleWebView> createState() => _ArticleWebView();
}

class _ArticleWebView extends State<ArticleWebView> {
  int _progress = 0;

  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as ArticleArguments;
    final controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(NavigationDelegate(
        onProgress: (int progress) {
          print(progress);
          _progress = progress;
        },
        onPageStarted: (url) {
          print('start');
        },
        onPageFinished: (url) {
          print('finish');
        },
      ))
      ..loadRequest(Uri.parse(args.url));

    return Scaffold(
        appBar: AppBar(
          title: Text('article'),
        ),
        body: Column(
          children: [
            LinearProgressIndicator(value: _progress.toDouble()),
            Expanded(child: WebViewWidget(controller: controller)),
          ],
        ));
  }
}

これで実行してみる
あれ?終わらない...

flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: start
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: start
flutter: 10
flutter: 10
flutter: start
flutter: 10
flutter: 10
flutter: 30
flutter: 30
flutter: 10
flutter: 10
flutter: 10
flutter: 10
flutter: 30
flutter: 10
flutter: 10
flutter: 10
flutter: start
flutter: 10
flutter: 10
flutter: start
flutter: 10
flutter: 10
flutter: start
flutter: start
flutter: 10
flutter: 10
flutter: start
flutter: start
flutter: start
flutter: 10
flutter: start
flutter: 10
flutter: 46
flutter: 86
flutter: 10
...

buildの中でcontrollerを定義しているから_progressの値が変わる度にbuildが呼ばれてcontrollerが定義され直して?終わらない感じっぽい
困った。urlは引数として前の画面から受け取らないとなのでbuildの中で定義してたのだが分割しよう
試行錯誤で出来たコードが以下

class ArticleWebView extends StatefulWidget {
  const ArticleWebView({super.key});
  static const routeName = '/article';

  @override
  State<ArticleWebView> createState() => _ArticleWebView();
}

class _ArticleWebView extends State<ArticleWebView> {
  double _progress = 0.0;
  bool _isFirst = true;
  bool _isLoading = false;
  final controller = WebViewController()
    ..setJavaScriptMode(JavaScriptMode.unrestricted);

  @override
  void initState() {
    super.initState();
    controller.setNavigationDelegate(NavigationDelegate(
      onPageStarted: (url) {
        if (mounted) {
          setState(() {
            _isLoading = true;
          });
        }
      },
      onPageFinished: (url) {
        if (mounted) {
          setState(() {
            _isLoading = false;
          });
        }
      },
      onProgress: (progress) {
        if (mounted) {
          setState(() {
            _progress = progress / 100;
          });
        }
      },
    ));
  }

  @override
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments as ArticleArguments;
    if (_isFirst) {
      _isFirst = false;
      controller.loadRequest(Uri.parse(args.url));
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('article'),
      ),
      body: Column(
        children: [
          _isLoading
              ? LinearProgressIndicator(value: _progress)
              : const SizedBox.shrink(),
          Expanded(child: WebViewWidget(controller: controller)),
        ],
      ),
    );
  }
}

ほ、本当に?_isFirstとかいつの時代だよとPRに書かれそう
これどうするのが正解なのか有識者の方教えてください。お願いします:bow:

まあでもとりあえず動いたから良し

なんか軽い気持ちでProgress出したかったのに意外と大変だった。多分知らないだけなんだろうけど。
今回はここまで。後何をやろう...ページング対応してないか
次回はページングやろうと思います

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