Cw 304 enhance talkback (#887)
* fix(#536): add talkback support to missing main and common elements * fix(#564): add talkback support for slidable node items & addresses page * fix: add missing delete button from add pin widget
Rafael Saes committed
Apr 18, 2023 at 17:36 UTC
5ad67b62a5ebc97be70e91727ab9801be61d8a3f
8 files changed
+231
-227
lib/src/screens/base_page.dart
+17
-12
@@ -58,19 +58,24 @@ abstract class BasePage extends StatelessWidget {
58
59
bool isMobileView = ResponsiveLayoutUtil.instance.isMobile(context);
60
61
- return SizedBox(
62
- height: isMobileView ? 37 : 45,
63
- width: isMobileView ? 37 : 45,
64
- child: ButtonTheme(
65
- minWidth: double.minPositive,
66
- child: TextButton(
67
- style: ButtonStyle(
68
- overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
61
+ return MergeSemantics(
62
+ child: SizedBox(
63
+ height: isMobileView ? 37 : 45,
64
+ width: isMobileView ? 37 : 45,
65
+ child: ButtonTheme(
66
+ minWidth: double.minPositive,
67
+ child: Semantics(
68
+ label: canUseCloseIcon && !isMobileView ? 'Close' : 'Back',
69
+ child: TextButton(
70
+ style: ButtonStyle(
71
+ overlayColor: MaterialStateColor.resolveWith(
72
+ (states) => Colors.transparent),
73
+ ),
74
+ onPressed: () => onClose(context),
75
+ child:
76
+ canUseCloseIcon && !isMobileView ? _closeButton : _backButton,
77
),
70
- onPressed: () => onClose(context),
71
- child: canUseCloseIcon && !isMobileView
72
- ? _closeButton
73
- : _backButton,
78
+ ),
79
),
80
),
81
);
lib/src/screens/dashboard/dashboard_page.dart
+55
-36
@@ -104,7 +104,7 @@ class _DashboardPageView extends BasePage {
104
//splashColor: Colors.transparent,
105
//padding: EdgeInsets.all(0),
106
onPressed: () => onOpenEndDrawer(),
107
- child: menuButton));
107
+ child: Semantics(label: 'Menu', child: menuButton)));
108
}
109
110
final DashboardViewModel dashboardViewModel;
@@ -149,17 +149,21 @@ class _DashboardPageView extends BasePage {
149
Padding(
150
padding: EdgeInsets.only(bottom: 24, top: 10),
151
child: Observer(builder: (context) {
152
- return SmoothPageIndicator(
153
- controller: controller,
154
- count: pages.length,
155
- effect: ColorTransitionEffect(
156
- spacing: 6.0,
157
- radius: 6.0,
158
- dotWidth: 6.0,
159
- dotHeight: 6.0,
160
- dotColor: Theme.of(context).indicatorColor,
161
- activeDotColor:
162
- Theme.of(context).accentTextTheme!.headline4!.backgroundColor!),
152
+ return ExcludeSemantics(
153
+ child: SmoothPageIndicator(
154
+ controller: controller,
155
+ count: pages.length,
156
+ effect: ColorTransitionEffect(
157
+ spacing: 6.0,
158
+ radius: 6.0,
159
+ dotWidth: 6.0,
160
+ dotHeight: 6.0,
161
+ dotColor: Theme.of(context).indicatorColor,
162
+ activeDotColor: Theme.of(context)
163
+ .accentTextTheme!
164
+ .headline4!
165
+ .backgroundColor!),
166
+ ),
167
);
168
}
169
)),
@@ -184,27 +188,38 @@ class _DashboardPageView extends BasePage {
188
mainAxisAlignment: MainAxisAlignment.spaceBetween,
189
children: MainActions.all
190
.where((element) => element.canShow?.call(dashboardViewModel) ?? true)
187
- .map((action) => ActionButton(
188
- image: Image.asset(action.image,
189
- height: 24,
190
- width: 24,
191
- color: action.isEnabled?.call(dashboardViewModel) ?? true
192
- ? Theme.of(context)
193
- .accentTextTheme
194
- .headline2!
195
- .backgroundColor!
196
- : Theme.of(context)
197
- .accentTextTheme
198
- .headline3!
199
- .backgroundColor!),
200
- title: action.name(context),
201
- onClick: () async => await action.onTap(context, dashboardViewModel),
202
- textColor: action.isEnabled?.call(dashboardViewModel) ?? true
203
- ? null
204
- : Theme.of(context)
205
- .accentTextTheme
206
- .headline3!
207
- .backgroundColor!,
191
+ .map((action) => Semantics(
192
+ button: true,
193
+ enabled: (action.isEnabled
194
+ ?.call(dashboardViewModel) ??
195
+ true),
196
+ child: ActionButton(
197
+ image: Image.asset(action.image,
198
+ height: 24,
199
+ width: 24,
200
+ color: action.isEnabled?.call(
201
+ dashboardViewModel) ??
202
+ true
203
+ ? Theme.of(context)
204
+ .accentTextTheme
205
+ .headline2!
206
+ .backgroundColor!
207
+ : Theme.of(context)
208
+ .accentTextTheme
209
+ .headline3!
210
+ .backgroundColor!),
211
+ title: action.name(context),
212
+ onClick: () async => await action.onTap(
213
+ context, dashboardViewModel),
214
+ textColor: action.isEnabled
215
+ ?.call(dashboardViewModel) ??
216
+ true
217
+ ? null
218
+ : Theme.of(context)
219
+ .accentTextTheme
220
+ .headline3!
221
+ .backgroundColor!,
222
+ ),
223
))
224
.toList(),
225
),
@@ -222,10 +237,14 @@ class _DashboardPageView extends BasePage {
237
return;
238
}
239
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
225
- pages.add(MarketPlacePage(dashboardViewModel: dashboardViewModel));
240
+ pages.add(Semantics(
241
+ label: 'Marketplace Page',
242
+ child: MarketPlacePage(dashboardViewModel: dashboardViewModel)));
243
}
227
- pages.add(balancePage);
228
- pages.add(TransactionsPage(dashboardViewModel: dashboardViewModel));
244
+ pages.add(Semantics(label: 'Balance Page', child: balancePage));
245
+ pages.add(Semantics(
246
+ label: 'Transactions Page',
247
+ child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
248
_isEffectsInstalled = true;
249
250
autorun((_) async {
lib/src/screens/new_wallet/new_wallet_page.dart
+29
-26
@@ -139,32 +139,35 @@ class _WalletNameFormState extends State<WalletNameForm> {
139
.decorationColor!,
140
width: 1.0),
141
),
142
- suffixIcon: IconButton(
143
- onPressed: () async {
144
- final rName = await generateName();
145
- FocusManager.instance.primaryFocus?.unfocus();
146
-
147
- setState(() {
148
- _controller.text = rName;
149
- _walletNewVM.name = rName;
150
- _controller.selection = TextSelection.fromPosition(
151
- TextPosition(offset: _controller.text.length));
152
- });
153
- },
154
- icon: Container(
155
- padding: const EdgeInsets.all(8),
156
- decoration: BoxDecoration(
157
- borderRadius: BorderRadius.circular(6.0),
158
- color: Theme.of(context).hintColor,
159
- ),
160
- width: 34,
161
- height: 34,
162
- child: Image.asset(
163
- 'assets/images/refresh_icon.png',
164
- color: Theme.of(context)
165
- .primaryTextTheme!
166
- .headline4!
167
- .decorationColor!,
142
+ suffixIcon: Semantics(
143
+ label: 'Generate Name',
144
+ child: IconButton(
145
+ onPressed: () async {
146
+ final rName = await generateName();
147
+ FocusManager.instance.primaryFocus?.unfocus();
148
+
149
+ setState(() {
150
+ _controller.text = rName;
151
+ _walletNewVM.name = rName;
152
+ _controller.selection = TextSelection.fromPosition(
153
+ TextPosition(offset: _controller.text.length));
154
+ });
155
+ },
156
+ icon: Container(
157
+ padding: const EdgeInsets.all(8),
158
+ decoration: BoxDecoration(
159
+ borderRadius: BorderRadius.circular(6.0),
160
+ color: Theme.of(context).hintColor,
161
+ ),
162
+ width: 34,
163
+ height: 34,
164
+ child: Image.asset(
165
+ 'assets/images/refresh_icon.png',
166
+ color: Theme.of(context)
167
+ .primaryTextTheme!
168
+ .headline4!
169
+ .decorationColor!,
170
+ ),
171
),
172
),
173
),
lib/src/screens/pin_code/pin_code_widget.dart
+16
-45
@@ -208,58 +208,29 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
208
const double marginLeft = 15;
209
210
if (index == 9) {
211
+ // Empty container
212
return Container(
213
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
213
- child: TextButton(
214
- onPressed: () => null,
215
- // (widget.hasLengthSwitcher ||
216
- // !settingsStore
217
- // .allowBiometricalAuthentication)
218
- // ? null
219
- // : () {
220
- // FIXME
221
- // if (authStore != null) {
222
- // WidgetsBinding.instance.addPostFrameCallback((_) {
223
- // final biometricAuth = BiometricAuth();
224
- // biometricAuth.isAuthenticated().then(
225
- // (isAuth) {
226
- // if (isAuth) {
227
- // authStore.biometricAuth();
228
- // _key.currentState.showSnackBar(
229
- // SnackBar(
230
- // content: Text(S.of(context).authenticated),
231
- // backgroundColor: Colors.green,
232
- // ),
233
- // );
234
- // }
235
- // }
236
- // );
237
- // });
238
- // }
239
- // },
240
- // FIX-ME: Style
241
- //color: Theme.of(context).backgroundColor,
242
- //shape: CircleBorder(),
243
- child: Container()
244
- // (widget.hasLengthSwitcher ||
245
- // !settingsStore
246
- // .allowBiometricalAuthentication)
247
- // ? Offstage()
248
- // : faceImage,
249
- ),
214
);
215
} else if (index == 10) {
216
index = 0;
217
} else if (index == 11) {
254
- return Container(
255
- margin: EdgeInsets.only(left: marginLeft, right: marginRight),
256
- child: TextButton(
257
- onPressed: () => _pop(),
258
- style: TextButton.styleFrom(
259
- backgroundColor: Theme.of(context).backgroundColor,
260
- shape: CircleBorder(),
218
+ return MergeSemantics(
219
+ child: Container(
220
+ margin: EdgeInsets.only(left: marginLeft, right: marginRight),
221
+ child: Semantics(
222
+ label: 'Delete',
223
+ button: true,
224
+ onTap: () => _pop(),
225
+ child: TextButton(
226
+ onPressed: () => _pop(),
227
+ style: TextButton.styleFrom(
228
+ backgroundColor: Theme.of(context).backgroundColor,
229
+ shape: CircleBorder(),
230
+ ),
231
+ child: deleteIconImage,
232
+ ),
233
),
262
- child: deleteIconImage,
234
),
235
);
236
} else {
lib/src/screens/receive/receive_page.dart
+17
-33
@@ -41,26 +41,7 @@ class ReceivePage extends BasePage {
41
final FocusNode _cryptoAmountFocus;
42
43
@override
44
- Widget leading(BuildContext context) {
45
- final _backButton = Icon(Icons.arrow_back_ios,
46
- color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
47
- size: 16,);
48
-
49
- return SizedBox(
50
- height: 37,
51
- width: 37,
52
- child: ButtonTheme(
53
- minWidth: double.minPositive,
54
- child: TextButton(
55
- // FIX-ME: Style
56
- //highlightColor: Colors.transparent,
57
- //splashColor: Colors.transparent,
58
- //padding: EdgeInsets.all(0),
59
- onPressed: () => onClose(context),
60
- child: _backButton),
61
- ),
62
- );
63
- }
44
+ Color get titleColor => Colors.white;
45
46
@override
47
Widget middle(BuildContext context) {
@@ -93,19 +74,22 @@ class ReceivePage extends BasePage {
74
75
return Material(
76
color: Colors.transparent,
96
- child: IconButton(
97
- padding: EdgeInsets.zero,
98
- constraints: BoxConstraints(),
99
- highlightColor: Colors.transparent,
100
- splashColor: Colors.transparent,
101
- iconSize: 25,
102
- onPressed: () {
103
- ShareUtil.share(
104
- text: addressListViewModel.address.address,
105
- context: context,
106
- );
107
- },
108
- icon: shareImage
77
+ child: Semantics(
78
+ label: 'Share',
79
+ child: IconButton(
80
+ padding: EdgeInsets.zero,
81
+ constraints: BoxConstraints(),
82
+ highlightColor: Colors.transparent,
83
+ splashColor: Colors.transparent,
84
+ iconSize: 25,
85
+ onPressed: () {
86
+ ShareUtil.share(
87
+ text: addressListViewModel.address.address,
88
+ context: context,
89
+ );
90
+ },
91
+ icon: shareImage
92
+ ),
93
)
94
);
95
}
lib/src/screens/receive/widgets/address_cell.dart
+10
-5
@@ -70,11 +70,16 @@ class AddressCell extends StatelessWidget {
70
),
71
),
72
));
73
- return Slidable(
74
- key: Key(address),
75
- startActionPane: _actionPane(context),
76
- endActionPane: _actionPane(context),
77
- child: cell,
73
+ return Semantics(
74
+ label: 'Slidable',
75
+ selected: isCurrent,
76
+ enabled: !isCurrent,
77
+ child: Slidable(
78
+ key: Key(address),
79
+ startActionPane: _actionPane(context),
80
+ endActionPane: _actionPane(context),
81
+ child: cell,
82
+ ),
83
);
84
}
85
lib/src/screens/settings/connection_sync_page.dart
+39
-27
@@ -41,9 +41,13 @@ class ConnectionSyncPage extends BasePage {
41
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
42
),
43
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
44
- NodeHeaderListRow(
45
- title: S.of(context).add_new_node,
46
- onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
44
+ Semantics(
45
+ button: true,
46
+ child: NodeHeaderListRow(
47
+ title: S.of(context).add_new_node,
48
+ onTap: (_) async =>
49
+ await Navigator.of(context).pushNamed(Routes.newNode),
50
+ ),
51
),
52
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
53
SizedBox(height: 100),
@@ -60,31 +64,39 @@ class ConnectionSyncPage extends BasePage {
64
itemBuilder: (_, sectionIndex, index) {
65
final node = nodeListViewModel.nodes[index];
66
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
63
- final nodeListRow = NodeListRow(
64
- title: node.uriRaw,
65
- isSelected: isSelected,
66
- isAlive: node.requestNode(),
67
- onTap: (_) async {
68
- if (isSelected) {
69
- return;
70
- }
67
+ final nodeListRow = Semantics(
68
+ label: 'Slidable',
69
+ selected: isSelected,
70
+ enabled: !isSelected,
71
+ child: NodeListRow(
72
+ title: node.uriRaw,
73
+ isSelected: isSelected,
74
+ isAlive: node.requestNode(),
75
+ onTap: (_) async {
76
+ if (isSelected) {
77
+ return;
78
+ }
79
72
- await showPopUp<void>(
73
- context: context,
74
- builder: (BuildContext context) {
75
- return AlertWithTwoActions(
76
- alertTitle: S.of(context).change_current_node_title,
77
- alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
78
- leftButtonText: S.of(context).cancel,
79
- rightButtonText: S.of(context).change,
80
- actionLeftButton: () => Navigator.of(context).pop(),
81
- actionRightButton: () async {
82
- await nodeListViewModel.setAsCurrent(node);
83
- Navigator.of(context).pop();
84
- },
85
- );
86
- });
87
- },
80
+ await showPopUp<void>(
81
+ context: context,
82
+ builder: (BuildContext context) {
83
+ return AlertWithTwoActions(
84
+ alertTitle:
85
+ S.of(context).change_current_node_title,
86
+ alertContent: nodeListViewModel
87
+ .getAlertContent(node.uriRaw),
88
+ leftButtonText: S.of(context).cancel,
89
+ rightButtonText: S.of(context).change,
90
+ actionLeftButton: () =>
91
+ Navigator.of(context).pop(),
92
+ actionRightButton: () async {
93
+ await nodeListViewModel.setAsCurrent(node);
94
+ Navigator.of(context).pop();
95
+ },
96
+ );
97
+ });
98
+ },
99
+ ),
100
);
101
102
final dismissibleRow = Slidable(
lib/src/widgets/introducing_card.dart
+48
-43
@@ -33,54 +33,59 @@ class IntroducingCard extends StatelessWidget {
33
children: [
34
Expanded(
35
flex: 1,
36
- child: Padding(
37
- padding: const EdgeInsets.all(24),
38
- child: Column(
39
- crossAxisAlignment: CrossAxisAlignment.start,
40
- children: [
41
- AutoSizeText(title ?? '',
42
- style: TextStyle(
43
- fontSize: 24,
44
- fontFamily: 'Lato',
45
- fontWeight: FontWeight.bold,
46
- color: Theme.of(context)
47
- .accentTextTheme!
48
- .headline2!
49
- .backgroundColor!,
50
- height: 1),
51
- maxLines: 1,
52
- textAlign: TextAlign.center),
53
- SizedBox(height: 14),
54
- Text(subTitle ?? '',
55
- textAlign: TextAlign.left,
56
- style: TextStyle(
57
- fontSize: 12,
58
- fontFamily: 'Lato',
59
- color: Theme.of(context)
60
- .accentTextTheme!
61
- .headline2!
62
- .backgroundColor!,
63
- height: 1)),
64
- ],
36
+ child: MergeSemantics(
37
+ child: Padding(
38
+ padding: const EdgeInsets.all(24),
39
+ child: Column(
40
+ crossAxisAlignment: CrossAxisAlignment.start,
41
+ children: [
42
+ AutoSizeText(title ?? '',
43
+ style: TextStyle(
44
+ fontSize: 24,
45
+ fontFamily: 'Lato',
46
+ fontWeight: FontWeight.bold,
47
+ color: Theme.of(context)
48
+ .accentTextTheme!
49
+ .headline2!
50
+ .backgroundColor!,
51
+ height: 1),
52
+ maxLines: 1,
53
+ textAlign: TextAlign.center),
54
+ SizedBox(height: 14),
55
+ Text(subTitle ?? '',
56
+ textAlign: TextAlign.left,
57
+ style: TextStyle(
58
+ fontSize: 12,
59
+ fontFamily: 'Lato',
60
+ color: Theme.of(context)
61
+ .accentTextTheme!
62
+ .headline2!
63
+ .backgroundColor!,
64
+ height: 1)),
65
+ ],
66
+ ),
67
),
68
),
69
),
70
Padding(
71
padding: const EdgeInsets.fromLTRB(0,16,16,0),
70
- child: GestureDetector(
71
- onTap: closeCard,
72
- child: Container(
73
- height: 23,
74
- width: 23,
75
- decoration: BoxDecoration(
76
- color: Colors.white, shape: BoxShape.circle),
77
- child: Center(
78
- child: Image.asset(
79
- 'assets/images/x.png',
80
- color: Palette.darkBlueCraiola,
81
- height: 15,
82
- width: 15,
83
- )),
72
+ child: Semantics(
73
+ label: 'Close',
74
+ child: GestureDetector(
75
+ onTap: closeCard,
76
+ child: Container(
77
+ height: 23,
78
+ width: 23,
79
+ decoration: BoxDecoration(
80
+ color: Colors.white, shape: BoxShape.circle),
81
+ child: Center(
82
+ child: Image.asset(
83
+ 'assets/images/x.png',
84
+ color: Palette.darkBlueCraiola,
85
+ height: 15,
86
+ width: 15,
87
+ )),
88
+ ),
89
),
90
),
91
)