Skip to content

Commit

Permalink
feat(digital-guide): add accessibility modes dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-the-shark committed Jan 19, 2025
1 parent 8075640 commit dd576a5
Show file tree
Hide file tree
Showing 12 changed files with 433 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lib/config/ui_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ abstract class FilterConfig {
static const paddingMedium = 8.0;
static const spacingBetweenWidgets = 12.0;
static final radius = BorderRadius.circular(8);
static const buttonPadding =
EdgeInsets.symmetric(vertical: 10, horizontal: 20);
}

class DialogsConfig {
static final padding = const EdgeInsets.all(20).copyWith(top: 6);
}

abstract class LottieAnimationConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import "dart:async";

import "package:flutter/material.dart";

import "../../../../config/ui_config.dart";
import "../../../../theme/app_theme.dart";
import "../../tabs/accessibility_dialog/presentation/accessibility_dialog.dart";

class AccessibilityButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 8),
child: OutlinedButton(
onPressed: () {},
onPressed: () {
unawaited(
showDialog(
context: context,
builder: (_) => const AccessibilityDialog(),
),
);
},
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
Expand Down
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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "../data/modes.dart";

final topLevelModes = [
const MotorImpairment(),
const VisualImpairment(),
const SensorySensitivity(),
const CognitiveImpairment(),
];
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);
}
}
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(),
],
);
}
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(),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart";

import "../business/top_level_modes.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 Column(
children: [
for (final mode in topLevelModes)
switch (mode) {
ModeWithChildren() => Column(
children: [
for (final subMode in mode.children)
_SubModePadding(child: ModeCheckbox(subMode)),
],
),
ModeWithKey() => ModeCheckbox(mode),
},
],
);
}
}
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,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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(
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
mode.localizedLabel(context),
style: context.aboutUsTheme.body,
),
value: value.value ?? false,
onChanged: onChanged,
);
}
}
Loading

0 comments on commit dd576a5

Please sign in to comment.