Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

旅詳細ページのUI実装 #89

Merged
merged 11 commits into from
Apr 29, 2023
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.path,
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
107 changes: 107 additions & 0 deletions lib/view/pages/trips/trip_detail_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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/extensions/build_context.dart';
import 'package:trip_app_nativeapp/features/trips/controller/trip_controller.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';

final tabStateProvider = StateProvider<int>((ref) => 0);

class TripDetailPage extends HookConsumerWidget {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

デザインめっちゃ良い感じだと思う!🧑‍🎨

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

static const path = ':id';
static const scheduleTabIndex = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こうするのいいね!👍

final int id;

@override
Widget build(BuildContext context, WidgetRef ref) {
final tabIndex = useState(scheduleTabIndex);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こうしたことなかった!ためになる!

final backgroundImageHeight = context.displaySize.width / 16 * 9;
return Scaffold(
body: SafeArea(
top: false,
child: ref.watch(tripsProvider).when(
data: (trips) {
final trip = trips.firstWhere((trip) => trip.id == id);
return NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
// TODO(seigi0714): スクロールした場合のみ表示したい。
title: Text(
trip.title.value,
style: context.textTheme.titleLarge,
),
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[memo]
ここtitleで取るのちょっとイケてないから消したいけど,ちょいめんどくさそうやから一旦おいてる!
スクリーンショット 2023-04-24 21 37 06

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',
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[memo]
一旦仮の画像入れてる!
その旅ごとのホーム画像みたいなのを入れれる用にする!

),
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
? TripSchedule(trip)
: const TripBelongingList(),
);
},
error: ErrorCat.new,
loading: CarDrivingLoading.new,
),
),
);
}
}
13 changes: 13 additions & 0 deletions lib/view/widgets/trips/trip_belonging_list.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

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

@override
Widget build(BuildContext context, WidgetRef ref) {
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/trips_list_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.go('${TripListPage.path}/${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,
),
),
),
],
),
],
),
),
),
),
);
}
}
16 changes: 16 additions & 0 deletions lib/view/widgets/trips/trip_schedule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip.dart';

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

final ExistingTrip trip;
static const height = 150.0;

@override
Widget build(BuildContext context) {
return const Center(
child: Text('スケジュール'),
);
}
}