Polish home page and carousel view

Tweak implementation of AppBtn semantics
This commit is contained in:
Shawn 2022-09-19 14:21:30 -06:00
parent 44bd0d15fa
commit 662925cbfa
11 changed files with 149 additions and 143 deletions

View File

@ -50,6 +50,7 @@ class _AppPageIndicatorState extends State<AppPageIndicator> {
return Semantics(
container: true,
liveRegion: true,
readOnly: true,
label: StringUtils.supplant($strings.appPageSemanticSwipe, {
'{pageTitle}': widget.semanticPageTitle,
'{count}': (value % (widget.count) + 1).toString(),

View File

@ -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),
);
}
}

View File

@ -28,11 +28,13 @@ class _ArtifactScreenState extends State<ArtifactCarouselScreen> {
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<ArtifactCarouselScreen> {
// 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<ArtifactCarouselScreen> {
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<ArtifactCarouselScreen> {
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<ArtifactCarouselScreen> {
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<ArtifactCarouselScreen> {
);
}
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<ArtifactCarouselScreen> {
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<ArtifactCarouselScreen> {
),
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,
),
],
),
),
);
}

View File

@ -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(

View File

@ -116,7 +116,7 @@ class _WonderEditorialScreenState extends State<WonderEditorialScreen> {
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(

View File

@ -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),
),
);
}

View File

@ -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),

View File

@ -127,11 +127,10 @@ class _HomeScreenState extends State<HomeScreen> 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<HomeScreen> 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<HomeScreen> 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<HomeScreen> 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(),
),
),
),

View File

@ -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"

View File

@ -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"

View File

@ -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