Compare commits

...

7 Commits

Author SHA1 Message Date
Shawn
9f3c38d1b5 Add focus element to event cards 2024-02-05 14:30:43 -07:00
Shawn
4bb389cec2 Rename events screen 2024-02-05 14:30:12 -07:00
Shawn
9903a96d96 Clear current focus when changing tabs 2024-02-05 14:29:44 -07:00
Shawn
6093a11c9a Cleanup 2024-02-05 14:29:12 -07:00
Shawn
f951eec66a Add safe area to homeBtn 2024-02-05 14:28:53 -07:00
Shawn
b527acba1c Use ExcludeFocus widget when IndexedStack child is not selected for better tab traversal with keyboard / screen reader. 2024-01-23 10:32:09 -07:00
Shawn
1bfecc0ba7 Rename Events screen 2024-01-23 10:27:22 -07:00
9 changed files with 69 additions and 58 deletions

View File

@ -39,7 +39,9 @@ class LazyIndexedStackState extends State<LazyIndexedStack> {
// Mark current index as active
_activated[widget.index] = true;
final children = List.generate(_activated.length, (i) {
return _activated[i] ? widget.children[i] : const SizedBox.shrink();
Widget child = _activated[i] ? widget.children[i] : const SizedBox.shrink();
bool isSelected = widget.index == i;
return ExcludeFocus(excluding: !isSelected, child: child);
});
return IndexedStack(
alignment: widget.alignment,

View File

@ -10,39 +10,42 @@ class TimelineEventCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MergeSemantics(
child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.sm),
child: DefaultTextColor(
color: darkMode ? Colors.white : Colors.black,
child: Container(
color: darkMode ? $styles.colors.greyStrong : $styles.colors.offWhite,
padding: EdgeInsets.all($styles.insets.sm),
child: IntrinsicHeight(
child: Row(
children: [
/// Date
SizedBox(
width: 75,
child: Column(
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),
],
return Focus(
child: MergeSemantics(
child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.sm),
child: DefaultTextColor(
color: darkMode ? Colors.white : Colors.black,
child: Container(
color: darkMode ? $styles.colors.greyStrong : $styles.colors.offWhite,
padding: EdgeInsets.all($styles.insets.sm),
child: IntrinsicHeight(
child: Row(
children: [
/// Date
SizedBox(
width: 75,
child: Column(
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),
],
),
),
),
/// Divider
Container(width: 1, color: darkMode ? Colors.white : $styles.colors.black),
/// Divider
Container(width: 1, color: darkMode ? Colors.white : $styles.colors.black),
Gap($styles.insets.sm),
Gap($styles.insets.sm),
/// Text content
Expanded(
child: Focus(child: Text(text, style: $styles.text.body)),
),
],
/// Text content
Expanded(
child: Text(text, style: $styles.text.body),
),
],
),
),
),
),

View File

@ -170,20 +170,22 @@ class _WonderEditorialScreenState extends State<WonderEditorialScreen> {
),
/// Home Btn
AnimatedBuilder(
animation: _scroller,
builder: (_, child) {
return AnimatedOpacity(
opacity: _scrollPos.value > 0 ? 0 : 1,
duration: $styles.times.med,
child: child,
);
},
child: Align(
alignment: backBtnAlign,
child: Padding(
padding: EdgeInsets.all($styles.insets.sm),
child: BackBtn(icon: AppIcons.north, onPressed: _handleBackPressed),
SafeArea(
child: AnimatedBuilder(
animation: _scroller,
builder: (_, child) {
return AnimatedOpacity(
opacity: _scrollPos.value > 0 ? 0 : 1,
duration: $styles.times.med,
child: child,
);
},
child: Align(
alignment: backBtnAlign,
child: Padding(
padding: EdgeInsets.all($styles.insets.sm),
child: BackBtn(icon: AppIcons.north, onPressed: _handleBackPressed),
),
),
),
)

View File

@ -98,6 +98,7 @@ class _PhotoGalleryState extends State<PhotoGallery> {
}
}
// Manually handle keyboard arrow-keys so we can control how the navigation behaves
bool _handleKeyDown(KeyDownEvent event) {
final key = event.logicalKey;
Map<LogicalKeyboardKey, int> keyActions = {
@ -112,20 +113,20 @@ class _PhotoGalleryState extends State<PhotoGallery> {
if (actionValue == null) return false;
int newIndex = _index + actionValue;
// Block actions along edges of the grid
// Allow free movement across the grid of items but block actions that try and go outside the bounds
bool isRightSide = _index % _gridSize == _gridSize - 1;
bool isLeftSide = _index % _gridSize == 0;
bool outOfBounds = newIndex < 0 || newIndex >= _imgCount;
bool outOfRange = newIndex < 0 || newIndex >= _imgCount;
if ((isRightSide && key == LogicalKeyboardKey.arrowRight) ||
(isLeftSide && key == LogicalKeyboardKey.arrowLeft) ||
outOfBounds) {
outOfRange) {
return false;
}
_setIndex(newIndex);
return true;
}
/// Converts a swipe direction into a new index
// Converts a swipe direction into a new index
void _handleSwipe(Offset dir) {
// Calculate new index, y swipes move by an entire row, x swipes move one index at a time
int newIndex = _index;

View File

@ -5,7 +5,7 @@ import 'package:wonders/ui/screens/artifact/artifact_carousel/artifact_carousel_
import 'package:wonders/ui/screens/editorial/editorial_screen.dart';
import 'package:wonders/ui/screens/photo_gallery/photo_gallery.dart';
import 'package:wonders/ui/screens/wonder_details/wonder_details_tab_menu.dart';
import 'package:wonders/ui/screens/wonder_events/wonder_events.dart';
import 'package:wonders/ui/screens/wonder_events/wonder_events_screen.dart';
class WonderDetailsScreen extends StatefulWidget with GetItStatefulWidgetMixin {
WonderDetailsScreen({Key? key, required this.type, this.tabIndex = 0}) : super(key: key);
@ -51,6 +51,7 @@ class _WonderDetailsScreenState extends State<WonderDetailsScreen>
void _handleTabTapped(int index) {
_tabController.index = index;
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
context.go(ScreenPaths.wonderDetails(widget.type, tabIndex: _tabController.index));
}
@ -80,7 +81,7 @@ class _WonderDetailsScreenState extends State<WonderDetailsScreen>
WonderEditorialScreen(wonder, contentPadding: menuPadding),
PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type),
ArtifactCarouselScreen(type: wonder.type, contentPadding: menuPadding),
WonderEvents(type: widget.type, contentPadding: menuPadding),
WonderEventsScreen(type: widget.type, contentPadding: menuPadding),
],
),

View File

@ -1,4 +1,4 @@
part of '../wonder_events.dart';
part of '../wonder_events_screen.dart';
class _EventsList extends StatefulWidget {
const _EventsList({

View File

@ -1,4 +1,4 @@
part of '../wonder_events.dart';
part of '../wonder_events_screen.dart';
class _TimelineBtn extends StatelessWidget {
const _TimelineBtn({Key? key, required this.type, this.width}) : super(key: key);

View File

@ -1,4 +1,4 @@
part of '../wonder_events.dart';
part of '../wonder_events_screen.dart';
class _WonderImageWithTimeline extends StatelessWidget {
const _WonderImageWithTimeline({Key? key, required this.data, required this.height}) : super(key: key);
@ -47,7 +47,9 @@ class _WonderImageWithTimeline extends StatelessWidget {
return Container(
decoration: BoxDecoration(
color: isSelected ? _fixLuminance(data.type.fgColor) : Colors.transparent,
border: isSelected ? Border.all(color: Colors.transparent) : Border.all(color: $styles.colors.greyMedium),
border: isSelected
? Border.all(color: Colors.transparent)
: Border.all(color: $styles.colors.greyMedium),
borderRadius: BorderRadius.circular($styles.corners.md),
),
);

View File

@ -18,15 +18,15 @@ part 'widgets/_events_list.dart';
part 'widgets/_timeline_btn.dart';
part 'widgets/_wonder_image_with_timeline.dart';
class WonderEvents extends StatefulWidget {
const WonderEvents({Key? key, required this.type, this.contentPadding = EdgeInsets.zero}) : super(key: key);
class WonderEventsScreen extends StatefulWidget {
const WonderEventsScreen({Key? key, required this.type, this.contentPadding = EdgeInsets.zero}) : super(key: key);
final WonderType type;
final EdgeInsets contentPadding;
@override
State<WonderEvents> createState() => _WonderEventsState();
State<WonderEventsScreen> createState() => _WonderEventsScreenState();
}
class _WonderEventsState extends State<WonderEvents> {
class _WonderEventsScreenState extends State<WonderEventsScreen> {
late final _data = wondersLogic.getData(widget.type);
final _eventsListKey = GlobalKey<_EventsListState>();
double _scrollPos = 0;