Skip to content

Commit

Permalink
Disallow choosing system color palette if not supported by device
Browse files Browse the repository at this point in the history
Rename `ColorSchemeType.app` -> `.custom`
Add `subtitle` and `enabled` params to `ChoiceListTile`
  • Loading branch information
evgfilim1 committed Feb 1, 2024
1 parent 925c8a9 commit dc2b8ad
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 23 deletions.
21 changes: 8 additions & 13 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import "package:dynamic_color/dynamic_color.dart";
import "package:flutter/material.dart";
import "package:flutter_localizations/flutter_localizations.dart";
import "package:package_info_plus/package_info_plus.dart";
Expand All @@ -13,21 +12,25 @@ import "screens/seat_randomizer.dart";
import "screens/settings/appearance.dart";
import "screens/settings/behavior.dart";
import "screens/settings/main.dart";
import "utils/color_scheme.dart";
import "utils/game_controller.dart";
import "utils/settings.dart";
import "utils/updates_checker.dart";
import "widgets/color_scheme_wrapper.dart";

void main() async {
WidgetsFlutterBinding.ensureInitialized();
final settings = await getSettings();
final packageInfo = await PackageInfo.fromPlatform();
final appColorScheme = await loadColorScheme(fallbackSeedColor: settings.seedColor);
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<SettingsModel>.value(value: settings),
Provider<PackageInfo>.value(value: packageInfo),
ChangeNotifierProvider<GameController>(create: (context) => GameController()),
ChangeNotifierProvider<UpdatesChecker>(create: (context) => UpdatesChecker()),
Provider<BrightnessAwareColorScheme>.value(value: appColorScheme),
],
child: const MyApp(),
),
Expand All @@ -40,23 +43,15 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsModel>();
return DynamicColorBuilder(
builder: (light, dark) => MaterialApp(
return BrightnessAwareColorSchemeBuilder(
builder: (colorScheme) => MaterialApp(
title: "Mafia companion",
theme: ThemeData(
colorScheme: (settings.colorSchemeType == ColorSchemeType.system ? light : null) ??
ColorScheme.fromSeed(
seedColor: settings.seedColor,
brightness: Brightness.light,
),
colorScheme: colorScheme.light,
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: (settings.colorSchemeType == ColorSchemeType.system ? dark : null) ??
ColorScheme.fromSeed(
seedColor: settings.seedColor,
brightness: Brightness.dark,
),
colorScheme: colorScheme.dark,
useMaterial3: true,
),
themeMode: settings.themeMode,
Expand Down
26 changes: 21 additions & 5 deletions lib/screens/settings/appearance.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import "package:flutter/foundation.dart";
import "package:flutter/material.dart";
import "package:flutter_colorpicker/flutter_colorpicker.dart";
import "package:provider/provider.dart";

import "../../utils/color_scheme.dart";
import "../../utils/settings.dart";
import "../../widgets/list_tiles/choice.dart";

Expand Down Expand Up @@ -30,6 +32,14 @@ class AppearanceSettingsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsModel>();
final colorScheme = context.read<BrightnessAwareColorScheme>();
final dynamicColorUnavailableReason = kIsWeb
? "в браузере"
: !colorScheme.isDynamicColorSupported
? "на этом устройстве"
: null;
final canSelectSeedColor =
settings.colorSchemeType == ColorSchemeType.custom || dynamicColorUnavailableReason != null;

return Scaffold(
appBar: AppBar(title: const Text("Внешний вид")),
Expand All @@ -52,25 +62,31 @@ class AppearanceSettingsScreen extends StatelessWidget {
onChanged: settings.setThemeMode,
),
ChoiceListTile(
enabled: dynamicColorUnavailableReason == null,
leading: const Icon(Icons.color_lens),
title: const Text("Цветовая схема"),
subtitle: dynamicColorUnavailableReason != null
? Text("Недоступно $dynamicColorUnavailableReason")
: null,
items: ColorSchemeType.values,
itemToString: (item) => switch (item) {
ColorSchemeType.system => "Системная",
ColorSchemeType.app => "Пользовательская",
ColorSchemeType.custom => "Пользовательская",
},
index: settings.colorSchemeType.index,
index: dynamicColorUnavailableReason == null
? settings.colorSchemeType.index
: ColorSchemeType.custom.index,
onChanged: settings.setColorSchemeType,
),
ListTile(
enabled: settings.colorSchemeType == ColorSchemeType.app,
enabled: canSelectSeedColor,
leading: Icon(
Icons.colorize,
color: settings.colorSchemeType == ColorSchemeType.app ? settings.seedColor : null,
color: canSelectSeedColor ? settings.seedColor : null,
),
title: const Text("Основной цвет"),
subtitle: Text(
settings.colorSchemeType == ColorSchemeType.app
canSelectSeedColor
? "#${(settings.seedColor.value & 0xFFFFFF).toRadixString(16).padLeft(6, "0")}"
: "Системный",
),
Expand Down
56 changes: 56 additions & 0 deletions lib/utils/color_scheme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import "package:dynamic_color/dynamic_color.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:material_color_utilities/material_color_utilities.dart";

Future<BrightnessAwareColorScheme> loadColorScheme({required Color fallbackSeedColor}) async {
try {
final corePalette = await DynamicColorPlugin.getCorePalette();

if (corePalette != null) {
return BrightnessAwareColorScheme.fromCorePalette(corePalette);
}
} on PlatformException {
// ignore
}

try {
final accentColor = await DynamicColorPlugin.getAccentColor();

if (accentColor != null) {
return BrightnessAwareColorScheme.fromAccentColor(accentColor, isDynamicColorSupported: true);
}
} on PlatformException {
// ignore
}

return BrightnessAwareColorScheme.fromAccentColor(fallbackSeedColor, isDynamicColorSupported: false);
}

class BrightnessAwareColorScheme {
final ColorScheme light;
final ColorScheme dark;
final bool isDynamicColorSupported;

BrightnessAwareColorScheme({
required this.light,
required this.dark,
required this.isDynamicColorSupported,
});

BrightnessAwareColorScheme.fromCorePalette(CorePalette palette)
: this(
light: palette.toColorScheme(brightness: Brightness.light),
dark: palette.toColorScheme(brightness: Brightness.dark),
isDynamicColorSupported: true,
);

BrightnessAwareColorScheme.fromAccentColor(
Color accentColor, {
required bool isDynamicColorSupported,
}) : this(
light: ColorScheme.fromSeed(seedColor: accentColor, brightness: Brightness.light),
dark: ColorScheme.fromSeed(seedColor: accentColor, brightness: Brightness.dark),
isDynamicColorSupported: isDynamicColorSupported,
);
}
6 changes: 3 additions & 3 deletions lib/utils/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ enum TimerType {

enum ColorSchemeType {
system,
app,
custom,
}

enum CheckUpdatesType {
Expand Down Expand Up @@ -99,8 +99,8 @@ Future<SettingsModel> getSettings() async {
switch (colorSchemeTypeString) {
case "system":
colorSchemeType = ColorSchemeType.system;
case "app":
colorSchemeType = ColorSchemeType.app;
case "app" || "custom":
colorSchemeType = ColorSchemeType.custom;
default:
assert(false, "Unknown color scheme type: $colorSchemeTypeString"); // fail for debug builds
colorSchemeType = defaults.colorSchemeType; // use default for release builds
Expand Down
35 changes: 35 additions & 0 deletions lib/widgets/color_scheme_wrapper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "package:flutter/material.dart";
import "package:provider/provider.dart";

import "../utils/color_scheme.dart";
import "../utils/settings.dart";

class BrightnessAwareColorSchemeBuilder extends StatelessWidget {
final Widget Function(BrightnessAwareColorScheme colorScheme) builder;

const BrightnessAwareColorSchemeBuilder({
super.key,
required this.builder,
});

@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsModel>();
final systemColorScheme = context.read<BrightnessAwareColorScheme>();
final customColorScheme = BrightnessAwareColorScheme.fromAccentColor(
settings.seedColor,
isDynamicColorSupported: systemColorScheme.isDynamicColorSupported,
);

if (!systemColorScheme.isDynamicColorSupported) {
return builder(customColorScheme);
}

switch (settings.colorSchemeType) {
case ColorSchemeType.system:
return builder(systemColorScheme);
case ColorSchemeType.custom:
return builder(customColorScheme);
}
}
}
7 changes: 6 additions & 1 deletion lib/widgets/list_tiles/choice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,23 @@ Future<T?> _showChoiceDialog<T>({
class ChoiceListTile<T> extends StatelessWidget {
final Widget? leading;
final Widget title;
final Widget? subtitle;
final List<T> items;
final ConverterFunction<T, String>? itemToString;
final int index;
final ValueChanged<T> onChanged;
final bool enabled;

const ChoiceListTile({
super.key,
this.leading,
required this.title,
this.subtitle,
required this.items,
this.itemToString,
required this.index,
required this.onChanged,
this.enabled = true,
});

String _itemToString(T item) => itemToString == null ? item.toString() : itemToString!(item);
Expand All @@ -73,9 +77,10 @@ class ChoiceListTile<T> extends StatelessWidget {

@override
Widget build(BuildContext context) => ListTile(
enabled: enabled,
leading: leading ?? const SizedBox.shrink(),
title: title,
subtitle: Text(_itemToString(items[index])),
subtitle: subtitle ?? Text(_itemToString(items[index])),
onTap: () => _onTileClick(context),
);
}
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ packages:
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
dependency: "direct main"
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies:
# TODO: upgrade once `flutter_localizations` is updated in sdk.
http: ^1.1.0
intl: ^0.18.1
material_color_utilities: ^0.8.0
package_info_plus: ^5.0.1
path: ^1.8.3
path_provider: ^2.1.1
Expand Down

0 comments on commit dc2b8ad

Please sign in to comment.