diff --git a/packages/neon/neon/lib/src/settings/models/option.dart b/packages/neon/neon/lib/src/settings/models/option.dart index 54279ff9..10a36e81 100644 --- a/packages/neon/neon/lib/src/settings/models/option.dart +++ b/packages/neon/neon/lib/src/settings/models/option.dart @@ -71,6 +71,7 @@ sealed class Option extends ChangeNotifier implements ValueListenable, Dis /// listeners. @override T get value => _value; + @mustCallSuper set value(final T newValue) { if (_value == newValue) { @@ -88,6 +89,7 @@ sealed class Option extends ChangeNotifier implements ValueListenable, Dis /// value as evaluated by the equality operator ==, this class notifies its /// listeners. bool get enabled => _enabled; + @mustCallSuper set enabled(final bool newValue) { if (_enabled == newValue) { @@ -108,7 +110,9 @@ sealed class Option extends ChangeNotifier implements ValueListenable, Dis final value = deserialize(data); if (value != null) { - this.value = value; + // Do not trigger the validation to avoid resetting when the values haven't been loaded yet. + _value = value; + notifyListeners(); } } @@ -203,10 +207,14 @@ class SelectOption extends Option { @override set value(final T value) { - super.value = value; - - if (value != null) { - unawaited(storage.setString(key.value, serialize()!)); + if (_values.keys.contains(value)) { + super.value = value; + + if (value != null) { + unawaited(storage.setString(key.value, serialize()!)); + } + } else { + debugPrint('"$value" is not in "${_values.keys.join('", "')}", ignoring'); } } @@ -224,6 +232,10 @@ class SelectOption extends Option { return; } _values = newValues; + if (!_values.keys.contains(_value)) { + debugPrint('"$value" is not in "${_values.keys.join('", "')}", resetting "${key.value}"'); + reset(); + } notifyListeners(); } diff --git a/packages/neon/neon/test/option_test.dart b/packages/neon/neon/test/option_test.dart index 6e5268e4..0b7050e0 100644 --- a/packages/neon/neon/test/option_test.dart +++ b/packages/neon/neon/test/option_test.dart @@ -147,6 +147,21 @@ void main() { expect(option.values, newValues, reason: 'Should keep the values.'); }); + test('Invalid values', () { + expect(option.values, equals(valuesLabel)); + + option + ..value = SelectValues.second + ..values = { + SelectValues.first: (final _) => 'first', + SelectValues.third: (final _) => 'third', + }; + expect(option.value, SelectValues.first, reason: 'Invalid value.'); + + option.value = SelectValues.second; + expect(option.value, SelectValues.first, reason: 'Invalid value.'); + }); + test('Reset', () { final callback = MockCallbackFunction(); option