diff --git a/lib/ui/common/controls/app_page_indicator.dart b/lib/ui/common/controls/app_page_indicator.dart index ba5a19f2..b0167482 100644 --- a/lib/ui/common/controls/app_page_indicator.dart +++ b/lib/ui/common/controls/app_page_indicator.dart @@ -50,6 +50,7 @@ class _AppPageIndicatorState extends State { return Semantics( container: true, liveRegion: true, + readOnly: true, label: StringUtils.supplant($strings.appPageSemanticSwipe, { '{pageTitle}': widget.semanticPageTitle, '{count}': (value % (widget.count) + 1).toString(), diff --git a/lib/ui/common/controls/buttons.dart b/lib/ui/common/controls/buttons.dart index 2cfd61eb..1b071d08 100644 --- a/lib/ui/common/controls/buttons.dart +++ b/lib/ui/common/controls/buttons.dart @@ -138,19 +138,14 @@ class AppBtn extends StatelessWidget { // add press effect: if (pressEffect) button = _ButtonPressEffect(button); - // add semantics: - if (semanticLabel.isEmpty) { - button = ExcludeSemantics(child: button); - } else { - button = Semantics( - label: semanticLabel, - button: true, - container: true, - child: button, - ); - } - - return button; + // add semantics? + if (semanticLabel.isEmpty) return button; + return Semantics( + label: semanticLabel, + button: true, + container: true, + child: ExcludeSemantics(child: button), + ); } } diff --git a/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart b/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart index 1bdc5426..4f5cb9ee 100644 --- a/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart +++ b/lib/ui/screens/artifact/artifact_carousel/artifact_carousel_screen.dart @@ -28,11 +28,13 @@ class _ArtifactScreenState extends State { late PageController _controller; double get _currentOffset { - bool inited = _controller.hasClients && _controller.position.haveDimensions; - return inited ? _controller.page! : _controller.initialPage * 1.0; + bool hasOffset = _controller.hasClients && _controller.position.haveDimensions; + return hasOffset ? _controller.page! : _controller.initialPage * 1.0; } - int get _currentIndex => _currentOffset.round() % _artifacts.length; + final _currentIndex = ValueNotifier(0); + + HighlightData get _currentArtifact => _artifacts[_currentIndex.value]; @override void initState() { @@ -43,9 +45,11 @@ class _ArtifactScreenState extends State { // start at a high offset so we can scroll backwards: initialPage: _artifacts.length * 9999, viewportFraction: 0.5, - ); + )..addListener(_handleCarouselScroll); } + void _handleCarouselScroll() => _currentIndex.value = _currentOffset.round() % _artifacts.length; + @override void dispose() { _controller.dispose(); @@ -74,20 +78,24 @@ class _ArtifactScreenState extends State { Widget build(BuildContext context) { return Container( color: $styles.colors.greyStrong, - child: AnimatedBuilder(animation: _controller, builder: _buildScreen), + child: AnimatedBuilder( + animation: _currentIndex, + builder: (context, __) => _buildScreen(context), + ), ); } - Widget _buildScreen(BuildContext context, _) { + Widget _buildScreen(BuildContext context) { final double w = context.widthPx; final double backdropWidth = w <= _maxElementWidth ? w : min(w * _partialElementWidth, _maxElementWidth); final double backdropHeight = math.min(context.heightPx * 0.65, _maxElementHeight); final bool small = backdropHeight / _maxElementHeight < 0.7; - final HighlightData artifact = _artifacts[_currentIndex]; return Stack( children: [ - Positioned.fill(child: _BlurredImageBg(url: artifact.imageUrl)), + Positioned.fill( + child: _BlurredImageBg(url: _currentArtifact.imageUrl), + ), Column( children: [ SimpleHeader($strings.artifactsTitleArtifacts, showBackBtn: false, isTransparent: true), @@ -113,27 +121,24 @@ class _ArtifactScreenState extends State { child: PageView.builder( controller: _controller, clipBehavior: Clip.none, - itemBuilder: (context, index) { - bool isCurrentIndex = index % _artifacts.length == _currentIndex; - return ExcludeSemantics( - excluding: isCurrentIndex == false, - child: MergeSemantics( - child: Semantics( - onIncrease: () => _handleArtifactTap(_currentIndex + 1), - onDecrease: () => _handleArtifactTap(_currentIndex - 1), - child: _CarouselItem( - index: index, - currentPage: _currentOffset, - artifact: _artifacts[index % _artifacts.length], - bottomPadding: backdropHeight, - maxWidth: backdropWidth, - maxHeight: backdropHeight, - onPressed: () => _handleArtifactTap(index), - ), + itemBuilder: (context, index) => AnimatedBuilder( + animation: _controller, + builder: (_, __) { + bool isCurrentIndex = index % _artifacts.length == _currentIndex.value; + return ExcludeSemantics( + excluding: !isCurrentIndex, + child: _CarouselItem( + index: index, + currentPage: _currentOffset, + artifact: _artifacts[index % _artifacts.length], + bottomPadding: backdropHeight, + maxWidth: backdropWidth, + maxHeight: backdropHeight, + onPressed: () => _handleArtifactTap(index), ), - ), - ); - }, + ); + }, + ), ), ), ), @@ -148,7 +153,7 @@ class _ArtifactScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Gap($styles.insets.md), - _buildContent(context, artifact, backdropWidth, small), + _buildContent(context, backdropWidth, small), Gap(small ? $styles.insets.sm : $styles.insets.md), AppBtn.from( text: $strings.artifactsButtonBrowse, @@ -169,16 +174,20 @@ class _ArtifactScreenState extends State { ); } - Widget _buildContent(BuildContext context, HighlightData artifact, double width, bool small) { - return Container( - width: width, - padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IgnorePointer( - child: ExcludeSemantics( + Widget _buildContent(BuildContext context, double width, bool small) { + return Semantics( + onIncrease: () => _handleArtifactTap(_currentOffset.round() + 1), + onDecrease: () => _handleArtifactTap(_currentOffset.round() - 1), + liveRegion: true, + child: Container( + width: width, + padding: EdgeInsets.symmetric(horizontal: $styles.insets.sm), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IgnorePointer( + ignoringSemantics: false, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -187,7 +196,7 @@ class _ArtifactScreenState extends State { alignment: Alignment.center, child: StaticTextScale( child: Text( - artifact.title, + _currentArtifact.title, overflow: TextOverflow.ellipsis, style: $styles.text.h2.copyWith(color: $styles.colors.black, height: 1.2), textAlign: TextAlign.center, @@ -197,21 +206,21 @@ class _ArtifactScreenState extends State { ), if (!small) Gap($styles.insets.xxs), Text( - artifact.date.isEmpty ? '--' : artifact.date, + _currentArtifact.date.isEmpty ? '--' : _currentArtifact.date, style: $styles.text.body, textAlign: TextAlign.center, ), ], - ).animate(key: ValueKey(artifact.artifactId)).fadeIn(), + ).animate(key: ValueKey(_currentArtifact.artifactId)).fadeIn(), ), - ), - Gap(small ? $styles.insets.xs : $styles.insets.sm), - AppPageIndicator( - count: _artifacts.length, - controller: _controller, - semanticPageTitle: $strings.artifactsSemanticArtifact, - ), - ], + Gap(small ? $styles.insets.xs : $styles.insets.sm), + AppPageIndicator( + count: _artifacts.length, + controller: _controller, + semanticPageTitle: $strings.artifactsSemanticArtifact, + ), + ], + ), ), ); } diff --git a/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart b/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart index 35e5e999..0241bde7 100644 --- a/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart +++ b/lib/ui/screens/artifact/artifact_carousel/widgets/_carousel_item.dart @@ -22,7 +22,7 @@ class _CarouselItem extends StatelessWidget { @override Widget build(BuildContext context) { return AppBtn.basic( - semanticLabel: '${artifact.title} ${artifact.date}', + semanticLabel: 'Explore artifact details', onPressed: onPressed, pressEffect: false, child: _ImagePreview( diff --git a/lib/ui/screens/editorial/editorial_screen.dart b/lib/ui/screens/editorial/editorial_screen.dart index d7d5dcf5..c86f5451 100644 --- a/lib/ui/screens/editorial/editorial_screen.dart +++ b/lib/ui/screens/editorial/editorial_screen.dart @@ -116,7 +116,7 @@ class _WonderEditorialScreenState extends State { CustomScrollView( primary: false, controller: _scroller, - cacheExtent: 500, + cacheExtent: 1000, slivers: [ /// Invisible padding at the top of the list, so the illustration shows through the btm SliverToBoxAdapter( diff --git a/lib/ui/screens/editorial/widgets/_scrolling_content.dart b/lib/ui/screens/editorial/widgets/_scrolling_content.dart index de320a02..92e6cf69 100644 --- a/lib/ui/screens/editorial/widgets/_scrolling_content.dart +++ b/lib/ui/screens/editorial/widgets/_scrolling_content.dart @@ -28,35 +28,37 @@ class _ScrollingContent extends StatelessWidget { final String dropChar = value.substring(0, 1); final textScale = MediaQuery.of(context).textScaleFactor; final double dropCapWidth = StringUtils.measure(dropChar, dropStyle).width * textScale; - final bool skipCaps = !localeLogic.isEnglish || MediaQuery.of(context).accessibleNavigation; + final bool skipCaps = !localeLogic.isEnglish; return Semantics( label: value, - child: !skipCaps - ? DropCapText( - _fixNewlines(value).substring(1), - dropCap: DropCap( - width: dropCapWidth, - height: $styles.text.body.fontSize! * $styles.text.body.height! * 2, - child: Transform.translate( - offset: Offset(0, bodyStyle.fontSize! * (bodyStyle.height! - 1) - 2), - child: Text( - dropChar, - overflow: TextOverflow.visible, - style: $styles.text.dropCase.copyWith( - color: $styles.colors.accent1, - height: 1, + child: ExcludeSemantics( + child: !skipCaps + ? DropCapText( + _fixNewlines(value).substring(1), + dropCap: DropCap( + width: dropCapWidth, + height: $styles.text.body.fontSize! * $styles.text.body.height! * 2, + child: Transform.translate( + offset: Offset(0, bodyStyle.fontSize! * (bodyStyle.height! - 1) - 2), + child: Text( + dropChar, + overflow: TextOverflow.visible, + style: $styles.text.dropCase.copyWith( + color: $styles.colors.accent1, + height: 1, + ), ), ), ), - ), - style: $styles.text.body, - dropCapPadding: EdgeInsets.only(right: 6), - dropCapStyle: $styles.text.dropCase.copyWith( - color: $styles.colors.accent1, - height: 1, - ), - ) - : Text(value, style: bodyStyle), + style: $styles.text.body, + dropCapPadding: EdgeInsets.only(right: 6), + dropCapStyle: $styles.text.dropCase.copyWith( + color: $styles.colors.accent1, + height: 1, + ), + ) + : Text(value, style: bodyStyle), + ), ); } diff --git a/lib/ui/screens/home/widgets/_animated_arrow_button.dart b/lib/ui/screens/home/widgets/_animated_arrow_button.dart index 54de2f42..95e86309 100644 --- a/lib/ui/screens/home/widgets/_animated_arrow_button.dart +++ b/lib/ui/screens/home/widgets/_animated_arrow_button.dart @@ -28,7 +28,7 @@ class _AnimatedArrowButton extends StatelessWidget { onPressed: onTap, child: SizedBox( height: 80, - width: 50, + width: 250, child: Animate( effects: [ CustomEffect(builder: _buildOpacityTween, duration: duration, curve: Curves.easeOut), diff --git a/lib/ui/screens/home/wonders_home_screen.dart b/lib/ui/screens/home/wonders_home_screen.dart index d78d8041..41c8843d 100644 --- a/lib/ui/screens/home/wonders_home_screen.dart +++ b/lib/ui/screens/home/wonders_home_screen.dart @@ -127,11 +127,10 @@ class _HomeScreenState extends State with SingleTickerProviderStateM /// Wonders Illustrations MergeSemantics( child: Semantics( - header: true, - image: true, liveRegion: true, onIncrease: () => _setPageIndex(_wonderIndex + 1), onDecrease: () => _setPageIndex(_wonderIndex - 1), + onTap: () => _showDetailsPage(), child: PageView.builder( controller: _pageController, onPageChanged: _handlePageViewChanged, @@ -170,29 +169,29 @@ class _HomeScreenState extends State with SingleTickerProviderStateM LightText( child: IgnorePointer( ignoringSemantics: false, - child: MergeSemantics( - child: Transform.translate( - offset: Offset(0, 30), - child: Column( - children: [ + child: Transform.translate( + offset: Offset(0, 30), + child: Column( + children: [ + ExcludeSemantics( // Hide the title when the menu is open for visual polish - AnimatedOpacity( + child: AnimatedOpacity( opacity: _isMenuOpen ? 0 : 1, duration: $styles.times.fast, child: WonderTitleText(currentWonder, enableShadows: true), ), - Gap($styles.insets.md), - AppPageIndicator( - count: _numWonders, - controller: _pageController, - color: $styles.colors.white, - dotSize: 8, - onDotPressed: _handlePageIndicatorDotPressed, - semanticPageTitle: $strings.homeSemanticWonder, - ), - Gap($styles.insets.md), - ], - ), + ), + Gap($styles.insets.md), + AppPageIndicator( + count: _numWonders, + controller: _pageController, + color: $styles.colors.white, + dotSize: 8, + onDotPressed: _handlePageIndicatorDotPressed, + semanticPageTitle: $strings.homeSemanticWonder, + ), + Gap($styles.insets.md), + ], ), ), ), @@ -200,24 +199,22 @@ class _HomeScreenState extends State with SingleTickerProviderStateM /// Animated arrow and background /// Wrap in a container that is full-width to make it easier to find for screen readers - MergeSemantics( - child: Container( - width: double.infinity, - alignment: Alignment.center, + Container( + width: double.infinity, + alignment: Alignment.center, - /// Lose state of child objects when index changes, this will re-run all the animated switcher and the arrow anim - key: ValueKey(_wonderIndex), - child: Stack( - children: [ - /// Expanding rounded rect that grows in height as user swipes up - Positioned.fill( - child: _buildSwipeableArrowBg(), - ), + /// Lose state of child objects when index changes, this will re-run all the animated switcher and the arrow anim + key: ValueKey(_wonderIndex), + child: Stack( + children: [ + /// Expanding rounded rect that grows in height as user swipes up + Positioned.fill( + child: _buildSwipeableArrowBg(), + ), - /// Arrow Btn that fades in and out - _AnimatedArrowButton(onTap: _showDetailsPage, semanticTitle: currentWonder.title), - ], - ), + /// Arrow Btn that fades in and out + _AnimatedArrowButton(onTap: _showDetailsPage, semanticTitle: currentWonder.title), + ], ), ), Gap($styles.insets.md), @@ -233,14 +230,11 @@ class _HomeScreenState extends State with SingleTickerProviderStateM duration: $styles.times.fast, opacity: _isMenuOpen ? 0 : 1, child: MergeSemantics( - child: Semantics( - sortKey: OrdinalSortKey(0), - child: CircleIconBtn( - icon: AppIcons.menu, - onPressed: _handleOpenMenuPressed, - semanticLabel: $strings.homeSemanticOpenMain, - ).safe(), - ), + child: CircleIconBtn( + icon: AppIcons.menu, + onPressed: _handleOpenMenuPressed, + semanticLabel: $strings.homeSemanticOpenMain, + ).safe(), ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 90158145..41a4e88b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -35,7 +35,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.3.1" + version: "3.3.0" args: dependency: transitive description: @@ -238,7 +238,7 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.4" + version: "6.1.2" fixnum: dependency: transitive description: @@ -588,7 +588,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.1.5" meta: dependency: transitive description: @@ -978,7 +978,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.9.1" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -1027,7 +1027,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.13" + version: "0.4.12" timing: dependency: transitive description: @@ -1125,14 +1125,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.2" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "9.3.0" + version: "9.0.0" watcher: dependency: transitive description: @@ -1190,5 +1190,5 @@ packages: source: hosted version: "2.3.0" sdks: - dart: ">=2.18.0-146.0.dev <3.0.0" + dart: ">=2.17.1 <3.0.0" flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 383a81ae..f6547b2b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: wonders description: Explore the famous wonders of the world. publish_to: "none" -version: 1.9.4 +version: 1.9.5 environment: sdk: ">=2.16.0 <3.0.0" diff --git a/release_notes.txt b/release_notes.txt index 25763d25..55414a07 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,3 +1,8 @@ +# 1.9.5 +- Improved a11y for Home Screen and Artifact Carousel +- Polished collection icons +- Added hero support to fullscreen Artifact Details + # 1.9.4 - Fix tappable arrow on home page - Reduce text size for collapsing pull quote