Update events view, add responsiveness, switch to dark mode

This commit is contained in:
Shawn 2022-11-14 11:59:45 -07:00
parent 973d9c61cf
commit 34a0ae7dbf
7 changed files with 195 additions and 77 deletions

View File

@ -158,7 +158,7 @@ class _Corners {
class _Sizes { class _Sizes {
double get maxContentWidth1 => 800; double get maxContentWidth1 => 800;
double get maxContentWidth2 => 600; double get maxContentWidth2 => 600;
double get maxContentWidth3 => 400; double get maxContentWidth3 => 500;
final Size minAppSize = Size(450, 600); final Size minAppSize = Size(450, 600);
} }

View File

@ -1,21 +1,26 @@
import 'package:wonders/common_libs.dart'; import 'package:wonders/common_libs.dart';
import 'package:wonders/logic/common/string_utils.dart'; import 'package:wonders/logic/common/string_utils.dart';
import 'package:wonders/ui/common/themed_text.dart';
class TimelineEventCard extends StatelessWidget { class TimelineEventCard extends StatelessWidget {
const TimelineEventCard({Key? key, required this.year, required this.text}) : super(key: key); const TimelineEventCard({Key? key, required this.year, required this.text, this.darkMode = false}) : super(key: key);
final int year; final int year;
final String text; final String text;
final bool darkMode;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MergeSemantics( return MergeSemantics(
child: Padding( child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.sm), padding: EdgeInsets.only(bottom: $styles.insets.sm),
child: DefaultTextColor(
color: darkMode ? Colors.white : Colors.black,
child: Container( child: Container(
color: $styles.colors.offWhite, color: darkMode ? $styles.colors.greyStrong : $styles.colors.offWhite,
padding: EdgeInsets.all($styles.insets.sm), padding: EdgeInsets.all($styles.insets.sm),
child: Row( child: Row(
children: [ children: [
/// Date
SizedBox( SizedBox(
width: 75, width: 75,
child: Column( child: Column(
@ -26,8 +31,12 @@ class TimelineEventCard extends StatelessWidget {
], ],
), ),
), ),
Center(child: Container(width: 1, height: 50, color: $styles.colors.black)),
/// Divider
Center(child: Container(width: 1, height: 50, color: darkMode ? Colors.white : $styles.colors.black)),
Gap($styles.insets.sm), Gap($styles.insets.sm),
/// Text content
Expanded( Expanded(
child: Text(text, style: $styles.text.bodySmall), child: Text(text, style: $styles.text.bodySmall),
), ),
@ -35,6 +44,7 @@ class TimelineEventCard extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }

View File

@ -1,8 +1,19 @@
part of '../wonder_events.dart'; part of '../wonder_events.dart';
class _EventsList extends StatefulWidget { class _EventsList extends StatefulWidget {
const _EventsList({Key? key, required this.data}) : super(key: key); const _EventsList(
{Key? key,
required this.data,
this.topHeight = 0,
this.blurOnScroll = false,
this.showTopGradient = true,
this.showBottomGradient = true})
: super(key: key);
final WonderData data; final WonderData data;
final double topHeight;
final bool blurOnScroll;
final bool showTopGradient;
final bool showBottomGradient;
@override @override
State<_EventsList> createState() => _EventsListState(); State<_EventsList> createState() => _EventsListState();
@ -16,8 +27,6 @@ class _EventsListState extends State<_EventsList> {
super.dispose(); super.dispose();
} }
void _handleGlobalTimelinePressed() => context.push(ScreenPaths.timeline(widget.data.type));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopRouterOnOverScroll( return PopRouterOnOverScroll(
@ -28,9 +37,9 @@ class _EventsListState extends State<_EventsList> {
AnimatedBuilder( AnimatedBuilder(
animation: _scroller, animation: _scroller,
builder: (_, __) { builder: (_, __) {
bool showBackdrop = true; bool showBackdrop = widget.blurOnScroll;
double backdropAmt = 0; double backdropAmt = 0;
if (_scroller.hasClients) { if (_scroller.hasClients && showBackdrop) {
double blurStart = 50; double blurStart = 50;
double maxScroll = 150; double maxScroll = 150;
double scrollPx = _scroller.position.pixels - blurStart; double scrollPx = _scroller.position.pixels - blurStart;
@ -77,20 +86,22 @@ class _EventsListState extends State<_EventsList> {
for (var e in events.entries) { for (var e in events.entries) {
final delay = 100.ms + (100 * listItems.length).ms; final delay = 100.ms + (100 * listItems.length).ms;
listItems.add( listItems.add(
TimelineEventCard(year: e.key, text: e.value) TimelineEventCard(year: e.key, text: e.value, darkMode: true)
.animate() .animate()
.fade(delay: delay, duration: $styles.times.med * 1.5) .fade(delay: delay, duration: $styles.times.med * 1.5)
.slide(begin: Offset(0, 1), curve: Curves.easeOutBack), .slide(begin: Offset(0, 1), curve: Curves.easeOutBack),
); );
} }
return SingleChildScrollView( return Stack(
children: [
SingleChildScrollView(
controller: _scroller, controller: _scroller,
child: Column( child: Column(
children: [ children: [
IgnorePointer(child: Gap(WonderEvents._topHeight)), IgnorePointer(child: Gap(widget.topHeight)),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: $styles.colors.white, color: $styles.colors.black,
borderRadius: BorderRadius.circular($styles.corners.md), borderRadius: BorderRadius.circular($styles.corners.md),
), ),
padding: EdgeInsets.symmetric(horizontal: $styles.insets.md), padding: EdgeInsets.symmetric(horizontal: $styles.insets.md),
@ -100,16 +111,7 @@ class _EventsListState extends State<_EventsList> {
buildHandle(), buildHandle(),
Gap($styles.insets.sm), Gap($styles.insets.sm),
...listItems, ...listItems,
Gap($styles.insets.lg),
AppBtn.from(
text: $strings.eventsListButtonOpenGlobal,
expand: true,
onPressed: _handleGlobalTimelinePressed,
semanticLabel: $strings.eventsListButtonOpenGlobal,
),
Gap($styles.insets.xl), Gap($styles.insets.xl),
CompassDivider(isExpanded: true),
Gap($styles.insets.md),
HiddenCollectible(widget.data.type, index: 2, size: 150), HiddenCollectible(widget.data.type, index: 2, size: 150),
Gap(150), Gap(150),
], ],
@ -117,6 +119,22 @@ class _EventsListState extends State<_EventsList> {
), ),
], ],
), ),
),
/// Vertical gradient on btm
if (widget.showBottomGradient)
Positioned.fill(
child: BottomCenter(
child: ListOverscollGradient(bottomUp: true, size: 100),
),
),
if (widget.showTopGradient)
Positioned.fill(
child: TopCenter(
child: ListOverscollGradient(size: 100),
),
),
],
); );
} }
} }

View File

@ -0,0 +1,20 @@
part of '../wonder_events.dart';
class _TimelineBtn extends StatelessWidget {
const _TimelineBtn({Key? key, required this.type}) : super(key: key);
final WonderType type;
@override
Widget build(BuildContext context) {
void handleBtnPressed() => context.push(ScreenPaths.timeline(type));
return Padding(
padding: EdgeInsets.symmetric(horizontal: $styles.insets.md),
child: AppBtn.from(
text: $strings.eventsListButtonOpenGlobal,
expand: true,
onPressed: handleBtnPressed,
semanticLabel: $strings.eventsListButtonOpenGlobal,
),
);
}
}

View File

@ -1,11 +1,12 @@
part of '../wonder_events.dart'; part of '../wonder_events.dart';
class _TopContent extends StatelessWidget { class _WonderImageWithTimeline extends StatelessWidget {
const _TopContent({Key? key, required this.data}) : super(key: key); const _WonderImageWithTimeline({Key? key, required this.data, required this.height}) : super(key: key);
final WonderData data; final WonderData data;
final double height;
Color _fixLuminence(Color color, [double luminence = 0.35]) { Color _fixLuminance(Color color, [double luminance = 0.35]) {
double d = luminence - color.computeLuminance(); double d = luminance - color.computeLuminance();
if (d <= 0) return color; if (d <= 0) return color;
int r = color.red, g = color.green, b = color.blue; int r = color.red, g = color.green, b = color.blue;
return Color.fromARGB(255, (r + (255 - r) * d).toInt(), (g + (255 - g) * d).toInt(), (b + (255 - b) * d).toInt()); return Color.fromARGB(255, (r + (255 - r) * d).toInt(), (g + (255 - g) * d).toInt(), (b + (255 - b) * d).toInt());
@ -14,7 +15,7 @@ class _TopContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
height: WonderEvents._topHeight, height: height,
child: MergeSemantics( child: MergeSemantics(
child: LightText( child: LightText(
child: SeparatedColumn( child: SeparatedColumn(
@ -45,7 +46,7 @@ class _TopContent extends StatelessWidget {
timelineBuilder: (_, data, isSelected) { timelineBuilder: (_, data, isSelected) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected ? _fixLuminence(data.type.fgColor) : Colors.transparent, color: isSelected ? _fixLuminance(data.type.fgColor) : Colors.transparent,
border: Border.all(color: $styles.colors.greyMedium), border: Border.all(color: $styles.colors.greyMedium),
borderRadius: BorderRadius.circular($styles.corners.md), borderRadius: BorderRadius.circular($styles.corners.md),
), ),

View File

@ -2,7 +2,7 @@ import 'package:wonders/common_libs.dart';
import 'package:wonders/logic/common/string_utils.dart'; import 'package:wonders/logic/common/string_utils.dart';
import 'package:wonders/logic/data/wonder_data.dart'; import 'package:wonders/logic/data/wonder_data.dart';
import 'package:wonders/ui/common/app_backdrop.dart'; import 'package:wonders/ui/common/app_backdrop.dart';
import 'package:wonders/ui/common/compass_divider.dart'; import 'package:wonders/ui/common/app_icons.dart';
import 'package:wonders/ui/common/curved_clippers.dart'; import 'package:wonders/ui/common/curved_clippers.dart';
import 'package:wonders/ui/common/hidden_collectible.dart'; import 'package:wonders/ui/common/hidden_collectible.dart';
import 'package:wonders/ui/common/list_gradient.dart'; import 'package:wonders/ui/common/list_gradient.dart';
@ -13,34 +13,103 @@ import 'package:wonders/ui/common/wonders_timeline_builder.dart';
import 'package:wonders/ui/wonder_illustrations/common/wonder_title_text.dart'; import 'package:wonders/ui/wonder_illustrations/common/wonder_title_text.dart';
part 'widgets/_events_list.dart'; part 'widgets/_events_list.dart';
part 'widgets/_top_content.dart'; part 'widgets/_timeline_btn.dart';
part 'widgets/_wonder_image_with_timeline.dart';
class WonderEvents extends StatelessWidget { class WonderEvents extends StatelessWidget {
static const double _topHeight = 450;
WonderEvents({Key? key, required this.type}) : super(key: key); WonderEvents({Key? key, required this.type}) : super(key: key);
final WonderType type; final WonderType type;
late final _data = wondersLogic.getData(type); late final _data = wondersLogic.getData(type);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (_, constraints) { void handleTimelineBtnPressed() => context.push(ScreenPaths.timeline(type));
return LayoutBuilder(builder: (context, constraints) {
return Container( return Container(
color: $styles.colors.black, color: $styles.colors.black,
child: SafeArea( child: SafeArea(
bottom: false, bottom: false,
child: Center(
child: SizedBox(
width: $styles.sizes.maxContentWidth1,
child: Stack( child: Stack(
children: [ children: [
/// Top content, sits underneath scrolling list Positioned.fill(
_TopContent(data: _data), top: $styles.insets.lg,
child: context.isLandscape ? _buildLandscape() : _buildPortrait(),
/// Scrolling Events list, takes up the full view ),
_EventsList(data: _data), Positioned(
right: $styles.insets.lg,
top: $styles.insets.lg,
child: CircleIconBtn(
icon: AppIcons.timeline,
onPressed: handleTimelineBtnPressed,
semanticLabel: $strings.eventsListButtonOpenGlobal)),
], ],
), ),
), ),
);
});
}
/// Landscape layout is a row, with the WonderImage on left and events on the right
Widget _buildLandscape() {
return Row(
children: [
/// WonderImage w/ Timeline btn sits on the left
Expanded(
child: Center(
child: SizedBox(
width: $styles.sizes.maxContentWidth3,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Gap($styles.insets.lg),
Expanded(child: Center(child: _WonderImageWithTimeline(data: _data, height: 500))),
Gap($styles.insets.lg),
SizedBox(width: 300, child: _TimelineBtn(type: type)),
Gap($styles.insets.xl),
],
),
),
),
),
/// Scrolling event list
Expanded(
child: Center(
child: SizedBox(
width: $styles.sizes.maxContentWidth1,
child: _EventsList(data: _data, topHeight: 100, blurOnScroll: false),
),
),
),
],
);
}
/// Portrait layout is a stack with the list scrolling overtop of the WonderImage
Widget _buildPortrait() {
return LayoutBuilder(builder: (_, constraints) {
double topHeight = max(constraints.maxHeight * .55, 200);
return Center(
child: SizedBox(
width: $styles.sizes.maxContentWidth3,
child: Stack(
children: [
/// Top content, sits underneath scrolling list
_WonderImageWithTimeline(height: topHeight, data: _data),
/// Scrolling Events list, takes up the full view
Column(
children: [
Expanded(
child: _EventsList(data: _data, topHeight: topHeight, blurOnScroll: true, showTopGradient: false),
),
Gap($styles.insets.lg),
SizedBox(width: $styles.sizes.maxContentWidth3, child: _TimelineBtn(type: _data.type)),
Gap($styles.insets.xl),
],
),
],
), ),
), ),
); );

View File

@ -297,7 +297,7 @@ packages:
name: flutter_inappwebview name: flutter_inappwebview
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.4.3+7" version: "5.7.1"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description: