implemented comprehensive accessibility (A11y) support across the app

This commit is contained in:
2026-07-13 23:41:47 +02:00
parent 8274dd46cd
commit 478f0ff20b
46 changed files with 590 additions and 226 deletions
+65 -60
View File
@@ -9,6 +9,7 @@ import '../../../../share_intent/remote_file_ref.dart';
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
import '../../../../utils/downloads/download_job.dart';
import '../../../../utils/haptics.dart';
import '../../../../widget/a11y/a11y_labels.dart';
import '../../../../widget/demo_restricted.dart';
import '../../../../widget/downloads/download_trigger.dart';
import '../data/chat_bubble_styles.dart';
@@ -120,9 +121,7 @@ class _ChatBubbleState extends State<ChatBubble>
if (!_rendersAsCommentBubble) {
base = styles.getSystemStyle();
} else {
base = widget.isSender
? styles.getSelfStyle()
: styles.getRemoteStyle();
base = widget.isSender ? styles.getSelfStyle() : styles.getRemoteStyle();
}
switch (widget.matchHighlight) {
case SearchHighlight.none:
@@ -130,7 +129,9 @@ class _ChatBubbleState extends State<ChatBubble>
case SearchHighlight.secondary:
return base.copyWith(
borderWidth: 1.5,
borderColor: Theme.of(context).colorScheme.primary.withValues(alpha: 0.45),
borderColor: Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.45),
);
case SearchHighlight.active:
return base.copyWith(
@@ -342,68 +343,72 @@ class _BubbleContent extends StatelessWidget {
});
@override
Widget build(BuildContext context) => Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.9,
minWidth: showActorDisplayName
? actorText.size.width
: timeText.size.width + (isSender ? spacing + timeIconSize : 0) + 3,
),
child: Stack(
children: [
if (showActorDisplayName) Positioned(top: 0, left: 0, child: actorWidget),
Padding(
padding: EdgeInsets.only(
bottom: showBubbleTime ? 18 : 0,
top: showActorDisplayName ? 18 : 0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (parent != null &&
bubbleData.messageType ==
GetRoomResponseObjectMessageType.comment) ...[
AnswerReference(
referenceMessage: parent!,
selfId: selfId,
),
const SizedBox(height: 5),
],
messageWidget,
],
),
),
if (showBubbleTime)
Positioned(
bottom: 0,
right: 0,
child: Row(
Widget build(BuildContext context) => MergeSemantics(
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 0.9,
minWidth: showActorDisplayName
? actorText.size.width
: timeText.size.width + (isSender ? spacing + timeIconSize : 0) + 3,
),
child: Stack(
children: [
if (showActorDisplayName)
Positioned(top: 0, left: 0, child: actorWidget),
Padding(
padding: EdgeInsets.only(
bottom: showBubbleTime ? 18 : 0,
top: showActorDisplayName ? 18 : 0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
timeText,
if (isSender) ...[
SizedBox(width: spacing),
Icon(
isRead ? Icons.done_all_outlined : Icons.done_outlined,
size: timeIconSize,
color: timeIconColor,
),
if (parent != null &&
bubbleData.messageType ==
GetRoomResponseObjectMessageType.comment) ...[
AnswerReference(referenceMessage: parent!, selfId: selfId),
const SizedBox(height: 5),
],
messageWidget,
],
),
),
if (downloadJob?.status.value is DownloadInProgress)
Positioned(
bottom: 0,
right: 0,
left: 0,
child: LinearProgressIndicator(
value: () {
final s = downloadJob!.status.value as DownloadInProgress;
return s.percent <= 0 ? null : s.percent / 100;
}(),
if (showBubbleTime)
Positioned(
bottom: 0,
right: 0,
child: Row(
children: [
timeText,
if (isSender) ...[
SizedBox(width: spacing),
Icon(
isRead ? Icons.done_all_outlined : Icons.done_outlined,
size: timeIconSize,
color: timeIconColor,
semanticLabel: isRead
? A11yLabels.messageRead
: A11yLabels.messageSent,
),
],
],
),
),
),
],
if (downloadJob?.status.value is DownloadInProgress)
Positioned(
bottom: 0,
right: 0,
left: 0,
child: LinearProgressIndicator(
semanticsLabel: A11yLabels.downloadingFile,
value: () {
final s = downloadJob!.status.value as DownloadInProgress;
return s.percent <= 0 ? null : s.percent / 100;
}(),
),
),
],
),
),
);
}