-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(digital-guide): add accessibility modes dialog
- Loading branch information
1 parent
8075640
commit f38e00d
Showing
10 changed files
with
376 additions
and
2 deletions.
There are no files selected for viewing
12 changes: 11 additions & 1 deletion
12
lib/features/digital_guide_view/presentation/widgets/accessibility_button.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
...res/digital_guide_view/tabs/accessibility_dialog/business/accessibility_mode_service.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import "dart:async"; | ||
|
||
import "package:fast_immutable_collections/fast_immutable_collections.dart"; | ||
import "package:riverpod_annotation/riverpod_annotation.dart"; | ||
|
||
import "../data/accessibility_mode_repository.dart"; | ||
import "../data/modes.dart"; | ||
|
||
part "accessibility_mode_service.g.dart"; | ||
|
||
// if the mode has children, it will be calculated based on them | ||
// if the mode has a key, it will be calculated based on the repository | ||
@riverpod | ||
class AccessibilityModeService extends _$AccessibilityModeService { | ||
@override | ||
Future<bool> build(AccessibilityMode mode) async { | ||
return switch (mode) { | ||
ModeWithChildren() => _calculateModeWithChildrenState(mode), | ||
ModeWithKey() => ref.watch( | ||
accessibilityModeRepositoryProvider(mode).future, | ||
), | ||
}; | ||
} | ||
|
||
Future<void> setMode({required bool newValue}) async { | ||
final modeStronglyTyped = mode; // needed for typing system | ||
await switch (modeStronglyTyped) { | ||
ModeWithChildren() => | ||
_setModeWithChildrenState(modeStronglyTyped, newValue), | ||
ModeWithKey() => _setSingularModeState(modeStronglyTyped, newValue), | ||
}; | ||
} | ||
|
||
// true if any of its children are true | ||
Future<bool> _calculateModeWithChildrenState(ModeWithChildren mode) async { | ||
final submodesValues = await Future.wait( | ||
mode.children.map( | ||
(child) => ref.watch(accessibilityModeServiceProvider(child).future), | ||
), | ||
); | ||
return submodesValues.anyIs(true); | ||
} | ||
|
||
// sets all childrens' of the mode to newValue | ||
Future<void> _setModeWithChildrenState( | ||
ModeWithChildren mode, | ||
bool newValue, | ||
) async { | ||
for (final child in mode.children) { | ||
await ref | ||
.read(accessibilityModeServiceProvider(child).notifier) | ||
.setMode(newValue: newValue); | ||
} | ||
} | ||
|
||
// calls directly the repository | ||
Future<void> _setSingularModeState( | ||
ModeWithKey modeStronglyTyped, | ||
bool newValue, | ||
) { | ||
return ref | ||
.read( | ||
accessibilityModeRepositoryProvider(modeStronglyTyped).notifier, | ||
) | ||
.setMode(newValue: newValue); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...ures/digital_guide_view/tabs/accessibility_dialog/data/accessibility_mode_repository.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import "dart:async"; | ||
|
||
import "package:riverpod_annotation/riverpod_annotation.dart"; | ||
|
||
import "../../../../../config/shared_prefs.dart"; | ||
import "modes.dart"; | ||
|
||
part "accessibility_mode_repository.g.dart"; | ||
|
||
@riverpod | ||
class AccessibilityModeRepository extends _$AccessibilityModeRepository { | ||
@override | ||
Future<bool> build(ModeWithKey mode) async { | ||
final prefs = await ref.watch(sharedPreferencesSingletonProvider.future); | ||
return prefs.getBool(mode.sharedPrefsKey) ?? false; | ||
} | ||
|
||
Future<void> setMode({required bool newValue}) async { | ||
state = AsyncValue.data(newValue); | ||
final prefs = await ref.watch(sharedPreferencesSingletonProvider.future); | ||
await prefs.setBool(mode.sharedPrefsKey, newValue); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
lib/features/digital_guide_view/tabs/accessibility_dialog/data/modes.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// https://dart.dev/language/class-modifiers#sealed | ||
sealed class AccessibilityMode { | ||
const AccessibilityMode(); | ||
} | ||
|
||
// this mode's state depends on the state of its children | ||
sealed class ModeWithChildren extends AccessibilityMode { | ||
const ModeWithChildren(this.children); | ||
final List<AccessibilityMode> children; | ||
} | ||
|
||
// this mode's state is stored locally in shared preferences | ||
sealed class ModeWithKey extends AccessibilityMode { | ||
const ModeWithKey(this.sharedPrefsKey); | ||
final String sharedPrefsKey; | ||
} | ||
|
||
class MotorImpairment extends ModeWithKey { | ||
const MotorImpairment() : super("_prefs_accessibility_motor_impairment"); | ||
} | ||
|
||
class Blind extends ModeWithKey { | ||
const Blind() : super("_prefs_accessibility_blind"); | ||
} | ||
|
||
class LowVision extends ModeWithKey { | ||
const LowVision() : super("_prefs_accessibility_low_vision"); | ||
} | ||
|
||
class SensorySensitivity extends ModeWithKey { | ||
const SensorySensitivity() | ||
: super("_prefs_accessibility_sensory_sensitivity"); | ||
} | ||
|
||
class CognitiveImpairment extends ModeWithKey { | ||
const CognitiveImpairment() | ||
: super("_prefs_accessibility_cognitive_impairment"); | ||
} | ||
|
||
class VisualImpairment extends ModeWithChildren { | ||
const VisualImpairment() | ||
: super( | ||
const [ | ||
LowVision(), | ||
Blind(), | ||
], | ||
); | ||
} |
18 changes: 18 additions & 0 deletions
18
...tures/digital_guide_view/tabs/accessibility_dialog/presentation/accessibility_dialog.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import "package:flutter/material.dart"; | ||
|
||
import "../../../../../utils/context_extensions.dart"; | ||
import "checkboxes_list.dart"; | ||
import "red_dialog.dart"; | ||
|
||
class AccessibilityDialog extends StatelessWidget { | ||
const AccessibilityDialog({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return RedDialog( | ||
title: context.localize.accessibility_profiles, | ||
subtitle: context.localize.you_can_adjust, | ||
child: const CheckboxesList(), | ||
); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
lib/features/digital_guide_view/tabs/accessibility_dialog/presentation/checkboxes_list.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import "package:flutter/material.dart"; | ||
import "package:flutter_hooks/flutter_hooks.dart"; | ||
|
||
import "../data/modes.dart"; | ||
import "mode_checkbox.dart"; | ||
|
||
class _SubModePadding extends Padding { | ||
const _SubModePadding({super.child}) | ||
: super( | ||
padding: const EdgeInsets.only(left: 25), | ||
); | ||
} | ||
|
||
class CheckboxesList extends HookWidget { | ||
const CheckboxesList({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return const Column( | ||
children: [ | ||
ModeCheckbox(MotorImpairment()), | ||
ModeCheckbox(VisualImpairment()), | ||
_SubModePadding(child: ModeCheckbox(Blind())), | ||
_SubModePadding(child: ModeCheckbox(LowVision())), | ||
ModeCheckbox(SensorySensitivity()), | ||
ModeCheckbox(CognitiveImpairment()), | ||
], | ||
); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
lib/features/digital_guide_view/tabs/accessibility_dialog/presentation/labels.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import "package:flutter/material.dart"; | ||
|
||
import "../../../../../utils/context_extensions.dart"; | ||
import "../data/modes.dart"; | ||
|
||
extension AccessibilityModeLocalizationX on AccessibilityMode { | ||
String localizedLabel(BuildContext context) { | ||
return switch (this) { | ||
MotorImpairment() => context.localize.motorImpairment, | ||
VisualImpairment() => context.localize.visualImpairment, | ||
Blind() => context.localize.blind, | ||
LowVision() => context.localize.lowVision, | ||
SensorySensitivity() => context.localize.sensorySensitivity, | ||
CognitiveImpairment() => context.localize.cognitiveImpairment, | ||
}; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
lib/features/digital_guide_view/tabs/accessibility_dialog/presentation/mode_checkbox.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import "dart:async"; | ||
|
||
import "package:flutter/material.dart"; | ||
import "package:flutter_riverpod/flutter_riverpod.dart"; | ||
|
||
import "../../../../../theme/app_theme.dart"; | ||
import "../business/accessibility_mode_service.dart"; | ||
import "../data/modes.dart"; | ||
import "labels.dart"; | ||
|
||
class ModeCheckbox extends ConsumerWidget { | ||
const ModeCheckbox(this.mode, {super.key}); | ||
final AccessibilityMode mode; | ||
@override | ||
Widget build(BuildContext context, WidgetRef ref) { | ||
final value = ref.watch(accessibilityModeServiceProvider(mode)); | ||
|
||
// ignore: avoid_positional_boolean_parameters | ||
void onChanged(bool? value) { | ||
if (value != null) { | ||
unawaited( | ||
ref | ||
.read(accessibilityModeServiceProvider(mode).notifier) | ||
.setMode(newValue: value), | ||
); | ||
} | ||
} | ||
|
||
return CheckboxListTile( | ||
controlAffinity: ListTileControlAffinity.leading, | ||
title: Text( | ||
mode.localizedLabel(context), | ||
style: context.aboutUsTheme.body, | ||
), | ||
value: value.value ?? false, | ||
onChanged: onChanged, | ||
); | ||
} | ||
} |
114 changes: 114 additions & 0 deletions
114
lib/features/digital_guide_view/tabs/accessibility_dialog/presentation/red_dialog.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import "package:flutter/material.dart"; | ||
|
||
import "../../../../../config/ui_config.dart"; | ||
import "../../../../../theme/app_theme.dart"; | ||
import "../../../../../utils/context_extensions.dart"; | ||
|
||
// pure UI, no logic, just a nice dialog with a title, subtitle and a child | ||
class RedDialog extends StatelessWidget { | ||
final String title; | ||
final String subtitle; | ||
final Widget child; | ||
|
||
const RedDialog({ | ||
super.key, | ||
required this.title, | ||
required this.child, | ||
required this.subtitle, | ||
}); | ||
@override | ||
Widget build(BuildContext context) { | ||
return Dialog( | ||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), | ||
child: DecoratedBox( | ||
decoration: BoxDecoration( | ||
color: context.colorTheme.whiteSoap, | ||
borderRadius: BorderRadius.circular(8), | ||
border: Border( | ||
top: BorderSide( | ||
color: context.colorTheme.orangePomegranade, | ||
), | ||
bottom: BorderSide( | ||
color: context.colorTheme.orangePomegranade, | ||
width: 5, | ||
), | ||
), | ||
boxShadow: [ | ||
BoxShadow( | ||
color: Colors.black.withAlpha(64), | ||
offset: const Offset(0, 4), | ||
blurRadius: 4, | ||
), | ||
], | ||
), | ||
child: ConstrainedBox( | ||
constraints: const BoxConstraints(maxWidth: 332), | ||
child: Column( | ||
mainAxisSize: MainAxisSize.min, | ||
children: [ | ||
Padding( | ||
padding: const EdgeInsets.all(20).copyWith(bottom: 0), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
children: [ | ||
Text( | ||
title, | ||
style: context.textTheme.headline.copyWith(height: 1.4), | ||
), | ||
IconButton( | ||
icon: const Icon(Icons.close), | ||
color: context.colorTheme.greyPigeon, | ||
onPressed: () => Navigator.of(context).pop(), | ||
), | ||
], | ||
), | ||
), | ||
Padding( | ||
padding: const EdgeInsets.symmetric(horizontal: 20), | ||
child: Text( | ||
subtitle, | ||
style: context.aboutUsTheme.body.copyWith( | ||
height: 1.4, | ||
color: context.colorTheme.greyPigeon, | ||
), | ||
), | ||
), | ||
const SizedBox(height: 6), | ||
child, | ||
Padding( | ||
padding: const EdgeInsets.all(20).copyWith(top: 0), | ||
child: ElevatedButton( | ||
onPressed: () { | ||
// we're saving the changes in real time anyway | ||
Navigator.of(context).pop(); | ||
}, | ||
style: ElevatedButton.styleFrom( | ||
backgroundColor: | ||
context.colorTheme.orangePomegranadeLighter, | ||
elevation: 2, | ||
padding: const EdgeInsets.symmetric( | ||
vertical: 10, | ||
horizontal: 20, | ||
), | ||
shape: RoundedRectangleBorder( | ||
borderRadius: FilterConfig.radius, | ||
), | ||
), | ||
child: Row( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
children: [ | ||
Text( | ||
context.localize.apply, | ||
style: context.textTheme.titleWhite, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} |
Oops, something went wrong.