Add settings page #46

Merged
Henry-Hiles merged 34 commits from settings-page into main 2026-07-17 20:36:42 -04:00
4 changed files with 79 additions and 21 deletions
Showing only changes of commit 2bf1e629b7 - Show all commits

settings fully working

Henry Hiles 2026-07-17 00:16:04 -04:00
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs

View file

@ -96,17 +96,31 @@ class App extends StatelessWidget {
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
// Use indigo to work around bugs in theme generation
theme: (lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.indigo))
.getTheme(
ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => true,
data: (settings) => settings.useSystemFont,
),
),
theme:
(ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => lightDynamic,
data: (settings) =>
settings.useDynamicTheming ? lightDynamic : null,
) ??
ColorScheme.fromSeed(seedColor: Colors.indigo))
.getTheme(
ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => true,
data: (settings) => settings.useSystemFont,
),
),
darkTheme:
(darkDynamic ??
(ref
.watch(SettingsController.provider)
.maybeWhen(
orElse: () => darkDynamic,
data: (settings) =>
settings.useDynamicTheming ? darkDynamic : null,
) ??
ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,

View file

@ -7,6 +7,7 @@ part "settings.g.dart";
abstract class Settings with _$Settings {
const factory Settings({
@Default(ThemeMode.light) ThemeMode theme,
@Default(true) bool useDynamicTheming,
@Default(false) bool useSystemFont,
}) = _Settings;

View file

@ -1,3 +1,5 @@
import "dart:io";
import "package:collection/collection.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart";
@ -36,7 +38,7 @@ class SettingsPage extends ConsumerWidget {
description:
"Toggle between Light Mode, Dark Mode, and System themes.",
builder: (title, description) => DialogListTile<ThemeMode>(
icon: Icon(Icons.palette),
icon: Icon(Icons.contrast),
title: title,
subtitle: Text(description),
initialValue: settings.theme,
@ -48,6 +50,27 @@ class SettingsPage extends ConsumerWidget {
.onError(showError),
),
),
Setting(
title: "Use Dynamic Theme",
description:
"Toggle on or off Dynamic Theme. Only available on Android, Linux, Windows, or MacOS.",
builder: (title, description) => SwitchListTile(
title: Text(title),
subtitle: Text(description),
secondary: Icon(Icons.palette),
value: settings.useDynamicTheming,
onChanged:
(Platform.isAndroid ||
Platform.isLinux ||
Platform.isMacOS ||
Platform.isWindows)
? (value) => ref
.watch(SettingsController.provider.notifier)
.set(settings.copyWith(useDynamicTheming: value))
.onError(showError)
: null,
),
),
Setting(
title: "Use System Font",
description:
@ -118,19 +141,37 @@ class SettingsPage extends ConsumerWidget {
context,
).colorScheme.primaryContainer,
itemCount: categories.length,
onTap: (index) =>
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text(
categories[index].title,
),
),
body: ListView(),
onTap: (index) => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text(
categories[index].title,
),
),
body: SuperListView(
padding: .symmetric(
vertical: 12,
horizontal: 8,
),
children: buildCategories(settings)
.values
.flattenedToList[selected.value]
.settings
.map(
(setting) => Padding(
padding: .only(bottom: 4),
child: setting.builder(
setting.title,
setting.description,
),
),
)
.toList(),
),
),
),
),
itemBuilder: (context, index) => ListTile(
leading: Icon(categories[index].icon),
title: Text(categories[index].title),

View file

@ -35,6 +35,7 @@ class DialogListTile<T> extends ConsumerWidget {
enabledBorder: InputBorder.none,
),
child: ListTile(
enabled: onChanged != null,
onTap: () => showDialog(
context: context,
builder: (context) => RadioDialog<T>(
@ -57,6 +58,7 @@ class DialogListTile<T> extends ConsumerWidget {
label: Text(
field.value == null ? "None" : getName(field.value as T),
overflow: TextOverflow.ellipsis,
style: onChanged == null ? .new(color: Colors.grey) : null,
),
),
),