さて最後は検索結果として記事の内容を表示する画面とその画面に遷移する処理を実装していきます。
記事表示画面を作成
article_screen.dart
を作成
まず記事を表示するページとしてscreens
ディレクトリ内にarticle_screen.dart
を作成しましょう。
AppBar などを備えたページにする為、Scaffold
を使います。
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
し、body
にWebView
widget を配置します
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を配置
);
}
}
WebView
widget には initialUrl
というパラメータがあり、このパラメータに記事の url を渡すことで記事の内容を表示することができます。
Article
インスタンスを受け取り、WebView
に渡す
記事の url はArticle
インスタンスに格納されているので、ArticleScreen
が引数にArticle
インスタンスを受け取るようにしましょう。
最後にWebView
に対して読み込む記事の url を渡してあげましょう。
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
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
を使ってタップイベントを実装する
現状ただのContainer
widget である、ArticleContainer
クラスにタップイベントを付与するためにはGestureDetector
という widget を使います。
GestureDetector
は、子要素に対しタップイベントを実装するための widget で、onTap
というパラメータにタップされた際の処理を定義することが出来ます。
このGestureDetector
でタップ範囲となる Widget を囲います。今回は余白部分は除いてContainer
widget をタップ範囲にしたいので、Padding
とContainer
の間に挟みます。
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
の処理が実行されるようになりました。
画面遷移処理を実装する
最後に画面遷移処理をGestureDetector
のonTap
に実装します。
Navigator
クラスにMaterialPageRoute
を使って、ArticleScreen
を遷移先として渡しましょう。
ArticleScreen
は引数としてArticle
インスタンスを受け取るので、このArticleContainer
が持っているArticle
インスタンスを渡してあげましょう。
child: GestureDetector(
onTap: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: ((context) => ArticleScreen(article: article)),
+ ),
+ );
},
child: Container(
...
),
),
🎊🎊🎊🥳 完成!!!
お疲れ様です!これで全ての実装が終わりました!
実装が正しくされていれば、以下のようなサンプルアプリが完成しているはずです。
うまくいかないという方は以下のレポジトリに完成したソースコードを挙げていますので、参考にしてみてください。