Skip to content

Commit

Permalink
GladeModels notify dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
petrnymsa committed Mar 25, 2024
1 parent 71deb32 commit b531f09
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"dart.flutterSdkPath": ".fvm/versions/3.16.5",
"dart.lineLength": 120
"dart.lineLength": 120,
"yaml.schemaStore.enable": false
}
4 changes: 3 additions & 1 deletion glade_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
- **[Breaking]**: TextEditingController is no more created automatically. When TextEditingController is used, input's behavior is slightly changed. See README.md for full info.
- **[Breaking]**: GladeInput's controller is now private. Use factory constructors to create input.
- **[Breaking]**: `Extra` parameter removed
- **[Breaking]**: `dependencies` are no loonger passed into `onChange` and in validator.
- **[Breaking]**: `dependencies` are no longer passed into `onChange` and in validator.
- **[Add]**: onDependencyChange - callback when dependency was udpated.
- **Improvement**: GladeModelDebugInfo now colorize String values to visualize whitespace.

## 1.6.0
- **Improvement**: GladeModelDebugInfo is more colorful and polished.
Expand Down
36 changes: 7 additions & 29 deletions glade_forms/lib/src/core/glade_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ typedef ValueTransform<T> = T Function(T input);

typedef StringInput = GladeInput<String>;

T _defaultTransform<T>(T input) => input;

class GladeInput<T> extends ChangeNotifier {
class GladeInput<T> {
/// Compares initial and current value.
@protected
// ignore: prefer-correct-callback-field-name, ok name
Expand Down Expand Up @@ -64,7 +62,7 @@ class GladeInput<T> extends ChangeNotifier {

/// Transforms passed value before assigning it into input.
// ignore: prefer-correct-callback-field-name, ok name
ValueTransform<T> valueTransform;
final ValueTransform<T>? _valueTransform;

final bool _useTextEditingController;

Expand Down Expand Up @@ -92,6 +90,8 @@ class GladeInput<T> extends ChangeNotifier {

GladeModel? _bindedModel;

InputDependencies get dependencies => dependenciesFactory();

T? get initialValue => _initialValue;

TextEditingController? get controller => _textEditingController;
Expand Down Expand Up @@ -170,7 +170,7 @@ class GladeInput<T> extends ChangeNotifier {
_initialValue = initialValue,
dependenciesFactory = dependenciesFactory ?? (() => []),
inputKey = inputKey ?? '__${T.runtimeType}__${Random().nextInt(100000000)}',
valueTransform = valueTransform ?? _defaultTransform,
_valueTransform = valueTransform,
_textEditingController = textEditingController ??
(useTextEditingController
? TextEditingController(
Expand All @@ -188,12 +188,6 @@ class GladeInput<T> extends ChangeNotifier {
if (_useTextEditingController) {
_textEditingController?.addListener(_onTextControllerChange);
}

final dependencies = this.dependenciesFactory();

for (final dependency in dependencies) {
dependency.addListener(() => _onDependencyUpdate(dependency.inputKey));
}
}

/// At least one of [value] or [initialValue] MUST be set.
Expand Down Expand Up @@ -589,23 +583,14 @@ class GladeInput<T> extends ChangeNotifier {
onChange: onChange ?? this.onChange,
onDependencyChange: onDependencyChange ?? this.onDependencyChange,
textEditingController: textEditingController ?? this._textEditingController,
valueTransform: valueTransform ?? this.valueTransform,
valueTransform: valueTransform ?? this._valueTransform,
trackUnchanged: trackUnchanged ?? this.trackUnchanged,
);
}

@mustCallSuper
@override
void dispose() {
_textEditingController?.removeListener(_onTextControllerChange);

final dependencies = dependenciesFactory();

for (final dependency in dependencies) {
dependency.removeListener(() => _onDependencyUpdate(dependency.inputKey));
}

super.dispose();
}

void _syncValueWithController(T value, {required bool shouldTriggerOnChange}) {
Expand Down Expand Up @@ -639,8 +624,7 @@ class GladeInput<T> extends ChangeNotifier {

void _setValue(T value, {required bool shouldTriggerOnChange}) {
_previousValue = _value;

_value = valueTransform(value);
_value = _useTextEditingController ? value : (_valueTransform?.call(value) ?? value);

_isPure = false;
__conversionError = null;
Expand All @@ -659,12 +643,6 @@ class GladeInput<T> extends ChangeNotifier {
}

_bindedModel?.notifyInputUpdated(this);

notifyListeners();
}

void _onDependencyUpdate(String inputKey) {
onDependencyChange?.call(inputKey);
}

// *
Expand Down
13 changes: 13 additions & 0 deletions glade_forms/lib/src/model/glade_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ abstract class GladeModel extends ChangeNotifier {
_lastUpdates.add(input);
} else {
_lastUpdates = [input];
notifyDependecies();
notifyListeners();
}
}
Expand All @@ -110,6 +111,18 @@ abstract class GladeModel extends ChangeNotifier {

_groupEdit = false;

notifyDependecies();

notifyListeners();
}

void notifyDependecies() {
for (final updatedInput in _lastUpdates) {
for (final input in inputs) {
if (input.dependencies.any((i) => i.inputKey == updatedInput.inputKey)) {
input.onDependencyChange?.call(updatedInput.inputKey);
}
}
}
}
}
65 changes: 55 additions & 10 deletions glade_forms/lib/src/widgets/glade_model_debug_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,30 +80,40 @@ class GladeModelDebugInfo<M extends GladeModel> extends StatelessWidget {
onPressed: () {
final _ = showDialog<void>(
context: context,
builder: (context) => const AlertDialog(
builder: (context) => AlertDialog(
content: Column(
children: [
Text("Each column's value has tooltip. Use it to display full value."),
Text('Bool values with black color mark untracked values within model.'),
Row(
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close),
),
),
const Text("Each column's value has tooltip. Use it to display full value."),
const Text('Bool values with black color mark untracked values within model.'),
const Row(
children: [
Icon(Icons.check, color: Colors.green),
Text('True value, tracked'),
],
),
Row(
const Row(
children: [
Icon(Icons.close, color: Colors.red),
Text('False value, tracked'),
],
),
Row(
const Row(
children: [
Icon(Icons.check, color: Colors.black),
Icon(Icons.close, color: Colors.black),
Text('True/False untracked'),
],
),
const SizedBox(height: 20),
const Text('String value is annotated with colored background such as'),
const _StringValue(x: ' There is whitespace at the beginning.'),
],
),
),
Expand Down Expand Up @@ -214,9 +224,9 @@ class _Table extends StatelessWidget {
if (showIsValid) _RowValue(value: x.isValid),
if (showValidationError) _RowValue(value: x.errorFormatted()),
if (showConversionError) _RowValue(value: x.hasConversionError),
if (showValue) _RowValue(value: x.value),
if (showInitialValue) _RowValue(value: x.initialValue),
if (showControllerText) _RowValue(value: x.controller?.text),
if (showValue) _RowValue(value: x.value, colorizedValue: true),
if (showInitialValue) _RowValue(value: x.initialValue, colorizedValue: true),
if (showControllerText) _RowValue(value: x.controller?.text, colorizedValue: true),
],
),
),
Expand All @@ -242,8 +252,15 @@ class _RowValue extends StatelessWidget {
final bool tracked;
final bool wrap;
final bool center;
final bool colorizedValue;

const _RowValue({required this.value, this.wrap = false, this.tracked = true, this.center = true});
const _RowValue({
required this.value,
this.wrap = false,
this.tracked = true,
this.center = true,
this.colorizedValue = false,
});

@override
Widget build(BuildContext context) {
Expand All @@ -253,6 +270,19 @@ class _RowValue extends StatelessWidget {
return _BoolIcon(value: x, colorize: tracked);
}

if (value case final String? x when x != null && colorizedValue) {
return Align(
alignment: center ? Alignment.center : Alignment.centerLeft,
child: Tooltip(
message: x,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 200),
child: _StringValue(x: x),
),
),
);
}

return Align(
alignment: center ? Alignment.center : Alignment.centerLeft,
child: Tooltip(
Expand All @@ -270,6 +300,21 @@ class _RowValue extends StatelessWidget {
}
}

class _StringValue extends StatelessWidget {
final String? x;

const _StringValue({required this.x});

@override
Widget build(BuildContext context) {
return Text(
x ?? '',
style:
Theme.of(context).textTheme.bodyMedium?.copyWith(backgroundColor: const Color.fromARGB(255, 196, 222, 184)),
);
}
}

class _BoolIcon extends StatelessWidget {
final bool value;
final bool colorize;
Expand Down
2 changes: 1 addition & 1 deletion glade_forms/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: glade_forms
description: A universal way to define form validators with support of translations.
version: 1.6.0
version: 2.0.0
repository: https://github.com/netglade/glade_forms
issue_tracker: https://github.com/netglade/glade_forms/issues
screenshots:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ In this example both bussiness rules controll checkbox via onChange and onDepend
return UsecaseContainer(
shortDescription: "Age input depends on checkbox's value automatically",
description: markdownData,
className: 'checkbox_dependency_change.dart',
className: 'dependencies/checkbox_dependency_change.dart',
child: GladeFormBuilder.create(
// ignore: avoid-undisposed-instances, handled by GladeFormBuilder
create: (context) => AgeRestrictedModel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ If *VIP content* is checked, **age** must be over 18.
return UsecaseContainer(
shortDescription: "Age input validation depends on checkbox's value",
description: markdownData,
className: 'one_checkbox_deps_validation.dart',
className: 'onchange/one_checkbox_deps_validation.dart',
child: GladeFormBuilder.create(
// ignore: avoid-undisposed-instances, handled by GladeFormBuilder
create: (context) => AgeRestrictedModel(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ If *age* is changed to value under 18, *vip content* is unchecked and vice-versa
return UsecaseContainer(
shortDescription: "Age input depends on checkbox's value automatically",
description: markdownData,
className: 'two_way_checkbox_change.dart',
className: 'onchange/two_way_checkbox_change.dart',
child: GladeFormBuilder.create(
// ignore: avoid-undisposed-instances, handled by GladeFormBuilder
create: (context) => AgeRestrictedModel(),
Expand Down
3 changes: 3 additions & 0 deletions storybook/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ flutter:
assets:
- assets/translations/
- lib/usecases/
- lib/usecases/dependencies/
- lib/usecases/onchange/
- lib/usecases/regress/

0 comments on commit b531f09

Please sign in to comment.