Skip to content

Commit

Permalink
Merge pull request #130 from FltSv/105-book-screen
Browse files Browse the repository at this point in the history
#105 書籍一覧ページと書籍詳細ページの内部実装、ダミーデータの作成
  • Loading branch information
FltSv authored Aug 25, 2024
2 parents b81e457 + 89e5a9a commit a51e7ac
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 0 deletions.
39 changes: 39 additions & 0 deletions mobile/lib/infra/fake/fake_repo.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:mobile/models/book.dart';
import 'package:mobile/models/creator.dart';
import 'package:mobile/models/exhibit.dart';
import 'package:mobile/models/product.dart';
Expand Down Expand Up @@ -90,4 +91,42 @@ class FakeRepo implements DataRepoBase {
"firebasestorage.googleapis.com/v0/b/gallery-found.appspot.com";
return "https://$domain/o/creators%2F$userId%2F$image";
}

static Future<List<Book>> fetchBooks() {
return Future.value([
Book(
id: '1',
title: 'ひまわり',
image:
'https://media.mstdn.jp/accounts/avatars/110/989/102/133/377/139/original/ef63d785a819a2f2.png',
urls: ['https://mstdn.jp/@himarori']),
Book(
id: '2',
title: '砂ちゃん',
image:
'https://media.mstdn.jp/accounts/avatars/000/113/775/original/4a94c289c389d678.jpg',
urls: ['https://mstdn.jp/@fltsv', 'https://mstdn.jp/@himarori']),
Book(
id: '3',
title: 'よるねこ',
image:
'https://media.mstdn.jp/accounts/avatars/000/155/200/original/2e948193ee954e55428290ad6ecada7f.png',
urls: [
'https://mstdn.jp/@NightCat',
'https://mstdn.jp/@himarori',
'https://mstdn.jp/@fltsv'
]),
Book(
id: '4',
title: 'もやちゃ',
image:
'https://media.mstdn.jp/accounts/avatars/109/719/600/512/825/943/original/898331de566f6f5e.png',
urls: [
'https://mstdn.jp/@kisskamakiri',
'https://mstdn.jp/@fltsv',
'https://mstdn.jp/@NightCat',
'https://mstdn.jp/@himarori'
])
]);
}
}
26 changes: 26 additions & 0 deletions mobile/lib/models/book.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:mobile/models/creator.dart';
import 'package:mobile/providers/data_provider.dart';

/// 書籍
class Book {
Book({
required this.id,
required this.title,
required this.image,
required this.urls,
});

final String id;

/// 書籍のタイトル
final String title;

/// 書籍のサムネイル画像のファイル名+トークン
final String image;
String get imageUrl => DataProvider().getImageUrl(creator.id, image);

/// 書籍購入のリンク 複数ある場合も可
final List<String> urls;

late final Creator creator;
}
6 changes: 6 additions & 0 deletions mobile/lib/providers/data_provider.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:mobile/infra/factory.dart';
import 'package:mobile/infra/fake/fake_repo.dart';
import 'package:mobile/models/book.dart';
import 'package:mobile/models/creator.dart';

/// データの取得と保持を行う
Expand All @@ -11,6 +13,9 @@ class DataProvider {
late List<Creator> _creators;
List<Creator> get creators => _creators;

late List<Book> _books;
List<Book> get books => _books;

late String Function(String userId, String image) _getImageUrl;
String getImageUrl(String userId, String image) =>
_getImageUrl(userId, image);
Expand All @@ -21,5 +26,6 @@ class DataProvider {

_getImageUrl = repo.getImageUrl;
_creators = await repo.fetchCreators();
_books = await FakeRepo.fetchBooks();
}
}
44 changes: 44 additions & 0 deletions mobile/lib/screens/book_detail_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:intersperse/intersperse.dart';
import 'package:mobile/models/book.dart';
import 'package:mobile/widgets/link_text.dart';
import 'package:url_launcher/url_launcher.dart';

class BookDetailScreen extends StatefulWidget {
const BookDetailScreen({
super.key,
required this.book,
});

final Book book;

@override
State<BookDetailScreen> createState() => _BookDetailScreenState();
}

class _BookDetailScreenState extends State<BookDetailScreen> {
@override
Widget build(BuildContext context) {
final book = widget.book;

return Scaffold(
appBar: AppBar(
title: Text(book.title),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
Image.network(book.image),
const Gap(8),
...book.urls.map((urltext) => LinkText(
text: urltext,
onTap: () async {
final url = Uri.parse(urltext);
await launchUrl(url, mode: LaunchMode.externalApplication);
}))
].intersperse(const Gap(8)).toList(),
),
);
}
}
67 changes: 67 additions & 0 deletions mobile/lib/screens/book_list_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:mobile/models/book.dart';
import 'package:mobile/providers/data_provider.dart';
import 'package:mobile/providers/navigate_provider.dart';
import 'package:mobile/screens/book_detail_screen.dart';
import 'package:mobile/widgets/empty_state.dart';

class BookListScreen extends StatefulWidget {
const BookListScreen({super.key});

@override
State<BookListScreen> createState() => _BookListScreenState();
}

class _BookListScreenState extends State<BookListScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('書籍一覧'),
),
body: GridView.count(
padding: const EdgeInsets.all(16),
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
physics: const NeverScrollableScrollPhysics(), // GridViewのスクロールを無効化
shrinkWrap: true, // GridViewの高さをコンテンツに合わせる
children: _getResults(),
),
);
}

List<Widget> _getResults() {
final results = DataProvider()
.books
.map((book) => BookItem(book: book))
.map<Widget>((item) => GestureDetector(
onTap: () => NavigateProvider.push(
context, BookDetailScreen(book: item.book)),
child: item,
))
.toList();

return results.isEmpty
? [
const Gap(16),
const EmptyState(message: '表示可能な書籍が見つかりませんでした。'),
]
: results;
}
}

class BookItem extends StatelessWidget {
const BookItem({
super.key,
required this.book,
});

final Book book;

@override
Widget build(BuildContext context) {
return Image.network(book.image);
}
}

0 comments on commit a51e7ac

Please sign in to comment.