Skip to content

Commit

Permalink
Merge pull request #89 from seigi0714/feature/trip_detail_page
Browse files Browse the repository at this point in the history
旅詳細ページのUI実装
  • Loading branch information
seigi0714 committed Apr 29, 2023
2 parents f50e95b + 7316b43 commit 0a11746
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 33 deletions.
17 changes: 17 additions & 0 deletions lib/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:trip_app_nativeapp/core/exception/app_exception.dart';
import 'package:trip_app_nativeapp/features/user/controller/app_user_controller.dart';
import 'package:trip_app_nativeapp/view/pages/debug_page.dart';
import 'package:trip_app_nativeapp/view/pages/error_page.dart';
import 'package:trip_app_nativeapp/view/pages/loading_page.dart';
import 'package:trip_app_nativeapp/view/pages/login_page.dart';
import 'package:trip_app_nativeapp/view/pages/trips/trip_detail_page.dart';
import 'package:trip_app_nativeapp/view/pages/trips/trips_list_page.dart';

part 'router.g.dart';
Expand Down Expand Up @@ -59,6 +61,21 @@ GoRouter router(RouterRef ref) {
GoRoute(
path: TripListPage.path,
builder: (context, state) => const TripListPage(),
routes: [
GoRoute(
path: TripDetailPage.pathParam,
builder: (context, state) {
final id = int.tryParse(state.params['id'] ?? '');
if (id != null) {
return TripDetailPage(id);
} else {
return const ErrorPage(
exception: AppException(message: '旅の選択に失敗しました。'),
);
}
},
),
],
),
GoRoute(
path: LoginPage.path,
Expand Down
121 changes: 121 additions & 0 deletions lib/view/pages/trips/trip_detail_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:trip_app_nativeapp/core/exception/app_exception.dart';
import 'package:trip_app_nativeapp/core/extensions/build_context.dart';
import 'package:trip_app_nativeapp/features/trips/controller/trip_controller.dart';
import 'package:trip_app_nativeapp/view/pages/trips/trips_list_page.dart';
import 'package:trip_app_nativeapp/view/widgets/common/car_driving_loading.dart';
import 'package:trip_app_nativeapp/view/widgets/common/error_cat.dart';
import 'package:trip_app_nativeapp/view/widgets/trips/trip_belonging_list.dart';
import 'package:trip_app_nativeapp/view/widgets/trips/trip_overview_card.dart';
import 'package:trip_app_nativeapp/view/widgets/trips/trip_schedule.dart';

class TripDetailPage extends HookConsumerWidget {
const TripDetailPage(this.id, {super.key});

static String path({required int id}) => '${TripListPage.path}/$id';
static const pathParam = ':id';
static const scheduleTabIndex = 0;
final int id;

@override
Widget build(BuildContext context, WidgetRef ref) {
final tabIndex = useState(scheduleTabIndex);
final backgroundImageHeight = context.displaySize.width / 16 * 9;
return Scaffold(
body: SafeArea(
top: false,
child: ref.watch(tripsProvider).when(
data: (trips) {
final trip = trips.firstWhereOrNull((trip) => trip.id == id);
if (trip == null) {
return const Center(
child: ErrorCat(
AppException(
code: 'not_found',
message: '選択した旅の予定が見つかりませんでした。',
),
null,
),
);
}

return NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
// TODO(seigi0714): スクロールした場合のみ表示したい。
title: Text(
trip.title.value,
style: context.textTheme.titleLarge,
),
expandedHeight:
backgroundImageHeight + TripOverviewCard.height / 2,
flexibleSpace: FlexibleSpaceBar(
background: SizedBox(
height: backgroundImageHeight +
TripOverviewCard.height / 2,
child: Stack(
alignment: Alignment.topCenter,
children: [
SizedBox(
height: backgroundImageHeight,
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://tsunagutabi.com/wp-content/uploads/2020/04/%E6%97%85%E8%A1%8C%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AB%E3%81%8A%E3%81%99%E3%81%99%E3%82%81%E3%81%AE%E3%83%95%E3%83%AA%E3%83%BC%E7%94%BB%E5%83%8F%EF%BC%86%E7%B4%A0%E6%9D%90%E3%82%B5%E3%82%A4%E3%83%88%E3%81%BE%E3%81%A8%E3%82%81.jpg',
),
fit: BoxFit.cover,
),
),
),
),
Positioned(
top: backgroundImageHeight -
TripOverviewCard.height / 2,
child: TripOverviewCard(trip),
),
],
),
),
),
pinned: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: TabBar(
controller: TabController(
length: 2,
vsync: Scaffold.of(context),
initialIndex: tabIndex.value,
),
labelColor: Colors.black,
tabs: const [
Tab(
text: '🗓️日程',
),
Tab(
text: '🧳持ち物',
)
],
onTap: (i) => tabIndex.value = i,
),
),
),
];
},
body: tabIndex.value == scheduleTabIndex
? const TripSchedule()
: const TripBelongingList(),
);
},
error: ErrorCat.new,
loading: CarDrivingLoading.new,
),
),
);
}
}
12 changes: 12 additions & 0 deletions lib/view/widgets/trips/trip_belonging_list.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';

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

@override
Widget build(BuildContext context) {
return const Center(
child: Text('持ち物リスト'),
);
}
}
69 changes: 37 additions & 32 deletions lib/view/widgets/trips/trip_card.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:lottie/lottie.dart';
import 'package:trip_app_nativeapp/core/extensions/build_context.dart';
import 'package:trip_app_nativeapp/core/extensions/datetime.dart';
import 'package:trip_app_nativeapp/core/gen/assets.gen.dart';
import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip.dart';
import 'package:trip_app_nativeapp/view/pages/trips/trip_detail_page.dart';

class TripCard extends StatelessWidget {
const TripCard(this.trip, {super.key});
Expand All @@ -13,39 +15,42 @@ class TripCard extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min, // Columnの高さを最小限にする
children: [
Expanded(
child: Lottie.asset(
Assets.lotties.tripCard,
height: context.displaySize.height * 0.14,
return GestureDetector(
onTap: () => context.push(TripDetailPage.path(id: trip.id)),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min, // Columnの高さを最小限にする
children: [
Expanded(
child: Lottie.asset(
Assets.lotties.tripCard,
height: context.displaySize.height * 0.14,
),
),
Text(
trip.title.value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: context.textTheme.titleLarge,
),
const Gap(8),
Text(
'🛫 ${trip.period.fromDate.toJsonDateString()}',
style: context.textTheme.titleMedium,
),
const Gap(8),
Text(
'${trip.period.endDate.toJsonDateString()} 🔚',
style: context.textTheme.titleMedium,
),
),
Text(
trip.title.value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: context.textTheme.titleLarge,
),
const Gap(8),
Text(
'🛫 ${trip.period.fromDate.toJsonDateString()}',
style: context.textTheme.titleMedium,
),
const Gap(8),
Text(
'${trip.period.endDate.toJsonDateString()} 🔚',
style: context.textTheme.titleMedium,
),
],
],
),
),
),
);
Expand Down
89 changes: 89 additions & 0 deletions lib/view/widgets/trips/trip_overview_card.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:trip_app_nativeapp/core/extensions/build_context.dart';
import 'package:trip_app_nativeapp/core/extensions/datetime.dart';
import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip.dart';

class TripOverviewCard extends StatelessWidget {
const TripOverviewCard(this.trip, {super.key});

final ExistingTrip trip;
static const height = 150.0;

@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: height,
width: context.displaySize.width * 0.95,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
alignment: Alignment.centerLeft,
child: Text(
trip.title.value,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: context.textTheme.headlineMedium,
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'🛫 ${trip.period.fromDate.toJsonDateString()}',
style: context.textTheme.titleMedium,
),
Text(
'${trip.period.endDate.toJsonDateString()} 🔚',
style: context.textTheme.titleMedium,
),
],
),
),
SizedBox(
width: 36,
height: 36,
child: IconButton(
onPressed: () => log('share'),
icon: const Icon(
Icons.share,
),
),
),
SizedBox(
width: 36,
height: 36,
child: IconButton(
onPressed: () => log('edit'),
icon: const Icon(
Icons.edit,
),
),
),
],
),
],
),
),
),
),
);
}
}
12 changes: 12 additions & 0 deletions lib/view/widgets/trips/trip_schedule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';

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

@override
Widget build(BuildContext context) {
return const Center(
child: Text('スケジュール'),
);
}
}
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ packages:
source: hosted
version: "4.4.0"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ environment:
sdk: '>=2.18.0 <3.0.0'

dependencies:
collection: ^1.17.0
connectivity_plus: ^3.0.2
cupertino_icons: ^1.0.2
device_preview: ^1.1.0
Expand Down

0 comments on commit 0a11746

Please sign in to comment.