Skip to content

Commit

Permalink
Added app text scale feature to settings
Browse files Browse the repository at this point in the history
  • Loading branch information
albor09 committed Jun 30, 2024
1 parent 0e3afe1 commit f273db1
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 33 deletions.
12 changes: 9 additions & 3 deletions lib/src/feature/app/widget/material_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class MaterialContext extends StatelessWidget {
Widget build(BuildContext context) {
final theme = SettingsScope.themeOf(context).theme;
final locale = SettingsScope.localeOf(context).locale;
final textScale = SettingsScope.textScaleOf(context).textScale;

final mediaQueryData = MediaQuery.of(context);

return MaterialApp(
theme: theme.lightTheme,
Expand All @@ -29,10 +32,13 @@ class MaterialContext extends StatelessWidget {
supportedLocales: Localization.supportedLocales,
locale: locale,
home: const HomeScreen(),
builder: (context, child) => MediaQuery.withClampedTextScaling(
builder: (context, child) => MediaQuery(
key: _globalKey,
minScaleFactor: 1.0,
maxScaleFactor: 2.0,
data: mediaQueryData.copyWith(
textScaler: TextScaler.linear(
mediaQueryData.textScaler.scale(textScale).clamp(0.5, 2),
),
),
child: child!,
),
);
Expand Down
16 changes: 16 additions & 0 deletions lib/src/feature/home/widget/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ class _HomeScreenState extends State<HomeScreen> {
final windowSize = constraints.materialBreakpoint;
return CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Column(
children: [
const Text('Text scale'),
Slider(
divisions: 8,
min: 0.5,
max: 2,
value: SettingsScope.textScaleOf(context).textScale,
onChanged: (value) {
SettingsScope.textScaleOf(context).setTextScale(value);
},
),
],
),
),
SliverPadding(
padding: HorizontalSpacing.centered(windowWidth),
sliver: SliverGrid.builder(
Expand Down
11 changes: 10 additions & 1 deletion lib/src/feature/initialization/logic/composition_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:sizzle_starter/src/feature/initialization/model/dependencies.dar
import 'package:sizzle_starter/src/feature/settings/bloc/settings_bloc.dart';
import 'package:sizzle_starter/src/feature/settings/data/locale_datasource.dart';
import 'package:sizzle_starter/src/feature/settings/data/locale_repository.dart';
import 'package:sizzle_starter/src/feature/settings/data/text_scale_datasource.dart';
import 'package:sizzle_starter/src/feature/settings/data/text_scale_repository.dart';
import 'package:sizzle_starter/src/feature/settings/data/theme_datasource.dart';
import 'package:sizzle_starter/src/feature/settings/data/theme_mode_codec.dart';
import 'package:sizzle_starter/src/feature/settings/data/theme_repository.dart';
Expand Down Expand Up @@ -85,15 +87,22 @@ final class CompositionRoot {
),
);

final textScaleRepository = TextScaleRepositoryImpl(
textScaleDataSource:
TextScaleDatasourceLocal(sharedPreferences: prefs));

final localeFuture = localeRepository.getLocale();
final theme = await themeRepository.getTheme();
final locale = await localeFuture;
final textScale = await textScaleRepository.getScale();

final initialState = SettingsState.idle(appTheme: theme, locale: locale);
final initialState = SettingsState.idle(
appTheme: theme, locale: locale, textScale: textScale);

final settingsBloc = SettingsBloc(
localeRepository: localeRepository,
themeRepository: themeRepository,
textScaleRepository: textScaleRepository,
initialState: initialState,
);
return settingsBloc;
Expand Down
136 changes: 109 additions & 27 deletions lib/src/feature/settings/bloc/settings_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:ui' show Locale;

import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sizzle_starter/src/feature/app/model/app_theme.dart';
import 'package:sizzle_starter/src/feature/settings/data/locale_repository.dart';
import 'package:sizzle_starter/src/feature/settings/data/text_scale_repository.dart';
import 'package:sizzle_starter/src/feature/settings/data/theme_repository.dart';

/// {@template settings_bloc}
Expand All @@ -13,20 +15,24 @@ final class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
SettingsBloc({
required LocaleRepository localeRepository,
required ThemeRepository themeRepository,
required TextScaleRepository textScaleRepository,
required SettingsState initialState,
}) : _localeRepo = localeRepository,
_themeRepo = themeRepository,
_textScaleRepo = textScaleRepository,
super(initialState) {
on<SettingsEvent>(
(event, emit) => switch (event) {
final _UpdateLocaleSettingsEvent e => _updateLocale(e, emit),
final _UpdateThemeSettingsEvent e => _updateTheme(e, emit),
final _UpdateTextScaleSettingsEvent e => _updateTextScale(e, emit),
},
);
}

final LocaleRepository _localeRepo;
final ThemeRepository _themeRepo;
final TextScaleRepository _textScaleRepo;

Future<void> _updateTheme(
_UpdateThemeSettingsEvent event,
Expand All @@ -35,20 +41,24 @@ final class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
try {
emitter(
SettingsState.processing(
appTheme: state.appTheme,
locale: state.locale,
),
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale),
);
await _themeRepo.setTheme(event.appTheme);

emitter(
SettingsState.idle(appTheme: event.appTheme, locale: state.locale),
SettingsState.idle(
appTheme: event.appTheme,
locale: state.locale,
textScale: state.textScale),
);
} on Object catch (e, stackTrace) {
emitter(
SettingsState.error(
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale,
cause: e,
),
);
Expand All @@ -63,20 +73,57 @@ final class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
try {
emitter(
SettingsState.processing(
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale),
);
await _localeRepo.setLocale(event.locale);

emitter(
SettingsState.idle(
appTheme: state.appTheme,
locale: event.locale,
textScale: state.textScale),
);
} on Object catch (e, stackTrace) {
emitter(
SettingsState.error(
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale,
cause: e,
),
);
await _localeRepo.setLocale(event.locale);
onError(e, stackTrace);
}
}

Future<void> _updateTextScale(
_UpdateTextScaleSettingsEvent event,
Emitter<SettingsState> emitter,
) async {
try {
emitter(
SettingsState.idle(appTheme: state.appTheme, locale: event.locale),
SettingsState.processing(
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale,
),
);
await _textScaleRepo.setScale(event.textScale);

emitter(
SettingsState.idle(
appTheme: state.appTheme,
locale: state.locale,
textScale: event.textScale),
);
} on Object catch (e, stackTrace) {
emitter(
SettingsState.error(
appTheme: state.appTheme,
locale: state.locale,
textScale: state.textScale,
cause: e,
),
);
Expand All @@ -87,76 +134,87 @@ final class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {

/// States for the [SettingsBloc].
sealed class SettingsState {
const SettingsState({this.locale, this.appTheme});
const SettingsState({this.locale, this.appTheme, this.textScale});

/// Application locale.
final Locale? locale;

/// Data class used to represent the state of theme.
final AppTheme? appTheme;

/// Application text scale
final double? textScale;

/// Idle state for the [SettingsBloc].
const factory SettingsState.idle({Locale? locale, AppTheme? appTheme}) =
_IdleSettingsState;
const factory SettingsState.idle(
{Locale? locale,
AppTheme? appTheme,
double? textScale}) = _IdleSettingsState;

/// Processing state for the [SettingsBloc].
const factory SettingsState.processing({Locale? locale, AppTheme? appTheme}) =
_ProcessingSettingsState;
const factory SettingsState.processing(
{Locale? locale,
AppTheme? appTheme,
double? textScale}) = _ProcessingSettingsState;

/// Error state for the [SettingsBloc].
const factory SettingsState.error({
required Object cause,
Locale? locale,
AppTheme? appTheme,
double? textScale,
}) = _ErrorSettingsState;
}

final class _IdleSettingsState extends SettingsState {
const _IdleSettingsState({super.locale, super.appTheme});
const _IdleSettingsState({super.locale, super.appTheme, super.textScale});

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is _IdleSettingsState &&
other.locale == locale &&
other.appTheme == appTheme;
other.appTheme == appTheme &&
other.textScale == textScale;
}

@override
int get hashCode => Object.hash(locale, appTheme);
int get hashCode => Object.hash(locale, appTheme, textScale);

@override
String toString() =>
'SettingsState.idle(locale: $locale, appTheme: $appTheme)';
'SettingsState.idle(locale: $locale, appTheme: $appTheme, textScale: $textScale)';
}

final class _ProcessingSettingsState extends SettingsState {
const _ProcessingSettingsState({super.locale, super.appTheme});
const _ProcessingSettingsState({
super.locale,
super.appTheme,
super.textScale,
});

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is _ProcessingSettingsState &&
other.locale == locale &&
other.appTheme == appTheme;
other.appTheme == appTheme &&
other.textScale == textScale;
}

@override
int get hashCode => Object.hash(locale, appTheme);
int get hashCode => Object.hash(locale, appTheme, textScale);

@override
String toString() =>
'SettingsState.processing(locale: $locale, appTheme: $appTheme)';
'SettingsState.processing(locale: $locale, appTheme: $appTheme, textScale: $textScale)';
}

final class _ErrorSettingsState extends SettingsState {
const _ErrorSettingsState({
required this.cause,
super.locale,
super.appTheme,
});
const _ErrorSettingsState(
{required this.cause, super.locale, super.appTheme, super.textScale});

/// The cause of the error.
final Object cause;
Expand All @@ -168,15 +226,16 @@ final class _ErrorSettingsState extends SettingsState {
return other is _ErrorSettingsState &&
other.cause == cause &&
other.locale == locale &&
other.appTheme == appTheme;
other.appTheme == appTheme &&
other.textScale == textScale;
}

@override
int get hashCode => Object.hash(cause, locale, appTheme);
int get hashCode => Object.hash(cause, locale, appTheme, textScale);

@override
String toString() => 'SettingsState.error(cause: $cause, '
'locale: $locale, appTheme: $appTheme)';
'locale: $locale, appTheme: $appTheme, textScale: $textScale)';
}

/// Events for the [SettingsBloc].
Expand All @@ -190,6 +249,9 @@ sealed class SettingsEvent {
/// Event to update the locale.
const factory SettingsEvent.updateLocale({required Locale locale}) =
_UpdateLocaleSettingsEvent;

const factory SettingsEvent.updateTextScale({required double textScale}) =
_UpdateTextScaleSettingsEvent;
}

final class _UpdateThemeSettingsEvent extends SettingsEvent {
Expand Down Expand Up @@ -231,3 +293,23 @@ final class _UpdateLocaleSettingsEvent extends SettingsEvent {
@override
String toString() => 'SettingsEvent.updateLocale(locale: $locale)';
}

final class _UpdateTextScaleSettingsEvent extends SettingsEvent {
const _UpdateTextScaleSettingsEvent({required this.textScale});

final double textScale;

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is _UpdateTextScaleSettingsEvent &&
other.textScale == textScale;
}

@override
int get hashCode => textScale.hashCode;

@override
String toString() => 'SettingsEvent.updateTextScale(scale: $textScale)';
}
Loading

0 comments on commit f273db1

Please sign in to comment.