mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1440-new-buy-sell-flow
# Conflicts: # lib/src/widgets/cake_image_widget.dart
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -13,6 +14,7 @@ class QrImage extends StatelessWidget {
|
||||
this.errorCorrectionLevel = qr.QrErrorCorrectLevel.H,
|
||||
this.embeddedImagePath,
|
||||
this.badgeImageOnEmbeddedImagePath,
|
||||
this.semanticsLabel,
|
||||
});
|
||||
|
||||
final double? size;
|
||||
@@ -24,6 +26,11 @@ class QrImage extends StatelessWidget {
|
||||
final String? embeddedImagePath;
|
||||
final String? badgeImageOnEmbeddedImagePath;
|
||||
|
||||
/// Accessible name announced for the code itself. Defaults to a generic
|
||||
/// localized "QR code" so the code is always discoverable; pass a more
|
||||
/// specific label when the host screen knows what the code encodes.
|
||||
final String? semanticsLabel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imagePath = embeddedImagePath ?? 'assets/images/qr-cake.png';
|
||||
@@ -68,8 +75,13 @@ class QrImage extends StatelessWidget {
|
||||
foregroundColor: foregroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
// The package default is a hardcoded English "qr code"; use the
|
||||
// localized string so the code is announced in the user's language.
|
||||
semanticsLabel: semanticsLabel ?? S.of(context).scan_qr_code,
|
||||
),
|
||||
centerImageToUse,
|
||||
// The embedded logo and its badge are purely decorative — the node
|
||||
// above already announces the code — so keep them out of the tree.
|
||||
ExcludeSemantics(child: centerImageToUse),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
this.alignment,
|
||||
this.allowDrawingOutsideViewBox,
|
||||
this.filterQuality,
|
||||
this.semanticsLabel,
|
||||
});
|
||||
|
||||
final String? imageUrl;
|
||||
@@ -33,6 +34,16 @@ class CakeImageWidget extends StatelessWidget {
|
||||
final double borderRadius;
|
||||
final FilterQuality? filterQuality;
|
||||
|
||||
/// Accessible name for this image.
|
||||
///
|
||||
/// Leave `null` (the default) for decorative imagery — the image then
|
||||
/// contributes nothing at all to the semantics tree, so screen readers do not
|
||||
/// stop on an unnamed node. Pass a localized string only when the image is the
|
||||
/// sole carrier of information for the user.
|
||||
final String? semanticsLabel;
|
||||
|
||||
bool get _isDecorative => semanticsLabel == null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (imageUrl == null || imageUrl!.isEmpty) {
|
||||
@@ -53,6 +64,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
alignment: alignment ?? Alignment.center,
|
||||
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false,
|
||||
colorFilter: effectiveColorFilter,
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
fit: fit ?? BoxFit.contain, errorBuilder: (context, e, trace) {
|
||||
return SvgPicture.asset(
|
||||
imageUrl!,
|
||||
@@ -62,6 +75,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
width: width,
|
||||
errorBuilder: (_, __, ___) => SizedBox(height: height, width: width),
|
||||
colorFilter: effectiveColorFilter,
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
);
|
||||
});
|
||||
@@ -73,6 +88,8 @@ class CakeImageWidget extends StatelessWidget {
|
||||
fit: fit,
|
||||
color: color,
|
||||
filterQuality: filterQuality ?? FilterQuality.medium,
|
||||
semanticLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
);
|
||||
}
|
||||
@@ -86,13 +103,9 @@ class CakeImageWidget extends StatelessWidget {
|
||||
alignment: alignment ?? Alignment.center,
|
||||
allowDrawingOutsideViewBox: allowDrawingOutsideViewBox ?? false,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
placeholderBuilder: (_) {
|
||||
return loadingWidget ??
|
||||
SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator()));
|
||||
},
|
||||
semanticsLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
placeholderBuilder: (_) => _buildLoadingWidget(),
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
)
|
||||
: Image.network(
|
||||
@@ -102,13 +115,11 @@ class CakeImageWidget extends StatelessWidget {
|
||||
fit: fit ?? BoxFit.cover,
|
||||
color: color,
|
||||
filterQuality: filterQuality ?? FilterQuality.medium,
|
||||
semanticLabel: semanticsLabel,
|
||||
excludeFromSemantics: _isDecorative,
|
||||
loadingBuilder: (_, Widget child, ImageChunkEvent? progress) {
|
||||
if (progress == null) return child;
|
||||
return loadingWidget ??
|
||||
SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator()));
|
||||
return _buildLoadingWidget();
|
||||
},
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
);
|
||||
@@ -117,8 +128,16 @@ class CakeImageWidget extends StatelessWidget {
|
||||
return imageWidget;
|
||||
}
|
||||
|
||||
/// A caller-supplied [loadingWidget] owns its own semantics; the built-in
|
||||
/// spinner is purely visual and must not become an unnamed focus stop.
|
||||
Widget _buildLoadingWidget() =>
|
||||
loadingWidget ?? const ExcludeSemantics(child:SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Center(child: CupertinoActivityIndicator())));
|
||||
|
||||
Widget _buildErrorWidget(BuildContext context) {
|
||||
return Container(
|
||||
final Widget placeholder = Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
@@ -134,5 +153,20 @@ class CakeImageWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Several call sites render meaningful content (e.g. the asset's initials) as
|
||||
// their [errorWidget], so leave its semantics to the caller.
|
||||
if (errorWidget != null) {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
return _isDecorative
|
||||
? ExcludeSemantics(child: placeholder)
|
||||
: Semantics(
|
||||
container: true,
|
||||
image: true,
|
||||
label: semanticsLabel,
|
||||
child: ExcludeSemantics(child: placeholder),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ class RoundedCheckbox extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return value
|
||||
// Unchecked renders nothing at all, so without an explicit `checked` state
|
||||
// the selection is invisible to screen readers.
|
||||
final Widget indicator = value
|
||||
? Container(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
@@ -23,5 +25,9 @@ class RoundedCheckbox extends StatelessWidget {
|
||||
size: 14.0,
|
||||
))
|
||||
: Offstage();
|
||||
|
||||
// Deliberately not a semantics container: the state merges into the
|
||||
// enclosing row/option node instead of adding a second focus stop.
|
||||
return Semantics(checked: value, child: indicator);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user