From 4661f6997c2793506b03aca005eabf196ad4a5f5 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 1 Mar 2023 14:32:00 -0700 Subject: [PATCH] Add a FocusTraversalGroup --- .../screens/editorial/editorial_screen.dart | 87 ++++++++++--------- .../wonder_details_tab_menu.dart | 84 +++++++----------- .../wonder_events/widgets/_events_list.dart | 2 +- 3 files changed, 75 insertions(+), 98 deletions(-) diff --git a/lib/ui/screens/editorial/editorial_screen.dart b/lib/ui/screens/editorial/editorial_screen.dart index 9724cab2..98d2ef3e 100644 --- a/lib/ui/screens/editorial/editorial_screen.dart +++ b/lib/ui/screens/editorial/editorial_screen.dart @@ -102,54 +102,55 @@ class _WonderEditorialScreenState extends State { /// Scrolling content - Includes an invisible gap at the top, and then scrolls over the illustration TopCenter( child: SizedBox( - //width: $styles.sizes.maxContentWidth1, - child: CustomScrollView( - primary: false, - controller: _scroller, - scrollBehavior: ScrollConfiguration.of(context).copyWith(), - slivers: [ - /// Invisible padding at the top of the list, so the illustration shows through the btm - SliverToBoxAdapter( - child: SizedBox(height: illustrationHeight), - ), - - /// Text content, animates itself to hide behind the app bar as it scrolls up - SliverToBoxAdapter( - child: ValueListenableBuilder( - valueListenable: _scrollPos, - builder: (_, value, child) { - double offsetAmt = max(0, value * .3); - double opacity = (1 - offsetAmt / 150).clamp(0, 1); - return Transform.translate( - offset: Offset(0, offsetAmt), - child: Opacity(opacity: opacity, child: child), - ); - }, - child: _TitleText(widget.data, scroller: _scroller), + child: FocusTraversalGroup( + child: CustomScrollView( + primary: false, + controller: _scroller, + scrollBehavior: ScrollConfiguration.of(context).copyWith(), + slivers: [ + /// Invisible padding at the top of the list, so the illustration shows through the btm + SliverToBoxAdapter( + child: SizedBox(height: illustrationHeight), ), - ), - /// Collapsing App bar, pins to the top of the list - SliverAppBar( - pinned: true, - collapsedHeight: minAppBarHeight, - toolbarHeight: minAppBarHeight, - expandedHeight: maxAppBarHeight, - backgroundColor: Colors.transparent, - elevation: 0, - leading: SizedBox.shrink(), - flexibleSpace: SizedBox.expand( - child: _AppBar( - widget.data.type, - scrollPos: _scrollPos, - sectionIndex: _sectionIndex, + /// Text content, animates itself to hide behind the app bar as it scrolls up + SliverToBoxAdapter( + child: ValueListenableBuilder( + valueListenable: _scrollPos, + builder: (_, value, child) { + double offsetAmt = max(0, value * .3); + double opacity = (1 - offsetAmt / 150).clamp(0, 1); + return Transform.translate( + offset: Offset(0, offsetAmt), + child: Opacity(opacity: opacity, child: child), + ); + }, + child: _TitleText(widget.data, scroller: _scroller), ), ), - ), - /// Editorial content (text and images) - _ScrollingContent(widget.data, scrollPos: _scrollPos, sectionNotifier: _sectionIndex), - ], + /// Collapsing App bar, pins to the top of the list + SliverAppBar( + pinned: true, + collapsedHeight: minAppBarHeight, + toolbarHeight: minAppBarHeight, + expandedHeight: maxAppBarHeight, + backgroundColor: Colors.transparent, + elevation: 0, + leading: SizedBox.shrink(), + flexibleSpace: SizedBox.expand( + child: _AppBar( + widget.data.type, + scrollPos: _scrollPos, + sectionIndex: _sectionIndex, + ), + ), + ), + + /// Editorial content (text and images) + _ScrollingContent(widget.data, scrollPos: _scrollPos, sectionNotifier: _sectionIndex), + ], + ), ), ), ), diff --git a/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart b/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart index 5c92955a..4b147dcb 100644 --- a/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart +++ b/lib/ui/screens/wonder_details/wonder_details_tab_menu.dart @@ -4,11 +4,7 @@ class WonderDetailsTabMenu extends StatelessWidget { static double bottomPadding = 0; static double buttonInset = 12; - const WonderDetailsTabMenu( - {Key? key, - required this.tabController, - this.showBg = false, - required this.wonderType}) + const WonderDetailsTabMenu({Key? key, required this.tabController, this.showBg = false, required this.wonderType}) : super(key: key); final TabController tabController; @@ -36,10 +32,7 @@ class WonderDetailsTabMenu extends StatelessWidget { ), // Buttons Padding( - padding: EdgeInsets.only( - left: $styles.insets.sm, - right: $styles.insets.xxs, - bottom: bottomPadding), + padding: EdgeInsets.only(left: $styles.insets.sm, right: $styles.insets.xxs, bottom: bottomPadding), // TabButtons are a Stack with a row of icon buttons, and an illustrated home button sitting on top. // The home buttons shows / hides itself based on `showHomeBtn` // The row contains an animated placeholder gap which makes room for the icon as it transitions in. @@ -48,33 +41,27 @@ class WonderDetailsTabMenu extends StatelessWidget { children: [ // Main tab btns + animated gap Center( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - // Home btn - _WonderHomeBtn( - size: homeBtnSize, - wonderType: wonderType, - borderSize: showBg ? 6 : 2, - ), - _TabBtn(0, tabController, - iconImg: 'editorial', - label: $strings.wonderDetailsTabLabelInformation, - color: iconColor), - _TabBtn(1, tabController, - iconImg: 'photos', - label: $strings.wonderDetailsTabLabelImages, - color: iconColor), - _TabBtn(2, tabController, - iconImg: 'artifacts', - label: $strings.wonderDetailsTabLabelArtifacts, - color: iconColor), - _TabBtn(3, tabController, - iconImg: 'timeline', - label: $strings.wonderDetailsTabLabelEvents, - color: iconColor), - ], + child: FocusTraversalGroup( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // Home btn + _WonderHomeBtn( + size: homeBtnSize, + wonderType: wonderType, + borderSize: showBg ? 6 : 2, + ), + _TabBtn(0, tabController, + iconImg: 'editorial', label: $strings.wonderDetailsTabLabelInformation, color: iconColor), + _TabBtn(1, tabController, + iconImg: 'photos', label: $strings.wonderDetailsTabLabelImages, color: iconColor), + _TabBtn(2, tabController, + iconImg: 'artifacts', label: $strings.wonderDetailsTabLabelArtifacts, color: iconColor), + _TabBtn(3, tabController, + iconImg: 'timeline', label: $strings.wonderDetailsTabLabelEvents, color: iconColor), + ], + ), ), ), ], @@ -87,11 +74,7 @@ class WonderDetailsTabMenu extends StatelessWidget { } class _WonderHomeBtn extends StatelessWidget { - const _WonderHomeBtn( - {Key? key, - required this.size, - required this.wonderType, - required this.borderSize}) + const _WonderHomeBtn({Key? key, required this.size, required this.wonderType, required this.borderSize}) : super(key: key); final double size; @@ -113,8 +96,7 @@ class _WonderHomeBtn extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(99), color: wonderType.fgColor, - image: DecorationImage( - image: AssetImage(wonderType.homeBtn), fit: BoxFit.fill), + image: DecorationImage(image: AssetImage(wonderType.homeBtn), fit: BoxFit.fill), ), ), ); @@ -143,12 +125,9 @@ class _TabBtn extends StatelessWidget { @override Widget build(BuildContext context) { bool selected = tabController.index == index; - final MaterialLocalizations localizations = - MaterialLocalizations.of(context); - final iconImgPath = - '${ImagePaths.common}/tab-$iconImg${selected ? '-active' : ''}.png'; - String tabLabel = localizations.tabLabel( - tabIndex: index + 1, tabCount: tabController.length); + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + final iconImgPath = '${ImagePaths.common}/tab-$iconImg${selected ? '-active' : ''}.png'; + String tabLabel = localizations.tabLabel(tabIndex: index + 1, tabCount: tabController.length); tabLabel = '$label: $tabLabel'; final double btnWidth = (context.widthPx / 6).clamp(_minWidth, _maxWidth); return MergeSemantics( @@ -157,15 +136,12 @@ class _TabBtn extends StatelessWidget { label: tabLabel, child: ExcludeSemantics( child: AppBtn.basic( - padding: EdgeInsets.only( - top: $styles.insets.md + $styles.insets.xs, - bottom: $styles.insets.sm), + padding: EdgeInsets.only(top: $styles.insets.md + $styles.insets.xs, bottom: $styles.insets.sm), onPressed: () => tabController.index = index, semanticLabel: label, minimumSize: Size(btnWidth, 0), // Image icon - child: Image.asset(iconImgPath, - height: 32, width: 32, color: selected ? null : color), + child: Image.asset(iconImgPath, height: 32, width: 32, color: selected ? null : color), ), ), ), diff --git a/lib/ui/screens/wonder_events/widgets/_events_list.dart b/lib/ui/screens/wonder_events/widgets/_events_list.dart index 21eeec4b..a04170cf 100644 --- a/lib/ui/screens/wonder_events/widgets/_events_list.dart +++ b/lib/ui/screens/wonder_events/widgets/_events_list.dart @@ -32,7 +32,7 @@ class _EventsListState extends State<_EventsList> { @override Widget build(BuildContext context) { - return widget.blurOnScroll ? _buildScrollingListWithBlur() : _buildScrollingList(); + return FocusTraversalGroup(child: widget.blurOnScroll ? _buildScrollingListWithBlur() : _buildScrollingList()); } /// The actual content of the scrolling list