-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #170 from FlutterKaigi/feature/add-sponsor-detail-…
…page スポンサー詳細ページを追加
- Loading branch information
Showing
6 changed files
with
434 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:confwebsite2023/core/components/responsive_widget.dart'; | ||
import 'package:confwebsite2023/core/theme.dart'; | ||
import 'package:confwebsite2023/features/footer/ui/footer.dart'; | ||
import 'package:confwebsite2023/features/header/data/header_item_button_data.dart'; | ||
import 'package:confwebsite2023/features/header/ui/header_widget.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/detail/sponsor_detail.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_hooks/flutter_hooks.dart'; | ||
import 'package:js/js_util.dart' as js_util; | ||
|
||
final class SponsorPage extends HookWidget { | ||
const SponsorPage({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final scrollController = useScrollController(); | ||
|
||
const sectionKeys = ( | ||
event: GlobalObjectKey('eventSectionKey'), | ||
session: GlobalObjectKey('sessionSectionKey'), | ||
sponsor: GlobalObjectKey('sponsorSectionKey'), | ||
staff: GlobalObjectKey('staffSectionKey'), | ||
); | ||
|
||
final items = <HeaderItemButtonData>[ | ||
HeaderItemButtonData( | ||
title: 'Staff', | ||
onPressed: () async => Scrollable.ensureVisible( | ||
sectionKeys.staff.currentContext!, | ||
curve: Curves.easeOutCirc, | ||
duration: const Duration(milliseconds: 750), | ||
), | ||
), | ||
]; | ||
|
||
return Scaffold( | ||
backgroundColor: baselineColorScheme.ref.secondary.secondary10, | ||
body: _MainPageBody( | ||
scrollController: scrollController, | ||
staffSectionKey: sectionKeys.staff, | ||
items: items, | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _MainPageBody extends StatelessWidget { | ||
const _MainPageBody({ | ||
required this.scrollController, | ||
required this.staffSectionKey, | ||
required this.items, | ||
}); | ||
|
||
final ScrollController scrollController; | ||
final GlobalKey<State<StatefulWidget>> staffSectionKey; | ||
final List<HeaderItemButtonData> items; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final width = MediaQuery.sizeOf(context).width; | ||
final largeScreenSize = ResponsiveWidget.largeScreenSize.toDouble(); | ||
final horizontal = max<double>(16, (width - largeScreenSize) / 4.0); | ||
final padding = EdgeInsets.symmetric( | ||
horizontal: horizontal, | ||
); | ||
|
||
js_util.callMethod<void>(js_util.globalThis, '_show', []); | ||
|
||
return Stack( | ||
children: [ | ||
const SizedBox( | ||
width: double.infinity, | ||
height: 800, | ||
child: DecoratedBox( | ||
decoration: BoxDecoration( | ||
gradient: LinearGradient( | ||
begin: Alignment.topCenter, | ||
end: Alignment.bottomCenter, | ||
colors: [ | ||
Color(0xFF602678), | ||
Color(0x004B0082), | ||
], | ||
), | ||
), | ||
), | ||
), | ||
CustomScrollView( | ||
controller: scrollController, | ||
slivers: [ | ||
_Sliver( | ||
padding: padding, | ||
child: HeaderBar( | ||
items: items, | ||
onTitleTap: () async => scrollController.animateTo( | ||
0, | ||
duration: const Duration(milliseconds: 750), | ||
curve: Curves.easeOutCirc, | ||
), | ||
), | ||
), | ||
const SliverToBoxAdapter( | ||
child: Spaces.vertical_30, | ||
), | ||
_Sliver( | ||
padding: padding, | ||
child: const SponsorDetail(), | ||
), | ||
const SliverToBoxAdapter( | ||
child: Spaces.vertical_200, | ||
), | ||
const SliverToBoxAdapter( | ||
child: Footer(), | ||
), | ||
], | ||
), | ||
], | ||
); | ||
} | ||
} | ||
|
||
class _Sliver extends StatelessWidget { | ||
const _Sliver({ | ||
required this.child, | ||
required this.padding, | ||
}); | ||
|
||
final Widget child; | ||
final EdgeInsets padding; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return SliverPadding( | ||
padding: padding, | ||
sliver: SliverToBoxAdapter( | ||
child: child, | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import 'package:confwebsite2023/core/components/responsive_widget.dart'; | ||
import 'package:confwebsite2023/core/gen/assets.gen.dart'; | ||
import 'package:confwebsite2023/core/theme.dart'; | ||
import 'package:confwebsite2023/features/sponsor/data/sponsor.dart'; | ||
import 'package:confwebsite2023/features/sponsor/data/sponsor_plan.dart'; | ||
import 'package:confwebsite2023/features/sponsor/data/sponsor_session.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/detail/sponsor_detail_content.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||
|
||
final class SponsorDetail extends ConsumerWidget { | ||
const SponsorDetail({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context, WidgetRef ref) { | ||
// TODO: データを動的に取得する | ||
const sponsor = _sponsor; | ||
|
||
return ResponsiveWidget( | ||
largeWidget: SponsorDetailContent( | ||
sponsor: sponsor, | ||
h1TextStyle: AppTextStyle.pcHeading1, | ||
h2TextStyle: AppTextStyle.pcHeading2, | ||
onTweetPressed: () {}, | ||
onCopyUrlPressed: () {}, | ||
), | ||
smallWidget: SponsorDetailContent( | ||
sponsor: sponsor, | ||
h1TextStyle: AppTextStyle.spHeading1, | ||
h2TextStyle: AppTextStyle.spHeading2, | ||
onTweetPressed: () {}, | ||
onCopyUrlPressed: () {}, | ||
), | ||
); | ||
} | ||
} | ||
|
||
// TODO: データを動的に取得する | ||
const _sponsor = Sponsor( | ||
name: 'flutterkaigi', | ||
displayName: '企業名・ブランド名', | ||
url: 'https://flutterkaigi.jp/2023/', | ||
logoAssetName: Assets.flutterkaigiLogoShadowed, | ||
plan: SponsorPlan.platinum, | ||
session: SponsorSession( | ||
id: 'id', | ||
title: 'Flutterアプリ開発におけるモジュール分割戦略 〜dart15万行を100分割する〜', | ||
url: 'https://flutterkaigi.jp/2023/', | ||
scheduledAt: '2023年11月10日 11:10〜11:55(45分)', | ||
), | ||
introduction: '紹介文(600字程度)\n紹介文(600字程度)\n紹介文(600字程度)', | ||
); | ||
|
||
// const _sponsor = Sponsor( | ||
// name: 'flutterkaigi', | ||
// displayName: '企業名・ブランド名', | ||
// url: 'https://flutterkaigi.jp/2023/', | ||
// logoAssetName: Assets.flutterkaigiLogoShadowed, | ||
// plan: SponsorPlan.platinum, | ||
// session: null, | ||
// introduction: '紹介文(600字程度)\n紹介文(600字程度)\n紹介文(600字程度)', | ||
// ); |
94 changes: 94 additions & 0 deletions
94
lib/features/sponsor/ui/detail/sponsor_detail_content.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import 'package:confwebsite2023/core/components/social_share.dart'; | ||
import 'package:confwebsite2023/core/theme.dart'; | ||
import 'package:confwebsite2023/features/sponsor/data/sponsor.dart'; | ||
import 'package:confwebsite2023/features/sponsor/data/sponsor_plan.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/detail/sponsor_introduction.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/detail/sponsor_session.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/sponsor_plan_header.dart'; | ||
import 'package:confwebsite2023/features/sponsor/ui/sponsors_header.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
final class SponsorDetailContent extends StatelessWidget { | ||
const SponsorDetailContent({ | ||
required this.sponsor, | ||
required this.h1TextStyle, | ||
required this.h2TextStyle, | ||
required this.onTweetPressed, | ||
required this.onCopyUrlPressed, | ||
super.key, | ||
}); | ||
|
||
final Sponsor sponsor; | ||
|
||
final TextStyle h1TextStyle; | ||
final TextStyle h2TextStyle; | ||
final VoidCallback? onTweetPressed; | ||
final VoidCallback? onCopyUrlPressed; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
SponsorsHeader( | ||
style: h1TextStyle, | ||
), | ||
SizedBox( | ||
width: double.infinity, | ||
child: SocialShare( | ||
onTweetPressed: onTweetPressed, | ||
onCopyUrlPressed: onCopyUrlPressed, | ||
), | ||
), | ||
Spaces.vertical_20, | ||
Container( | ||
width: double.infinity, | ||
decoration: const BoxDecoration( | ||
color: Color(0x22000000), | ||
borderRadius: BorderRadius.all(Radius.circular(12)), | ||
), | ||
child: Padding( | ||
padding: const EdgeInsets.all(40), | ||
child: Column( | ||
children: [ | ||
SponsorPlanHeader( | ||
text: switch (sponsor.plan) { | ||
SponsorPlan.platinum => 'Platinum Sponsor', | ||
SponsorPlan.gold => 'Gold Sponsor', | ||
SponsorPlan.silver => 'Silver Sponsor', | ||
}, | ||
style: h2TextStyle, | ||
plan: sponsor.plan, | ||
), | ||
Column( | ||
children: [ | ||
SponsorIntroduction( | ||
assetName: sponsor.logoAssetName, | ||
name: sponsor.displayName, | ||
url: sponsor.url, | ||
introduction: sponsor.introduction, | ||
), | ||
Spaces.vertical_40, | ||
if (sponsor.session != null) ...[ | ||
const Divider(height: 1), | ||
Spaces.vertical_40, | ||
SponsorSessionSection(session: sponsor.session!), | ||
], | ||
], | ||
), | ||
], | ||
), | ||
), | ||
), | ||
Spaces.vertical_20, | ||
SizedBox( | ||
width: double.infinity, | ||
child: SocialShare( | ||
onTweetPressed: onTweetPressed, | ||
onCopyUrlPressed: onCopyUrlPressed, | ||
), | ||
), | ||
], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import 'package:confwebsite2023/core/theme.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_svg/svg.dart'; | ||
import 'package:url_launcher/link.dart'; | ||
|
||
final class SponsorIntroduction extends StatelessWidget { | ||
const SponsorIntroduction({ | ||
required this.assetName, | ||
required this.name, | ||
required this.url, | ||
required this.introduction, | ||
super.key, | ||
}); | ||
|
||
final String assetName; | ||
final String name; | ||
final String url; | ||
final String introduction; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final textTheme = Theme.of(context).textTheme; | ||
|
||
return Column( | ||
children: [ | ||
SvgPicture.asset( | ||
assetName, | ||
width: 400, | ||
height: 200, | ||
fit: BoxFit.fitHeight, | ||
), | ||
SizedBox( | ||
width: double.infinity, | ||
child: Padding( | ||
padding: const EdgeInsets.all(40), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text( | ||
name, | ||
style: textTheme.headlineLarge, | ||
), | ||
Spaces.vertical_16, | ||
Transform.translate( | ||
offset: const Offset(-16, 0), | ||
child: Link( | ||
uri: Uri.parse(url), | ||
target: LinkTarget.blank, | ||
builder: (_, followLink) => TextButton( | ||
style: TextButton.styleFrom( | ||
padding: const EdgeInsets.symmetric( | ||
horizontal: 16, | ||
), | ||
alignment: Alignment.centerLeft, | ||
textStyle: Theme.of(context) | ||
.textTheme | ||
.bodyLarge | ||
?.copyWith( | ||
color: baselineColorScheme.sys.dark.onBackground, | ||
), | ||
), | ||
onPressed: followLink, | ||
child: Text(url), | ||
), | ||
), | ||
), | ||
Spaces.vertical_16, | ||
Text( | ||
introduction, | ||
style: textTheme.bodyLarge, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
], | ||
); | ||
} | ||
} |
Oops, something went wrong.