mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 06:46:22 +01:00
Refactoring: migrate ThemeSettings to use bloc
This commit is contained in:
parent
232be4d267
commit
7b1a70d1d1
6 changed files with 331 additions and 95 deletions
139
app/lib/bloc/settings/theme.dart
Normal file
139
app/lib/bloc/settings/theme.dart
Normal file
|
@ -0,0 +1,139 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:copy_with/copy_with.dart';
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
import 'package:to_string/to_string.dart';
|
||||
|
||||
part 'theme.g.dart';
|
||||
|
||||
@autoCopyWith
|
||||
@toString
|
||||
class ThemeSettingsState {
|
||||
const ThemeSettingsState({
|
||||
required this.isFollowSystemTheme,
|
||||
required this.isUseBlackInDarkTheme,
|
||||
required this.seedColor,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool isFollowSystemTheme;
|
||||
final bool isUseBlackInDarkTheme;
|
||||
final Color seedColor;
|
||||
}
|
||||
|
||||
abstract class ThemeSettingsEvent {
|
||||
const ThemeSettingsEvent();
|
||||
}
|
||||
|
||||
@toString
|
||||
class ThemeSettingsSetFollowSystemTheme extends ThemeSettingsEvent {
|
||||
const ThemeSettingsSetFollowSystemTheme(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
}
|
||||
|
||||
@toString
|
||||
class ThemeSettingsSetUseBlackInDarkTheme extends ThemeSettingsEvent {
|
||||
const ThemeSettingsSetUseBlackInDarkTheme(this.value, this.theme);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final bool value;
|
||||
final ThemeData theme;
|
||||
}
|
||||
|
||||
@toString
|
||||
class ThemeSettingsSetSeedColor extends ThemeSettingsEvent {
|
||||
const ThemeSettingsSetSeedColor(this.value);
|
||||
|
||||
@override
|
||||
String toString() => _$toString();
|
||||
|
||||
final Color value;
|
||||
}
|
||||
|
||||
class ThemeSettingsError {
|
||||
const ThemeSettingsError(this.ev, [this.error, this.stackTrace]);
|
||||
|
||||
final ThemeSettingsEvent ev;
|
||||
final Object? error;
|
||||
final StackTrace? stackTrace;
|
||||
}
|
||||
|
||||
@npLog
|
||||
class ThemeSettingsBloc extends Bloc<ThemeSettingsEvent, ThemeSettingsState> {
|
||||
ThemeSettingsBloc(DiContainer c)
|
||||
: assert(require(c)),
|
||||
_c = c,
|
||||
super(ThemeSettingsState(
|
||||
isFollowSystemTheme: c.pref.isFollowSystemThemeOr(false),
|
||||
isUseBlackInDarkTheme: c.pref.isUseBlackInDarkThemeOr(false),
|
||||
seedColor: getSeedColor(),
|
||||
)) {
|
||||
on<ThemeSettingsSetFollowSystemTheme>(_onSetFollowSystemTheme);
|
||||
on<ThemeSettingsSetUseBlackInDarkTheme>(_onSetUseBlackInDarkTheme);
|
||||
on<ThemeSettingsSetSeedColor>(_onSetSeedColor);
|
||||
}
|
||||
|
||||
static bool require(DiContainer c) => DiContainer.has(c, DiType.pref);
|
||||
|
||||
Stream<ThemeSettingsError> errorStream() => _errorStream.stream;
|
||||
|
||||
Future<void> _onSetFollowSystemTheme(ThemeSettingsSetFollowSystemTheme ev,
|
||||
Emitter<ThemeSettingsState> emit) async {
|
||||
final oldValue = state.isFollowSystemTheme;
|
||||
emit(state.copyWith(isFollowSystemTheme: ev.value));
|
||||
if (await _c.pref.setFollowSystemTheme(ev.value)) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
} else {
|
||||
_log.severe("[_onSetFollowSystemTheme] Failed writing pref");
|
||||
_errorStream.add(ThemeSettingsError(ev));
|
||||
emit(state.copyWith(isFollowSystemTheme: oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSetUseBlackInDarkTheme(ThemeSettingsSetUseBlackInDarkTheme ev,
|
||||
Emitter<ThemeSettingsState> emit) async {
|
||||
final oldValue = state.isUseBlackInDarkTheme;
|
||||
emit(state.copyWith(isUseBlackInDarkTheme: ev.value));
|
||||
if (await _c.pref.setUseBlackInDarkTheme(ev.value)) {
|
||||
if (ev.theme.brightness == Brightness.dark) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
}
|
||||
} else {
|
||||
_log.severe("[_onSetUseBlackInDarkTheme] Failed writing pref");
|
||||
_errorStream.add(ThemeSettingsError(ev));
|
||||
emit(state.copyWith(isUseBlackInDarkTheme: oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSetSeedColor(
|
||||
ThemeSettingsSetSeedColor ev, Emitter<ThemeSettingsState> emit) async {
|
||||
final oldValue = state.seedColor;
|
||||
emit(state.copyWith(seedColor: ev.value));
|
||||
if (await _c.pref.setSeedColor(ev.value.withAlpha(0xFF).value)) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
} else {
|
||||
_log.severe("[_onSetSeedColor] Failed writing pref");
|
||||
_errorStream.add(ThemeSettingsError(ev));
|
||||
emit(state.copyWith(seedColor: oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
final DiContainer _c;
|
||||
final _errorStream = StreamController<ThemeSettingsError>.broadcast();
|
||||
}
|
74
app/lib/bloc/settings/theme.g.dart
Normal file
74
app/lib/bloc/settings/theme.g.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'theme.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension $ThemeSettingsStateCopyWith on ThemeSettingsState {
|
||||
ThemeSettingsState copyWith(
|
||||
{bool? isFollowSystemTheme,
|
||||
bool? isUseBlackInDarkTheme,
|
||||
Color? seedColor}) =>
|
||||
_$copyWith(
|
||||
isFollowSystemTheme: isFollowSystemTheme,
|
||||
isUseBlackInDarkTheme: isUseBlackInDarkTheme,
|
||||
seedColor: seedColor);
|
||||
|
||||
ThemeSettingsState _$copyWith(
|
||||
{bool? isFollowSystemTheme,
|
||||
bool? isUseBlackInDarkTheme,
|
||||
Color? seedColor}) {
|
||||
return ThemeSettingsState(
|
||||
isFollowSystemTheme: isFollowSystemTheme ?? this.isFollowSystemTheme,
|
||||
isUseBlackInDarkTheme:
|
||||
isUseBlackInDarkTheme ?? this.isUseBlackInDarkTheme,
|
||||
seedColor: seedColor ?? this.seedColor);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$ThemeSettingsBlocNpLog on ThemeSettingsBloc {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log = Logger("bloc.settings.theme.ThemeSettingsBloc");
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// ToStringGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$ThemeSettingsStateToString on ThemeSettingsState {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "ThemeSettingsState {isFollowSystemTheme: $isFollowSystemTheme, isUseBlackInDarkTheme: $isUseBlackInDarkTheme, seedColor: $seedColor}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$ThemeSettingsSetFollowSystemThemeToString
|
||||
on ThemeSettingsSetFollowSystemTheme {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "ThemeSettingsSetFollowSystemTheme {value: $value}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$ThemeSettingsSetUseBlackInDarkThemeToString
|
||||
on ThemeSettingsSetUseBlackInDarkTheme {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "ThemeSettingsSetUseBlackInDarkTheme {value: $value, theme: $theme}";
|
||||
}
|
||||
}
|
||||
|
||||
extension _$ThemeSettingsSetSeedColorToString on ThemeSettingsSetSeedColor {
|
||||
String _$toString() {
|
||||
// ignore: unnecessary_string_interpolations
|
||||
return "ThemeSettingsSetSeedColor {value: $value}";
|
||||
}
|
||||
}
|
|
@ -1,39 +1,63 @@
|
|||
import 'package:event_bus/event_bus.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
import 'package:kiwi/kiwi.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/event/event.dart';
|
||||
import 'package:nc_photos/bloc/settings/theme.dart';
|
||||
import 'package:nc_photos/di_container.dart';
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/mobile/android/android_info.dart';
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/pref.dart';
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:np_codegen/np_codegen.dart';
|
||||
|
||||
part 'theme_settings.g.dart';
|
||||
|
||||
class ThemeSettings extends StatefulWidget {
|
||||
class ThemeSettings extends StatelessWidget {
|
||||
const ThemeSettings({super.key});
|
||||
|
||||
@override
|
||||
createState() => _ThemeSettingsState();
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => ThemeSettingsBloc(KiwiContainer().resolve<DiContainer>()),
|
||||
child: const _WrappedThemeSettings(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WrappedThemeSettings extends StatefulWidget {
|
||||
const _WrappedThemeSettings();
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _WrappedThemeSettingsState();
|
||||
}
|
||||
|
||||
@npLog
|
||||
class _ThemeSettingsState extends State<ThemeSettings> {
|
||||
class _WrappedThemeSettingsState extends State<_WrappedThemeSettings> {
|
||||
@override
|
||||
initState() {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isFollowSystemTheme = Pref().isFollowSystemThemeOr(false);
|
||||
_isUseBlackInDarkTheme = Pref().isUseBlackInDarkThemeOr(false);
|
||||
_seedColor = getSeedColor();
|
||||
_errorSubscription =
|
||||
context.read<ThemeSettingsBloc>().errorStream().listen((_) {
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
void dispose() {
|
||||
_errorSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Builder(
|
||||
builder: (context) => _buildContent(context),
|
||||
|
@ -51,32 +75,60 @@ class _ThemeSettingsState extends State<ThemeSettings> {
|
|||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
subtitle: Text(L10n.global().settingsSeedColorDescription),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: _seedColor,
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
BlocBuilder<ThemeSettingsBloc, ThemeSettingsState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.seedColor != current.seedColor,
|
||||
builder: (context, state) {
|
||||
return ListTile(
|
||||
title: Text(L10n.global().settingsSeedColorTitle),
|
||||
subtitle: Text(L10n.global().settingsSeedColorDescription),
|
||||
trailing: Icon(
|
||||
Icons.circle,
|
||||
size: 32,
|
||||
color: state.seedColor,
|
||||
),
|
||||
onTap: () => _onSeedColorPressed(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (platform_k.isAndroid &&
|
||||
AndroidInfo().sdkInt >= AndroidVersion.Q)
|
||||
SwitchListTile(
|
||||
title: Text(L10n.global().settingsFollowSystemThemeTitle),
|
||||
value: _isFollowSystemTheme,
|
||||
onChanged: (value) => _onFollowSystemThemeChanged(value),
|
||||
BlocBuilder<ThemeSettingsBloc, ThemeSettingsState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.isFollowSystemTheme !=
|
||||
current.isFollowSystemTheme,
|
||||
builder: (context, state) {
|
||||
return SwitchListTile(
|
||||
title: Text(L10n.global().settingsFollowSystemThemeTitle),
|
||||
value: state.isFollowSystemTheme,
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<ThemeSettingsBloc>()
|
||||
.add(ThemeSettingsSetFollowSystemTheme(value));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text(L10n.global().settingsUseBlackInDarkThemeTitle),
|
||||
subtitle: Text(_isUseBlackInDarkTheme
|
||||
? L10n.global().settingsUseBlackInDarkThemeTrueDescription
|
||||
: L10n.global()
|
||||
.settingsUseBlackInDarkThemeFalseDescription),
|
||||
value: _isUseBlackInDarkTheme,
|
||||
onChanged: (value) =>
|
||||
_onUseBlackInDarkThemeChanged(context, value),
|
||||
BlocBuilder<ThemeSettingsBloc, ThemeSettingsState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.isUseBlackInDarkTheme !=
|
||||
current.isUseBlackInDarkTheme,
|
||||
builder: (context, state) {
|
||||
return SwitchListTile(
|
||||
title: Text(L10n.global().settingsUseBlackInDarkThemeTitle),
|
||||
subtitle: Text(state.isUseBlackInDarkTheme
|
||||
? L10n.global()
|
||||
.settingsUseBlackInDarkThemeTrueDescription
|
||||
: L10n.global()
|
||||
.settingsUseBlackInDarkThemeFalseDescription),
|
||||
value: state.isUseBlackInDarkTheme,
|
||||
onChanged: (value) {
|
||||
context.read<ThemeSettingsBloc>().add(
|
||||
ThemeSettingsSetUseBlackInDarkTheme(
|
||||
value, Theme.of(context)));
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -85,47 +137,6 @@ class _ThemeSettingsState extends State<ThemeSettings> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _onFollowSystemThemeChanged(bool value) async {
|
||||
final oldValue = _isFollowSystemTheme;
|
||||
setState(() {
|
||||
_isFollowSystemTheme = value;
|
||||
});
|
||||
if (await Pref().setFollowSystemTheme(value)) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
} else {
|
||||
_log.severe("[_onFollowSystemThemeChanged] Failed writing pref");
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
setState(() {
|
||||
_isFollowSystemTheme = oldValue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUseBlackInDarkThemeChanged(
|
||||
BuildContext context, bool value) async {
|
||||
final oldValue = _isUseBlackInDarkTheme;
|
||||
setState(() {
|
||||
_isUseBlackInDarkTheme = value;
|
||||
});
|
||||
if (await Pref().setUseBlackInDarkTheme(value)) {
|
||||
if (Theme.of(context).brightness == Brightness.dark) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
}
|
||||
} else {
|
||||
_log.severe("[_onUseBlackInDarkThemeChanged] Failed writing pref");
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
setState(() {
|
||||
_isUseBlackInDarkTheme = oldValue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSeedColorPressed(BuildContext context) async {
|
||||
final result = await showDialog<Color>(
|
||||
context: context,
|
||||
|
@ -134,28 +145,12 @@ class _ThemeSettingsState extends State<ThemeSettings> {
|
|||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final oldValue = _seedColor;
|
||||
setState(() {
|
||||
_seedColor = result;
|
||||
});
|
||||
if (await Pref().setSeedColor(result.withAlpha(0xFF).value)) {
|
||||
KiwiContainer().resolve<EventBus>().fire(ThemeChangedEvent());
|
||||
} else {
|
||||
_log.severe("[_onSeedColorPressed] Failed writing pref");
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().writePreferenceFailureNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
setState(() {
|
||||
_seedColor = oldValue;
|
||||
});
|
||||
if (mounted) {
|
||||
context.read<ThemeSettingsBloc>().add(ThemeSettingsSetSeedColor(result));
|
||||
}
|
||||
}
|
||||
|
||||
late bool _isFollowSystemTheme;
|
||||
late bool _isUseBlackInDarkTheme;
|
||||
late Color _seedColor;
|
||||
late final StreamSubscription _errorSubscription;
|
||||
}
|
||||
|
||||
class _SeedColorPicker extends StatefulWidget {
|
||||
|
|
|
@ -6,10 +6,10 @@ part of 'theme_settings.dart';
|
|||
// NpLogGenerator
|
||||
// **************************************************************************
|
||||
|
||||
extension _$_ThemeSettingsStateNpLog on _ThemeSettingsState {
|
||||
extension _$_WrappedThemeSettingsStateNpLog on _WrappedThemeSettingsState {
|
||||
// ignore: unused_element
|
||||
Logger get _log => log;
|
||||
|
||||
static final log =
|
||||
Logger("widget.settings.theme_settings._ThemeSettingsState");
|
||||
Logger("widget.settings.theme_settings._WrappedThemeSettingsState");
|
||||
}
|
||||
|
|
|
@ -297,6 +297,24 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
copy_with:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: copy_with
|
||||
ref: "copy_with-1.0.0"
|
||||
resolved-ref: c3ef6b3b5337f99ee7c0e1fb655bacf635d8b072
|
||||
url: "https://gitlab.com/nkming2/dart-copy-with"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
copy_with_build:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: copy_with_build
|
||||
ref: "copy_with_build-1.0.0"
|
||||
resolved-ref: "8303676d56dc16bc2fb1e38ad95c5d97b493e613"
|
||||
url: "https://gitlab.com/nkming2/dart-copy-with"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -40,6 +40,11 @@ dependencies:
|
|||
circular_reveal_animation: ^2.0.1
|
||||
collection: ^1.15.0
|
||||
connectivity_plus: ^2.0.2
|
||||
copy_with:
|
||||
git:
|
||||
url: https://gitlab.com/nkming2/dart-copy-with
|
||||
path: copy_with
|
||||
ref: copy_with-1.0.0
|
||||
devicelocale: ^0.5.0
|
||||
device_info_plus: ^4.0.0
|
||||
draggable_scrollbar:
|
||||
|
@ -124,6 +129,11 @@ dev_dependencies:
|
|||
test: any
|
||||
bloc_test: any
|
||||
build_runner: ^2.1.11
|
||||
copy_with_build:
|
||||
git:
|
||||
url: https://gitlab.com/nkming2/dart-copy-with
|
||||
path: copy_with_build
|
||||
ref: copy_with_build-1.0.0
|
||||
drift_dev: ^1.7.0
|
||||
flutter_lints: ^2.0.1
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in a new issue