Feature: Add feature toggles and settings for modular features (e.g., Car, Inventory), enhance navigation for mobile/desktop, and improve i18n integration.

This commit is contained in:
2025-09-27 13:37:43 +02:00
parent 8ca98d4720
commit 8fa071e565
13 changed files with 545 additions and 81 deletions

View File

@@ -0,0 +1,116 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttery/fluttery.dart';
import 'package:fluttery/preferences.dart';
import 'package:app/core/i18n/translations.g.dart';
/// Define all toggleable features here.
/// You can freely add more later.
enum AppFeature {
inventory,
car,
household, // incl. budget
reports,
}
extension AppFeatureKey on AppFeature {
String get prefKey {
switch (this) {
case AppFeature.inventory:
return 'feature.inventory.enabled';
case AppFeature.car:
return 'feature.car.enabled';
case AppFeature.household:
return 'feature.household.enabled';
case AppFeature.reports:
return 'feature.reports.enabled';
}
}
/// Optional: default state if the pref isn't set yet
bool get defaultEnabled {
switch (this) {
case AppFeature.inventory:
return true;
case AppFeature.car:
return false;
case AppFeature.household:
return true;
case AppFeature.reports:
return true;
}
}
/// Human-readable name for UI using translations
String displayName(BuildContext context) {
final t = Translations.of(context);
switch (this) {
case AppFeature.inventory:
return t.features.inventory.displayName;
case AppFeature.car:
return t.features.car.displayName;
case AppFeature.household:
return t.features.household.displayName;
case AppFeature.reports:
return t.features.reports.displayName;
}
}
/// Description/help text shown below the switch using translations
String description(BuildContext context) {
final t = Translations.of(context);
switch (this) {
case AppFeature.inventory:
return t.features.inventory.description;
case AppFeature.car:
return t.features.car.description;
case AppFeature.household:
return t.features.household.description;
case AppFeature.reports:
return t.features.reports.description;
}
}
/// Optional: an icon to show in list tiles (import material in the view)
}
/// Controller pattern like your other controllers (ChangeNotifier + init)
class FeatureController extends ChangeNotifier {
final Preferences _prefs;
FeatureController() : _prefs = App.service<Preferences>();
/// In-memory cache of feature states
final Map<AppFeature, bool> _enabled = {
for (final f in AppFeature.values) f: f.defaultEnabled,
};
/// Call during app bootstrap (similar to other controllers).
Future<void> init() async {
for (final f in AppFeature.values) {
final v = await _prefs.getBool(f.prefKey);
_enabled[f] = v ?? true;
}
}
bool isEnabled(AppFeature feature) =>
_enabled[feature] ?? feature.defaultEnabled;
/// Convenience map for UI bindings
Map<AppFeature, bool> get allStates => Map.unmodifiable(_enabled);
Future<void> setEnabled(AppFeature feature, bool value) async {
_enabled[feature] = value;
await _prefs.setBool(feature.prefKey, value);
notifyListeners();
}
/// Optional helper: filter routes/features in the app shell, etc.
bool get hasInventory => isEnabled(AppFeature.inventory);
bool get hasCar => isEnabled(AppFeature.car);
bool get hasHousehold => isEnabled(AppFeature.household);
bool get hasReports => isEnabled(AppFeature.reports);
}