Chapter 23

  記事表示画面の実装

heyhey1028
heyhey1028
2023.02.23に更新

さて最後は検索結果として記事の内容を表示する画面とその画面に遷移する処理を実装していきます。

記事表示画面を作成

article_screen.dartを作成

まず記事を表示するページとしてscreensディレクトリ内にarticle_screen.dartを作成しましょう。

AppBar などを備えたページにする為、Scaffoldを使います。

lib/screens/article_screen.dart
import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Article Page'),
      ),
      body:null,
    );
  }
}

webview_flutterを導入

次にハンズオンの最初で解説した通り、今回は記事の内容をWebViewを使って表示するので、webview_flutterというパッケージを導入します。

$ flutter pub add webview_flutter:'^3.0.4'

インストールしたらarticle_screen.dartで使うためimportし、bodyWebViewwidget を配置します

article_screen.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart'; // webview_flutterをimport

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Article Page'),
      ),
+      body: WebView(), // WebViewを配置
    );
  }
}

WebViewwidget には initialUrl というパラメータがあり、このパラメータに記事の url を渡すことで記事の内容を表示することができます。

Articleインスタンスを受け取り、WebViewに渡す

記事の url はArticleインスタンスに格納されているので、ArticleScreenが引数にArticleインスタンスを受け取るようにしましょう。

最後にWebViewに対して読み込む記事の url を渡してあげましょう。

article_screen.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:qiita_search/models/article.dart';

class ArticleScreen extends StatelessWidget {
  const ArticleScreen({
    required this.article, // 引き数にArticleインスタンスを受け取る
    super.key,
  });

  final Article article;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Article Page'),
      ),
      body: WebView(
+        initialUrl: article.url, // 記事のurlを渡す
      ),
    );
  }
}

これで画面は完成です!

画面遷移を実装

次は上記で作成したArticleScreenに遷移する処理を実装していきます。

画面遷移は、記事検索画面の検索結果一覧に表示された記事をタップすることで画面遷移するように実装したいと思います。

その為、先に作成したArticleContainerクラスに画面遷移の処理を追加します。

ArticleContainer
article_container.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:qiita_search/models/article.dart';
import 'package:qiita_search/screens/article_screen.dart';

class ArticleContainer extends StatelessWidget {
  const ArticleContainer({
    super.key,
    required this.article,
  });

  final Article article;

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(
        horizontal: 16,
        vertical: 12,
      ),
      child: GestureDetector(
        onTap: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: ((context) => ArticleScreen(article: article)),
            ),
          );
        },
        child: Container(
          height: 180,
          padding: const EdgeInsets.symmetric(
            horizontal: 20,
            vertical: 16,
          ),
          decoration: const BoxDecoration(
            color: Color(0xFF55C500),
            borderRadius: BorderRadius.all(
              Radius.circular(32),
            ),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                DateFormat('yyyy/MM/dd').format(article.createdAt),
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 12,
                ),
              ),
              Text(
                article.title,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
              Text(
                '#${article.tags.join(' #')}',
                style: const TextStyle(
                  fontSize: 12,
                  color: Colors.white,
                  fontStyle: FontStyle.italic,
                ),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Column(
                    children: [
                      const Icon(
                        Icons.favorite,
                        color: Colors.white,
                      ),
                      Text(
                        article.likesCount.toString(),
                        style: const TextStyle(
                          fontSize: 12,
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      CircleAvatar(
                        radius: 26,
                        backgroundImage: NetworkImage(article.user.iconUrl),
                      ),
                      const SizedBox(height: 4),
                      Text(
                        article.user.id,
                        style: const TextStyle(
                          fontSize: 12,
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

GestureDetectorを使ってタップイベントを実装する

現状ただのContainerwidget である、ArticleContainerクラスにタップイベントを付与するためにはGestureDetectorという widget を使います。

GestureDetectorは、子要素に対しタップイベントを実装するための widget で、onTapというパラメータにタップされた際の処理を定義することが出来ます。

このGestureDetectorでタップ範囲となる Widget を囲います。今回は余白部分は除いてContainerwidget をタップ範囲にしたいので、PaddingContainerの間に挟みます。

article_container.dart
class ArticleContainer extends StatelessWidget {
  const ArticleContainer({
    super.key,
    required this.article,
  });

  final Article article;

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(
        horizontal: 16,
        vertical: 12,
      ),
+      child: GestureDetector( // GestureDetectorでContainerを囲う
+        onTap: () {},
        child: Container(
            ...
        ),
      ),
    );
  }
}

これでContainerをタップするとGestureDetectorに設定したonTapの処理が実行されるようになりました。

画面遷移処理を実装する

最後に画面遷移処理をGestureDetectoronTapに実装します。

NavigatorクラスにMaterialPageRouteを使って、ArticleScreenを遷移先として渡しましょう。

ArticleScreenは引数としてArticleインスタンスを受け取るので、このArticleContainerが持っているArticleインスタンスを渡してあげましょう。

article_container.dart
child: GestureDetector(
        onTap: () {
+        Navigator.of(context).push(
+            MaterialPageRoute(
+              builder: ((context) => ArticleScreen(article: article)),
+            ),
+          );
        },
        child: Container(
            ...
        ),
      ),

🎊🎊🎊🥳 完成!!!

お疲れ様です!これで全ての実装が終わりました!

実装が正しくされていれば、以下のようなサンプルアプリが完成しているはずです。

うまくいかないという方は以下のレポジトリに完成したソースコードを挙げていますので、参考にしてみてください。

https://github.com/heyhey1028/flutter_expansion/tree/main/qiita_search