settings system

This commit is contained in:
2025-08-08 22:37:12 +02:00
parent f214a06fbf
commit 7cddcd29d6
4 changed files with 379 additions and 16 deletions

View File

@@ -7,6 +7,7 @@ class AppSettingsModel {
Id? id;
double? valueDouble;
bool? valueBool;
int? valueIndex;
String? valueString;
AppSettingsModel();

View File

@@ -5,21 +5,71 @@ import 'package:firka/ui/widget/firka_icon.dart';
import 'package:isar/isar.dart';
import 'package:majesticons_flutter/majesticons_flutter.dart';
const bellRing = 1_001;
const rounding1 = 1_002;
const rounding2 = 1_003;
const rounding3 = 1_004;
const rounding4 = 1_005;
const classAvgOnGraph = 1_006;
const leftHandedMode = 1_007;
const language = 1_008;
class SettingsStore {
LinkedHashMap<String, SettingsItem> items = LinkedHashMap.of({});
SettingsStore() {
items["settings"] = SettingsGroup(
0_001,
0,
LinkedHashMap.of({
"application": SettingsSubGroup(1_001, FirkaIconType.Majesticons,
Majesticon.settingsCogSolid, "Alkalmazás", []),
"customization": SettingsSubGroup(2_001, FirkaIconType.Majesticons,
Majesticon.flower2Solid, "Személyre szabás", []),
"notifications": SettingsSubGroup(1_001, FirkaIconType.Majesticons,
Majesticon.bellSolid, "Értesítések", []),
"extras": SettingsSubGroup(1_001, FirkaIconType.Majesticons,
Majesticon.lightningBoltSolid, "Extrák", []),
"settings_header": SettingsHeader(0, "Beállítások"),
"settings_padding": SettingsPadding(0, 20),
"application": SettingsSubGroup(
0,
FirkaIconType.Majesticons,
Majesticon.settingsCogSolid,
"Alkalmazás",
LinkedHashMap.of({
// TODO: Make a back arrow widget
"settings_header": SettingsHeader(0, "Általános"),
"settings_padding": SettingsPadding(0, 23),
"bell_delay": SettingsDouble(
bellRing, null, null, "Csengő eltolódása", 0),
"rounding_1": SettingsDouble(rounding1, null, null,
"Alapértelmezett kerekítés 1 -> 2", 0.5),
"rounding_2": SettingsDouble(rounding2, null, null,
"Alapértelmezett kerekítés 2 -> 3", 0.5),
"rounding_3": SettingsDouble(rounding3, null, null,
"Alapértelmezett kerekítés 3 -> 4", 0.5),
"rounding_4": SettingsDouble(rounding4, null, null,
"Alapértelmezett kerekítés 4 -> 5", 0.5),
"class_avg_on_graph": SettingsBoolean(classAvgOnGraph, null,
null, "Osztályátlag a grafikonon", true),
"navbar": SettingsSubGroup(
0,
null, // TODO: icon
null,
"Navigációs sáv",
LinkedHashMap.of({}),
),
"left_handed_mode": SettingsBoolean(
leftHandedMode, null, null, "Balkezes mód", false),
"language_header": SettingsHeaderSmall(0, "Nyelv"),
"language": SettingsItemsRadio(language, null, null,
["Autómatikus", "Magyar", "Angol", "Német"], 0)
})),
"customization": SettingsSubGroup(
0,
FirkaIconType.Majesticons,
Majesticon.flower2Solid,
"Személyre szabás",
LinkedHashMap.of({})),
"notifications": SettingsSubGroup(0, FirkaIconType.Majesticons,
Majesticon.bellSolid, "Értesítések", LinkedHashMap.of({})),
"extras": SettingsSubGroup(0, FirkaIconType.Majesticons,
Majesticon.lightningBoltSolid, "Extrák", LinkedHashMap.of({})),
"settings_other_padding": SettingsPadding(0, 20),
"settings_other_header": SettingsHeaderSmall(0, "Egyéb"),
}));
items;
@@ -74,8 +124,10 @@ extension SettingExt on LinkedHashMap<String, SettingsItem> {
class SettingsItem {
Id key;
FirkaIconType? iconType;
Object? iconData;
SettingsItem(this.key);
SettingsItem(this.key, this.iconType, this.iconData);
void save(IsarCollection<AppSettingsModel> model) {}
@@ -85,6 +137,10 @@ class SettingsItem {
class SettingsGroup implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
LinkedHashMap<String, SettingsItem> children;
SettingsGroup(this.key, this.children);
@@ -107,32 +163,56 @@ class SettingsGroup implements SettingsItem {
class SettingsSubGroup implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
List<SettingsItem> children;
LinkedHashMap<String, SettingsItem> children;
SettingsSubGroup(
this.key, this.iconType, this.iconData, this.title, this.children);
@override
void load(IsarCollection<AppSettingsModel> model) {
for (var item in children) {
for (var item in children.values) {
item.load(model);
}
}
@override
void save(IsarCollection<AppSettingsModel> model) {
for (var item in children) {
for (var item in children.values) {
item.save(model);
}
}
}
class SettingsPadding implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
double padding;
SettingsPadding(this.key, this.padding);
@override
void load(IsarCollection<AppSettingsModel> model) {}
@override
void save(IsarCollection<AppSettingsModel> model) {}
}
class SettingsHeader implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
SettingsHeader(this.key, this.title);
@@ -144,9 +224,31 @@ class SettingsHeader implements SettingsItem {
void save(IsarCollection<AppSettingsModel> model) {}
}
class SettingsHeaderSmall implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
SettingsHeaderSmall(this.key, this.title);
@override
void load(IsarCollection<AppSettingsModel> model) {}
@override
void save(IsarCollection<AppSettingsModel> model) {}
}
class SettingsSubtitle implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
SettingsSubtitle(this.key, this.title);
@@ -161,11 +263,16 @@ class SettingsSubtitle implements SettingsItem {
class SettingsBoolean implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
bool value = false;
bool defaultValue;
SettingsBoolean(this.key, this.title, this.defaultValue);
SettingsBoolean(
this.key, this.iconType, this.iconData, this.title, this.defaultValue);
@override
void load(IsarCollection<AppSettingsModel> model) {
@@ -187,14 +294,53 @@ class SettingsBoolean implements SettingsItem {
}
}
class SettingsItemsRadio implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
List<String> values;
int activeIndex = 0;
int defaultIndex;
SettingsItemsRadio(
this.key, this.iconType, this.iconData, this.values, this.defaultIndex);
@override
void load(IsarCollection<AppSettingsModel> model) {
var v = model.getSync(key);
if (v == null || v.valueIndex == null) {
activeIndex = v!.valueIndex!;
} else {
activeIndex = defaultIndex;
}
}
@override
void save(IsarCollection<AppSettingsModel> model) {
var v = AppSettingsModel();
v.id = key;
v.valueIndex = activeIndex;
model.put(v);
}
}
class SettingsDouble implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
double value = 0;
double defaultValue;
SettingsDouble(this.key, this.title, this.defaultValue);
SettingsDouble(
this.key, this.iconType, this.iconData, this.title, this.defaultValue);
@override
void load(IsarCollection<AppSettingsModel> model) {
@@ -219,11 +365,16 @@ class SettingsDouble implements SettingsItem {
class SettingsString implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
String title;
String value = "";
String defaultValue;
SettingsString(this.key, this.title, this.defaultValue);
SettingsString(
this.key, this.iconType, this.iconData, this.title, this.defaultValue);
@override
void load(IsarCollection<AppSettingsModel> model) {

View File

@@ -1,6 +1,7 @@
import 'package:firka/helpers/ui/firka_card.dart';
import 'package:firka/main.dart';
import 'package:firka/ui/model/style.dart';
import 'package:firka/ui/phone/screens/settings/settings_screen.dart';
import 'package:flutter/material.dart';
import '../../screens/debug/debug_screen.dart';
@@ -39,6 +40,7 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) {
children: [
GestureDetector(
onTap: () => {
Navigator.pop(context),
Navigator.push(
context,
MaterialPageRoute(
@@ -48,6 +50,20 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) {
left: [Text('Debug screen')],
right: [],
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SettingsScreen(data, data.settings.items)));
},
child: FirkaCard(
left: [Text('Settings')],
right: [],
),
)
],
),

View File

@@ -0,0 +1,195 @@
import 'dart:collection';
import 'package:firka/helpers/db/models/app_settings_model.dart';
import 'package:firka/helpers/ui/firka_card.dart';
import 'package:firka/main.dart';
import 'package:firka/ui/model/style.dart';
import 'package:firka/ui/widget/firka_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../../../helpers/settings/setting.dart';
class SettingsScreen extends StatefulWidget {
final AppInitialization data;
final LinkedHashMap<String, SettingsItem> items;
const SettingsScreen(this.data, this.items, {super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState(data, items);
}
class _SettingsScreenState extends State<SettingsScreen> {
final AppInitialization data;
final LinkedHashMap<String, SettingsItem> items;
_SettingsScreenState(this.data, this.items);
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateSystemUI();
});
}
void _updateSystemUI() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.dark,
statusBarColor: Colors.transparent,
systemNavigationBarColor: appStyle.colors.background,
systemNavigationBarIconBrightness: Brightness.dark,
systemNavigationBarDividerColor: Colors.transparent,
));
}
List<Widget> createWidgetTree(Iterable<SettingsItem> items) {
var widgets = List<Widget>.empty(growable: true);
for (var item in items) {
if (item is SettingsGroup) {
widgets.addAll(createWidgetTree(item.children.values));
}
if (item is SettingsPadding) {
widgets.add(SizedBox(
width: item.padding,
height: item.padding,
));
}
if (item is SettingsHeader) {
widgets.add(Text(
item.title,
style: appStyle.fonts.H_H1.apply(color: appStyle.colors.textPrimary),
));
}
if (item is SettingsHeaderSmall) {
widgets.add(Text(
item.title,
style:
appStyle.fonts.H_14px.apply(color: appStyle.colors.textPrimary),
));
}
if (item is SettingsSubGroup) {
List<Widget> cardWidgets = [];
if (item.iconType != null && item.iconData != null) {
cardWidgets.add(FirkaIconWidget(
item.iconType!,
item.iconData!,
color: appStyle.colors.accent,
));
cardWidgets.add(SizedBox(width: 8));
}
cardWidgets.add(Text(item.title,
style: appStyle.fonts.B_14SB
.apply(color: appStyle.colors.textPrimary)));
widgets.add(GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingsScreen(data, item.children)));
},
child: FirkaCard(left: cardWidgets),
));
}
if (item is SettingsDouble) {
var v = item.value.toStringAsPrecision(2);
widgets.add(FirkaCard(left: [
Text(item.title,
style: appStyle.fonts.B_16SB
.apply(color: appStyle.colors.textPrimary))
], right: [
Text(v == "0.0" ? "0" : v,
style: appStyle.fonts.B_14R
.apply(color: appStyle.colors.textPrimary))
]));
}
if (item is SettingsBoolean) {
widgets.add(FirkaCard(
left: [
Text(item.title,
style: appStyle.fonts.B_16SB
.apply(color: appStyle.colors.textPrimary))
],
right: [
Switch(
value: item.value,
activeColor: appStyle.colors.accent,
onChanged: (v) {
setState(() {
item.value = v;
});
data.isar.writeTxn(() async {
item.save(data.isar.appSettingsModels);
});
})
],
));
}
if (item is SettingsItemsRadio) {
for (var i = 0; i < item.values.length; i++) {
var k = item.values[i];
widgets.add(FirkaCard(left: [
Text(k,
style: appStyle.fonts.B_16R
.apply(color: appStyle.colors.textPrimary))
], right: [
Checkbox(
value: item.values[item.activeIndex] == k,
fillColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return appStyle.colors.secondary;
}),
onChanged: (_) {
setState(() {
item.activeIndex = i;
});
data.isar.writeTxn(() async {
item.save(data.isar.appSettingsModels);
});
})
]));
}
}
}
return widgets;
}
@override
Widget build(BuildContext context) {
_updateSystemUI(); // Update system UI on every build, to compensate for the android system being dumb
var body = createWidgetTree(items.values);
return Scaffold(
backgroundColor: appStyle.colors.background,
body: SafeArea(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Padding(
padding: EdgeInsetsGeometry.all(20),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: body)),
)
],
),
),
),
);
}
}