Update events view, add responsiveness, switch to dark mode
This commit is contained in:
parent
973d9c61cf
commit
34a0ae7dbf
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +1,47 @@
|
|||||||
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: Container(
|
child: DefaultTextColor(
|
||||||
color: $styles.colors.offWhite,
|
color: darkMode ? Colors.white : Colors.black,
|
||||||
padding: EdgeInsets.all($styles.insets.sm),
|
child: Container(
|
||||||
child: Row(
|
color: darkMode ? $styles.colors.greyStrong : $styles.colors.offWhite,
|
||||||
children: [
|
padding: EdgeInsets.all($styles.insets.sm),
|
||||||
SizedBox(
|
child: Row(
|
||||||
width: 75,
|
children: [
|
||||||
child: Column(
|
/// Date
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
SizedBox(
|
||||||
children: [
|
width: 75,
|
||||||
Text('${year.abs()}', style: $styles.text.h3.copyWith(fontWeight: FontWeight.w400, height: 1)),
|
child: Column(
|
||||||
Text(StringUtils.getYrSuffix(year), style: $styles.text.bodySmall),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
],
|
children: [
|
||||||
|
Text('${year.abs()}', style: $styles.text.h3.copyWith(fontWeight: FontWeight.w400, height: 1)),
|
||||||
|
Text(StringUtils.getYrSuffix(year), style: $styles.text.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Center(child: Container(width: 1, height: 50, color: $styles.colors.black)),
|
/// Divider
|
||||||
Gap($styles.insets.sm),
|
Center(child: Container(width: 1, height: 50, color: darkMode ? Colors.white : $styles.colors.black)),
|
||||||
Expanded(
|
Gap($styles.insets.sm),
|
||||||
child: Text(text, style: $styles.text.bodySmall),
|
|
||||||
),
|
/// Text content
|
||||||
],
|
Expanded(
|
||||||
|
child: Text(text, style: $styles.text.bodySmall),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,46 +86,55 @@ 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(
|
||||||
controller: _scroller,
|
children: [
|
||||||
child: Column(
|
SingleChildScrollView(
|
||||||
children: [
|
controller: _scroller,
|
||||||
IgnorePointer(child: Gap(WonderEvents._topHeight)),
|
child: Column(
|
||||||
Container(
|
children: [
|
||||||
decoration: BoxDecoration(
|
IgnorePointer(child: Gap(widget.topHeight)),
|
||||||
color: $styles.colors.white,
|
Container(
|
||||||
borderRadius: BorderRadius.circular($styles.corners.md),
|
decoration: BoxDecoration(
|
||||||
),
|
color: $styles.colors.black,
|
||||||
padding: EdgeInsets.symmetric(horizontal: $styles.insets.md),
|
borderRadius: BorderRadius.circular($styles.corners.md),
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Gap($styles.insets.xs),
|
|
||||||
buildHandle(),
|
|
||||||
Gap($styles.insets.sm),
|
|
||||||
...listItems,
|
|
||||||
Gap($styles.insets.lg),
|
|
||||||
AppBtn.from(
|
|
||||||
text: $strings.eventsListButtonOpenGlobal,
|
|
||||||
expand: true,
|
|
||||||
onPressed: _handleGlobalTimelinePressed,
|
|
||||||
semanticLabel: $strings.eventsListButtonOpenGlobal,
|
|
||||||
),
|
),
|
||||||
Gap($styles.insets.xl),
|
padding: EdgeInsets.symmetric(horizontal: $styles.insets.md),
|
||||||
CompassDivider(isExpanded: true),
|
child: Column(
|
||||||
Gap($styles.insets.md),
|
children: [
|
||||||
HiddenCollectible(widget.data.type, index: 2, size: 150),
|
Gap($styles.insets.xs),
|
||||||
Gap(150),
|
buildHandle(),
|
||||||
],
|
Gap($styles.insets.sm),
|
||||||
|
...listItems,
|
||||||
|
Gap($styles.insets.xl),
|
||||||
|
HiddenCollectible(widget.data.type, index: 2, size: 150),
|
||||||
|
Gap(150),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
lib/ui/screens/wonder_events/widgets/_timeline_btn.dart
Normal file
20
lib/ui/screens/wonder_events/widgets/_timeline_btn.dart
Normal 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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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),
|
||||||
),
|
),
|
@ -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,36 +13,105 @@ 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: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
top: $styles.insets.lg,
|
||||||
|
child: context.isLandscape ? _buildLandscape() : _buildPortrait(),
|
||||||
|
),
|
||||||
|
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: Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: $styles.sizes.maxContentWidth1,
|
width: $styles.sizes.maxContentWidth3,
|
||||||
child: Stack(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
/// Top content, sits underneath scrolling list
|
Gap($styles.insets.lg),
|
||||||
_TopContent(data: _data),
|
Expanded(child: Center(child: _WonderImageWithTimeline(data: _data, height: 500))),
|
||||||
|
Gap($styles.insets.lg),
|
||||||
/// Scrolling Events list, takes up the full view
|
SizedBox(width: 300, child: _TimelineBtn(type: type)),
|
||||||
_EventsList(data: _data),
|
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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user