From 068d7daf9db75a48515f9361edca4f2aec5d9ff5 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 1 Mar 2023 14:36:30 -0700 Subject: [PATCH] Add focus outlines to btns, add focus nodes to event markers and editorial text content --- lib/ui/common/controls/buttons.dart | 47 ++++++++++++++++--- .../editorial/widgets/_scrolling_content.dart | 2 +- .../timeline/widgets/_event_markers.dart | 23 ++++++--- .../timeline/widgets/_scrolling_viewport.dart | 6 +++ 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/lib/ui/common/controls/buttons.dart b/lib/ui/common/controls/buttons.dart index 23502f0d..f90c7fc6 100644 --- a/lib/ui/common/controls/buttons.dart +++ b/lib/ui/common/controls/buttons.dart @@ -121,19 +121,35 @@ class AppBtn extends StatelessWidget { tapTargetSize: MaterialTapTargetSize.shrinkWrap, splashFactory: NoSplash.splashFactory, backgroundColor: ButtonStyleButton.allOrNull(bgColor ?? defaultColor), - overlayColor: ButtonStyleButton.allOrNull(Colors.white.withOpacity(.1)), // disable default press effect + overlayColor: ButtonStyleButton.allOrNull(Colors.transparent), // disable default press effect shape: ButtonStyleButton.allOrNull(shape), padding: ButtonStyleButton.allOrNull(padding ?? EdgeInsets.all($styles.insets.md)), enableFeedback: enableFeedback, ); - Widget button = TextButton( - onPressed: onPressed, - style: style, - child: DefaultTextStyle( - style: DefaultTextStyle.of(context).style.copyWith(color: textColor), - child: content, + Widget button = _CustomFocusBuilder( + builder: (context, focus) => Stack( + children: [ + TextButton( + onPressed: onPressed, + style: style, + focusNode: focus, + child: DefaultTextStyle( + style: DefaultTextStyle.of(context).style.copyWith(color: textColor), + child: content, + ), + ), + if (focus.hasFocus) + Positioned.fill( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular($styles.corners.md), + border: Border.all(color: $styles.colors.accent1, width: 3))), + ), + ) + ], ), ); @@ -181,3 +197,20 @@ class _ButtonPressEffectState extends State<_ButtonPressEffect> { ); } } + +class _CustomFocusBuilder extends StatefulWidget { + const _CustomFocusBuilder({Key? key, required this.builder}) : super(key: key); + final Widget Function(BuildContext context, FocusNode focus) builder; + + @override + State<_CustomFocusBuilder> createState() => _CustomFocusBuilderState(); +} + +class _CustomFocusBuilderState extends State<_CustomFocusBuilder> { + late final _focusNode = FocusNode()..addListener(() => setState(() {})); + + @override + Widget build(BuildContext context) { + return widget.builder.call(context, _focusNode); + } +} diff --git a/lib/ui/screens/editorial/widgets/_scrolling_content.dart b/lib/ui/screens/editorial/widgets/_scrolling_content.dart index 06c0468c..b47cd54f 100644 --- a/lib/ui/screens/editorial/widgets/_scrolling_content.dart +++ b/lib/ui/screens/editorial/widgets/_scrolling_content.dart @@ -20,7 +20,7 @@ class _ScrollingContent extends StatelessWidget { @override Widget build(BuildContext context) { - Text buildText(String value) => Text(_fixNewlines(value), style: $styles.text.body); + Widget buildText(String value) => Focus(child: Text(_fixNewlines(value), style: $styles.text.body)); Widget buildDropCapText(String value) { final TextStyle dropStyle = $styles.text.dropCase; diff --git a/lib/ui/screens/timeline/widgets/_event_markers.dart b/lib/ui/screens/timeline/widgets/_event_markers.dart index 00b2721d..615e18ef 100644 --- a/lib/ui/screens/timeline/widgets/_event_markers.dart +++ b/lib/ui/screens/timeline/widgets/_event_markers.dart @@ -3,9 +3,11 @@ part of '../timeline_screen.dart'; /// A vertically aligned stack of dots that represent global events /// The event closest to the [selectedYr] param will be visible selected class _EventMarkers extends StatefulWidget { - const _EventMarkers(this.selectedYr, {Key? key, required this.onEventChanged}) : super(key: key); + const _EventMarkers(this.selectedYr, {Key? key, required this.onEventChanged, required this.onMarkerPressed}) + : super(key: key); final void Function(TimelineEvent? event) onEventChanged; + final void Function(TimelineEvent event) onMarkerPressed; final int selectedYr; @override @@ -62,8 +64,12 @@ class _EventMarkersState extends State<_EventMarkers> { /// Create a marker for each event List markers = timelineLogic.events.map((event) { double offsetY = _calculateOffsetY(event.year); - return _EventMarker(offsetY, - isSelected: event == selectedEvent, semanticLabel: '${event.year}: ${event.description}'); + return _EventMarker( + offsetY, + event: event, + isSelected: event == selectedEvent, + onPressed: widget.onMarkerPressed, + ); }).toList(); /// Stack of fractionally positioned markers @@ -110,11 +116,13 @@ class _EventMarker extends StatelessWidget { this.offset, { Key? key, required this.isSelected, - required this.semanticLabel, + required this.event, + required this.onPressed, }) : super(key: key); final double offset; + final TimelineEvent event; final bool isSelected; - final String semanticLabel; + final void Function(TimelineEvent event) onPressed; @override Widget build(BuildContext context) { @@ -126,8 +134,9 @@ class _EventMarker extends StatelessWidget { height: 0, child: OverflowBox( maxHeight: 30, - child: Semantics( - label: semanticLabel, + child: AppBtn.basic( + semanticLabel: '${event.year}: ${event.description}', + onPressed: () => onPressed(event), child: Container( alignment: Alignment.center, height: 30, diff --git a/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart b/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart index 9d2c90be..4be33802 100644 --- a/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart +++ b/lib/ui/screens/timeline/widgets/_scrolling_viewport.dart @@ -45,6 +45,11 @@ class _ScalingViewportState extends State<_ScrollingViewport> { AppHaptics.selectionClick(); } + void _handleMarkerPressed(event) { + final pos = controller.calculateScrollPosFromYear(event.year); + controller.scroller.animateTo(pos, duration: $styles.times.med, curve: Curves.easeOutBack); + } + @override Widget build(BuildContext context) { return GestureDetector( @@ -132,6 +137,7 @@ class _ScalingViewportState extends State<_ScrollingViewport> { builder: (_, __) => _EventMarkers( controller.calculateYearFromScrollPos(), onEventChanged: _handleEventMarkerChanged, + onMarkerPressed: _handleMarkerPressed, ), ), ],