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 // Mark current index as active
_activated[widget.index] = true; _activated[widget.index] = true;
final children = List.generate(_activated.length, (i) { 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( return IndexedStack(
alignment: widget.alignment, alignment: widget.alignment,

View File

@ -10,7 +10,8 @@ class TimelineEventCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MergeSemantics( return Focus(
child: MergeSemantics(
child: Padding( child: Padding(
padding: EdgeInsets.only(bottom: $styles.insets.sm), padding: EdgeInsets.only(bottom: $styles.insets.sm),
child: DefaultTextColor( child: DefaultTextColor(
@ -27,7 +28,8 @@ class TimelineEventCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('${year.abs()}', style: $styles.text.h3.copyWith(fontWeight: FontWeight.w400, height: 1)), Text('${year.abs()}',
style: $styles.text.h3.copyWith(fontWeight: FontWeight.w400, height: 1)),
Text(StringUtils.getYrSuffix(year), style: $styles.text.bodySmall), Text(StringUtils.getYrSuffix(year), style: $styles.text.bodySmall),
], ],
), ),
@ -40,7 +42,7 @@ class TimelineEventCard extends StatelessWidget {
/// Text content /// Text content
Expanded( Expanded(
child: Focus(child: Text(text, style: $styles.text.body)), child: Text(text, style: $styles.text.body),
), ),
], ],
), ),
@ -48,6 +50,7 @@ class TimelineEventCard extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }

View File

@ -170,7 +170,8 @@ class _WonderEditorialScreenState extends State<WonderEditorialScreen> {
), ),
/// Home Btn /// Home Btn
AnimatedBuilder( SafeArea(
child: AnimatedBuilder(
animation: _scroller, animation: _scroller,
builder: (_, child) { builder: (_, child) {
return AnimatedOpacity( return AnimatedOpacity(
@ -186,6 +187,7 @@ class _WonderEditorialScreenState extends State<WonderEditorialScreen> {
child: BackBtn(icon: AppIcons.north, onPressed: _handleBackPressed), 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) { bool _handleKeyDown(KeyDownEvent event) {
final key = event.logicalKey; final key = event.logicalKey;
Map<LogicalKeyboardKey, int> keyActions = { Map<LogicalKeyboardKey, int> keyActions = {
@ -112,20 +113,20 @@ class _PhotoGalleryState extends State<PhotoGallery> {
if (actionValue == null) return false; if (actionValue == null) return false;
int newIndex = _index + actionValue; 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 isRightSide = _index % _gridSize == _gridSize - 1;
bool isLeftSide = _index % _gridSize == 0; bool isLeftSide = _index % _gridSize == 0;
bool outOfBounds = newIndex < 0 || newIndex >= _imgCount; bool outOfRange = newIndex < 0 || newIndex >= _imgCount;
if ((isRightSide && key == LogicalKeyboardKey.arrowRight) || if ((isRightSide && key == LogicalKeyboardKey.arrowRight) ||
(isLeftSide && key == LogicalKeyboardKey.arrowLeft) || (isLeftSide && key == LogicalKeyboardKey.arrowLeft) ||
outOfBounds) { outOfRange) {
return false; return false;
} }
_setIndex(newIndex); _setIndex(newIndex);
return true; return true;
} }
/// Converts a swipe direction into a new index // Converts a swipe direction into a new index
void _handleSwipe(Offset dir) { void _handleSwipe(Offset dir) {
// Calculate new index, y swipes move by an entire row, x swipes move one index at a time // Calculate new index, y swipes move by an entire row, x swipes move one index at a time
int newIndex = _index; 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/editorial/editorial_screen.dart';
import 'package:wonders/ui/screens/photo_gallery/photo_gallery.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_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 { class WonderDetailsScreen extends StatefulWidget with GetItStatefulWidgetMixin {
WonderDetailsScreen({Key? key, required this.type, this.tabIndex = 0}) : super(key: key); 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) { void _handleTabTapped(int index) {
_tabController.index = index; _tabController.index = index;
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
context.go(ScreenPaths.wonderDetails(widget.type, tabIndex: _tabController.index)); context.go(ScreenPaths.wonderDetails(widget.type, tabIndex: _tabController.index));
} }
@ -80,7 +81,7 @@ class _WonderDetailsScreenState extends State<WonderDetailsScreen>
WonderEditorialScreen(wonder, contentPadding: menuPadding), WonderEditorialScreen(wonder, contentPadding: menuPadding),
PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type), PhotoGallery(collectionId: wonder.unsplashCollectionId, wonderType: wonder.type),
ArtifactCarouselScreen(type: wonder.type, contentPadding: menuPadding), 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 { class _EventsList extends StatefulWidget {
const _EventsList({ const _EventsList({

View File

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

View File

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