A game or app settings oriented wrapper API for shared_preferences (with cache), type-safe settings framework for Flutter applications with automatic validation, change notifications, and modular design.
Note: The same warnings and caveats apply as with the original shared_preferences
package, such as not using it for sensitive data or large datasets.
- Optional Type Safety. Compile-time type checking for all setting values
- Instance or Global/Static Usage. Use settings as a singleton or create an instance (e.g. in isolates)
- Validation. Optional validators ensure data integrity
- Change Notifications. Listen for changes in settings values
- Hierarchical Organization. Group related settings together (e.g. app settings, UI settings)
- Automatic Persistence Settings are automatically saved to device storage (obviously with the same caveats as shared_preferences)
Add this to your package's pubspec.yaml
file:
dependencies:
easy_shared_preferences: <latest_version>
Then run:
flutter pub get
import 'package:easy_shared_preferences/easy_shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize global settings early in main()
await GlobalSettings.initialize([
// Game settings group
GroupConfig(
key: 'game',
items: [
BoolSetting(key: 'soundEnabled', defaultValue: true),
DoubleSetting(
key: 'volume',
defaultValue: 0.8,
validator: (value) => value >= 0.0 && value <= 1.0,
),
IntSetting(
key: 'difficulty',
defaultValue: 1,
validator: (value) => value >= 1 && value <= 3,
),
],
),
// UI settings group
GroupConfig(
key: 'ui',
items: [
StringSetting(
key: 'theme',
defaultValue: 'light',
validator: (value) => ['light', 'dark', 'auto'].contains(value),
),
BoolSetting(key: 'showAnimations', defaultValue: true),
IntSetting(
key: 'fontSize',
defaultValue: 14,
validator: (value) => value >= 12 && value <= 24,
),
],
),
], enableLogging: true);
// Now you can use GlobalSettings anywhere in your app
// For example, to get a setting value:
bool soundEnabled = GlobalSettings.getBool('game.soundEnabled');
runApp(MyApp());
}
import 'package:easy_shared_preferences/easy_shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Create the settings store and manager
final store = SettingsStore();
final settings = EasySettings(store: store);
// Define your settings groups
final gameSettings = SettingsGroup(
key: 'game',
items: [
BoolSetting(key: 'soundEnabled', defaultValue: true),
DoubleSetting(
key: 'volume',
defaultValue: 0.8,
validator: CommonValidators.percentage.validate,
),
IntSetting(key: 'difficulty', defaultValue: 1),
],
store: store,
);
final uiSettings = SettingsGroup(
key: 'ui',
items: [
StringSetting(
key: 'theme',
defaultValue: 'light',
validator: EnumValidator<String>(['light', 'dark', 'auto']).validate,
),
BoolSetting(key: 'notifications', defaultValue: true),
],
store: store,
);
// Register and initialize
settings.register(gameSettings);
settings.register(uiSettings);
await settings.init();
runApp(MyApp());
}
// Read settings
bool soundEnabled = settings.getBool('game.soundEnabled');
double volume = settings.getDouble('game.volume');
String theme = settings.getString('ui.theme');
// Write settings
await settings.setBool('game.soundEnabled', false);
await settings.setDouble('game.volume', 0.5);
await settings.setString('ui.theme', 'dark');
// Batch operations
await settings.setMultiple({
'game.soundEnabled': false,
'game.volume': 0.3,
'ui.theme': 'dark',
});
// Change callbacks
settings.addChangeCallback((key, oldValue, newValue) {
print('Setting $key changed from $oldValue to $newValue');
});
// Don't forget to dispose when done!
settings.dispose();
Settings support optional validation using built-in validator classes:
final volumeSetting = DoubleSetting(
key: 'volume',
defaultValue: 0.5,
validator: CommonValidators.percentage.validate, // 0.0 to 1.0
);
final themeSetting = StringSetting(
key: 'theme',
defaultValue: 'light',
validator: EnumValidator<String>(['light', 'dark', 'auto']).validate,
);
final emailSetting = StringSetting(
key: 'email',
defaultValue: '',
validator: CommonValidators.email.validate,
);
final passwordSetting = StringSetting(
key: 'password',
defaultValue: '',
validator: CompositeValidator<String>.and([
LengthValidator(minLength: 8, maxLength: 50),
RegexValidator(r'\d', customDescription: 'Must contain at least one digit'),
]).validate,
);
Listen to setting changes with streams:
gameSettings['soundEnabled']?.stream.listen((enabled) {
print('Sound ${enabled ? 'enabled' : 'disabled'}');
updateAudioEngine(enabled);
});
uiSettings['theme']?.stream.listen((theme) {
print('Theme changed to: $theme');
updateAppTheme(theme);
});
Some settings can be marked as read-only:
final systemSetting = BoolSetting(
key: 'debugMode',
defaultValue: false,
userConfigurable: false, // Cannot be modified by user code
);
// Reset a single setting
await settings.resetSetting('game.volume');
// Reset an entire group
await settings.resetGroup('ui');
// Reset all settings
await settings.resetAll();
The framework includes built-in testing support:
void main() {
group('Settings Tests', () {
late EasySettings settings;
setUp(() {
settings = EasySettings();
settings.register(SettingsGroup.forTesting(
key: 'test',
items: [
BoolSetting(key: 'testFlag', defaultValue: false),
],
));
});
test('should set and get values correctly', () async {
await settings.init();
await settings.setBool('test.testFlag', true);
expect(settings.getBool('test.testFlag'), true);
});
});
}
BoolSetting
: Boolean values (true/false)IntSetting
: Integer numbersDoubleSetting
: Floating-point numbersStringSetting
: Text valuesStringListSetting
: List of strings
EasySettings()
: Main settings managerSettingsGroup()
: Container for related settingssettings.register()
: Register a settings groupsettings.init()
: Initialize the settings systemsettings.getBool()
,getInt()
,getDouble()
,getString()
,getStringList()
: Type-safe getterssettings.setBool()
,setInt()
,setDouble()
,setString()
,setStringList()
: Type-safe setterssettings.setMultiple()
: Batch operationssettings.resetSetting()
,resetGroup()
,resetAll()
: Reset operations
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.