From 35b6822477347eb2570ad423872aaeb4a6d11c5a Mon Sep 17 00:00:00 2001 From: Anthony Date: Sun, 29 Dec 2024 19:59:36 +0700 Subject: [PATCH] v6.13.0 --- CHANGELOG.md | 7 + example/pubspec.lock | 2 +- lib/helpers/extensions.dart | 18 +++ lib/helpers/loading_style.dart | 7 +- lib/validation/rules.dart | 40 ++--- lib/widgets/form/form.dart | 250 +++++++++++++------------------- lib/widgets/form/form_data.dart | 7 + pubspec.yaml | 2 +- 8 files changed, 157 insertions(+), 176 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc1606..895afa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [6.13.0] - 2024-12-29 + +* Fix validation max and min rules +* New `init` method added to Forms +* Small refactor to `NyForm` class +* Update loadingStyle `render` method + ## [6.12.2] - 2024-12-22 * Fix stateName in NyState diff --git a/example/pubspec.lock b/example/pubspec.lock index f7d1708..234655a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -411,7 +411,7 @@ packages: path: ".." relative: true source: path - version: "6.12.1" + version: "6.13.0" path: dependency: transitive description: diff --git a/lib/helpers/extensions.dart b/lib/helpers/extensions.dart index 94a40f3..fb456f2 100644 --- a/lib/helpers/extensions.dart +++ b/lib/helpers/extensions.dart @@ -101,6 +101,24 @@ extension NyInt on int? { } } +/// Extensions for List +extension NyListWidget on List { + /// Add a gap between each child. + List withGap(double space) { + assert(space >= 0, 'Space should be a non-negative value.'); + + List newChildren = []; + for (int i = 0; i < length; i++) { + newChildren.add(this[i]); + if (i < length - 1) { + newChildren.add(SizedBox(height: space)); + } + } + + return newChildren; + } +} + /// Extensions for [Map] extension NyMap on Map? { /// dump the value to the console. [tag] is optional. diff --git a/lib/helpers/loading_style.dart b/lib/helpers/loading_style.dart index 4c28471..1ed8c56 100644 --- a/lib/helpers/loading_style.dart +++ b/lib/helpers/loading_style.dart @@ -41,14 +41,15 @@ class LoadingStyle { skeletonizerEffect = null; /// Render the loading widget - Widget render() { + Widget render({Widget? child}) { switch (type) { case LoadingStyleType.normal: - return child ?? Nylo.appLoader(); + if (child != null) return child; + return this.child ?? Nylo.appLoader(); case LoadingStyleType.skeletonizer: return Skeletonizer( enabled: true, - child: child ?? Nylo.appLoader(), + child: child ?? (this.child ?? Nylo.appLoader()), ); case LoadingStyleType.none: return SizedBox.shrink(); diff --git a/lib/validation/rules.dart b/lib/validation/rules.dart index 6e55ade..ff96a43 100644 --- a/lib/validation/rules.dart +++ b/lib/validation/rules.dart @@ -448,35 +448,35 @@ class MaxRule extends ValidationRule { "$attribute must be a maximum length of $intMatch characters."; textFieldMessage = "Must be a maximum of $intMatch characters."; super.handle(info); - return (data.length < intMatch); + return (data.length <= intMatch); } if (data is int) { description = "$attribute must be a maximum of $intMatch."; textFieldMessage = "Must be a maximum of $intMatch."; super.handle(info); - return (data < intMatch); + return (data <= intMatch); } if (data is List) { description = "$attribute must be a maximum of $intMatch."; textFieldMessage = "Must be a maximum of $intMatch."; super.handle(info); - return (data.length < intMatch); + return (data.length <= intMatch); } if (data is Map) { description = "$attribute must be a maximum of $intMatch."; textFieldMessage = "Must be a maximum of $intMatch."; super.handle(info); - return (data.length < intMatch); + return (data.length <= intMatch); } if (data is double) { description = "$attribute must be a maximum of $intMatch."; textFieldMessage = "Must be a maximum of $intMatch."; super.handle(info); - return (data < intMatch); + return (data <= intMatch); } return false; @@ -505,38 +505,38 @@ class MinRule extends ValidationRule { dynamic data = info['data']; if (data is String) { description = - "$attribute must be a maximum length of $intMatch characters."; - textFieldMessage = "Must be a maximum of $intMatch characters."; + "$attribute must be a minimum length of $intMatch characters."; + textFieldMessage = "Must be a minimum of $intMatch characters."; super.handle(info); - return (data.length > intMatch); + return (data.length >= intMatch); } if (data is int) { - description = "$attribute must be a maximum of $intMatch."; - textFieldMessage = "Must be a maximum of $intMatch."; + description = "$attribute must be a minimum of $intMatch."; + textFieldMessage = "Must be a minimum of $intMatch."; super.handle(info); - return (data > intMatch); + return (data >= intMatch); } if (data is List) { - description = "$attribute must be a maximum of $intMatch."; - textFieldMessage = "Must be a maximum of $intMatch."; + description = "$attribute must be a minimum of $intMatch."; + textFieldMessage = "Must be a minimum of $intMatch."; super.handle(info); - return (data.length > intMatch); + return (data.length >= intMatch); } if (data is Map) { - description = "$attribute must be a maximum of $intMatch."; - textFieldMessage = "Must be a maximum of $intMatch."; + description = "$attribute must be a minimum of $intMatch."; + textFieldMessage = "Must be a minimum of $intMatch."; super.handle(info); - return (data.length > intMatch); + return (data.length >= intMatch); } if (data is double) { - description = "$attribute must be a maximum of $intMatch."; - textFieldMessage = "Must be a maximum of $intMatch."; + description = "$attribute must be a minimum of $intMatch."; + textFieldMessage = "Must be a minimum of $intMatch."; super.handle(info); - return (data > intMatch); + return (data >= intMatch); } return false; } diff --git a/lib/widgets/form/form.dart b/lib/widgets/form/form.dart index 59b6ce9..caa9bc9 100644 --- a/lib/widgets/form/form.dart +++ b/lib/widgets/form/form.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '/widgets/ny_future_builder.dart'; import '/helpers/extensions.dart'; import '/helpers/loading_style.dart'; import 'package:recase/recase.dart'; @@ -7,7 +8,6 @@ import '/forms/ny_login_form.dart'; import '/helpers/helper.dart'; import '/nylo.dart'; import '/widgets/ny_form.dart'; -import '/widgets/ny_list_view.dart'; import '/widgets/ny_state.dart'; import '/widgets/ny_text_field.dart'; import 'form_item.dart'; @@ -58,10 +58,12 @@ class NyForm extends StatefulWidget { this.footer, this.headerSpacing = 10, this.footerSpacing = 10, - this.loading, + @Deprecated('Use loadingStyle instead') this.loading, + LoadingStyle? loadingStyle, this.locked = false}) : form = form..setData(initialData ?? {}, refreshState: false), type = "form", + loadingStyle = loadingStyle ?? LoadingStyle.skeletonizer(), children = null; /// Create a form with children @@ -87,9 +89,11 @@ class NyForm extends StatefulWidget { this.footer, this.headerSpacing = 10, this.footerSpacing = 10, - this.loading, + @Deprecated('Use loadingStyle instead') this.loading, + LoadingStyle? loadingStyle, this.locked = false}) : form = form..setData(initialData ?? {}, refreshState: false), + loadingStyle = loadingStyle ?? LoadingStyle.skeletonizer(), type = "list"; /// The type of form @@ -113,6 +117,9 @@ class NyForm extends StatefulWidget { /// The loading widget, defaults to skeleton final Widget? loading; + /// The loading style + final LoadingStyle loadingStyle; + /// Get the state name static state(String stateName) { return "form_$stateName"; @@ -302,7 +309,7 @@ class _NyFormState extends NyState { Map dummyData = widget.form.getDummyData; if (dummyData.containsKey(field.key)) { dummyDataValue = dummyData[field.key]; - if (dummyDataValue != null) { + if (dummyDataValue != null && value == null) { widget.form .setFieldValue(field.key, dummyDataValue, refreshState: false); } @@ -373,13 +380,11 @@ class _NyFormState extends NyState { if (data is Map && data.containsKey('action')) { if (data['action'] == 'refresh') { _construct(); - NyListView.stateReset("${stateName!}_ny_grid"); return; } if (data['action'] == 'clear') { widget.form.clear(refreshState: false); _construct(); - NyListView.stateReset("${stateName!}_ny_grid"); return; } if (data['action'] == 'setValue') { @@ -390,7 +395,6 @@ class _NyFormState extends NyState { }); _construct(); setState(() {}); - NyListView.stateReset("${stateName!}_ny_grid"); return; } @@ -404,7 +408,6 @@ class _NyFormState extends NyState { }); _construct(); setState(() {}); - NyListView.stateReset("${stateName!}_ny_grid"); return; } if (["hideField", "showField"].contains(data['action'])) { @@ -422,7 +425,6 @@ class _NyFormState extends NyState { return child; }); setState(() {}); - NyListView.stateReset("${stateName!}_ny_grid"); return; } @@ -430,7 +432,6 @@ class _NyFormState extends NyState { _construct(); initialFormData = null; setState(() {}); - NyListView.stateReset("${stateName!}_ny_grid"); return; } return; @@ -483,144 +484,69 @@ class _NyFormState extends NyState { @override Widget view(BuildContext context) { - dynamic data; + /// If the form has initial data, construct the form + if (initialFormData != null) { + _construct(); + return _createWidget(IgnorePointer( + ignoring: widget.locked, + child: ListView( + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true, + children: _createForm(), + ), + )); + } + + if (widget.form.getLoadData is! Future Function() && + initialFormData == null) { + initialFormData = widget.form.getLoadData!(); + widget.form.setData(initialFormData, refreshState: false); + } if (widget.form.getLoadData is Future Function()) { - data = () async { + // ignore: prefer_function_declarations_over_variables + dynamic formData = () async { if (initialFormData == null) { dynamic data = await widget.form.getLoadData!(); initialFormData = data; - widget.form.formReady(); widget.form.setData(data, refreshState: false); } _construct(); - - List items = []; - List> groupedItems = widget.form.groupedItems; - List childrenNotHidden = _children.where((test) { - if (test.field.hidden == false) { - return true; - } - if (showableFields.contains(test.field.name)) { - return true; - } - return false; - }).toList(); - - for (List listItems in groupedItems) { - if (listItems.length == 1) { - List allItems = childrenNotHidden - .where((test) => test.field.name == listItems[0]) - .toList(); - if (allItems.isNotEmpty) { - items.add(allItems.first); - } - continue; - } - - List childrenRowWidgets = [ - for (String action in listItems) - (childrenNotHidden - .where((test) => test.field.name == action) - .isNotEmpty) - ? Flexible( - child: childrenNotHidden - .where((test) => test.field.name == action) - .isNotEmpty - ? childrenNotHidden - .where((test) => test.field.name == action) - .first - : const SizedBox.shrink()) - : const SizedBox.shrink() - ]; - - Row row = Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: childrenRowWidgets, - ).withGap(childrenRowWidgets - .where((element) => element.runtimeType == SizedBox) - .isNotEmpty - ? 0 - : widget.mainAxisSpacing); - - items.add(row); - } - - return items; + return _createForm(); }; - } else { - if (widget.form.getLoadData is Function()) { - if (initialFormData == null) { - initialFormData = widget.form.getLoadData!(); - widget.form.setData(widget.form.getLoadData!(), refreshState: false); - } - _construct(); - } - data = () { - List items = []; - List> groupedItems = widget.form.groupedItems; - List childrenNotHidden = - _children.where((test) => test.field.hidden == false).toList(); - for (List listItems in groupedItems) { - if (listItems.length == 1) { - List allItems = childrenNotHidden - .where((test) => test.field.name == listItems[0]) - .toList(); - if (allItems.isNotEmpty) { - items.add(allItems.first); - } - continue; - } - - List childrenRowWidgets = [ - for (String action in listItems) - (childrenNotHidden - .where((test) => test.field.name == action) - .isNotEmpty) - ? Flexible( - child: childrenNotHidden - .where((test) => test.field.name == action) - .isNotEmpty - ? childrenNotHidden - .where((test) => test.field.name == action) - .first - : const SizedBox.shrink()) - : const SizedBox.shrink() - ]; - - Row row = Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: childrenRowWidgets, - ).withGap(childrenRowWidgets - .where((element) => element.runtimeType == SizedBox) - .isNotEmpty - ? 0 - : widget.mainAxisSpacing); - - items.add(row); - } - return items; - }; - widget.form.formReady(); + return _createWidget(IgnorePointer( + ignoring: widget.locked, + child: NyFutureBuilder>( + future: formData(), + child: (context, data) { + widget.form.formReady(); + return ListView( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + children: [if (data != null) ...data], + ); + }, + loadingStyle: LoadingStyle.normal(child: loadingWidget())), + )); } - Widget widgetForm = IgnorePointer( + _construct(); + + return _createWidget(IgnorePointer( ignoring: widget.locked, - child: NyListView.grid( - stateName: "${stateName!}_ny_grid", - mainAxisSpacing: widget.crossAxisSpacing, - crossAxisCount: 1, + child: ListView( physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, shrinkWrap: true, - child: (context, item) => item, - data: data, - loadingStyle: LoadingStyle.normal(child: loadingWidget()), + children: _createForm(), ), - ); + )); + } + /// Create the widget + Widget _createWidget(Widget widgetForm) { if (widget.type == "list") { return Column( children: [ @@ -656,14 +582,30 @@ class _NyFormState extends NyState { if (widget.loading != null) { return widget.loading!; } + + return widget.loadingStyle.render( + child: Column( + children: _createForm(), + )); + } + + List _createForm() { List items = []; List> groupedItems = widget.form.groupedItems; + List childrenNotHidden = _children.where((test) { + if (test.field.hidden == false) { + return true; + } + if (showableFields.contains(test.field.name)) { + return true; + } + return false; + }).toList(); for (List listItems in groupedItems) { if (listItems.length == 1) { - List allItems = _children - .where((test) => - test.field.name == listItems[0] && test.field.hidden == false) + List allItems = childrenNotHidden + .where((test) => test.field.name == listItems[0]) .toList(); if (allItems.isNotEmpty) { items.add(allItems.first); @@ -671,30 +613,36 @@ class _NyFormState extends NyState { continue; } + List childrenRowWidgets = [ + for (String action in listItems) + (childrenNotHidden + .where((test) => test.field.name == action) + .isNotEmpty) + ? Flexible( + child: childrenNotHidden + .where((test) => test.field.name == action) + .isNotEmpty + ? childrenNotHidden + .where((test) => test.field.name == action) + .first + : const SizedBox.shrink()) + : const SizedBox.shrink() + ]; + Row row = Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, - children: [ - for (String action in listItems) - if (_children - .where((test) => - test.field.name == action && test.field.hidden == false) - .isNotEmpty) - Flexible( - child: _children - .where((test) => - test.field.name == action && - test.field.hidden == false) - .first), - ], - ).withGap(widget.mainAxisSpacing); + children: childrenRowWidgets, + ).withGap(childrenRowWidgets + .where((element) => element.runtimeType == SizedBox) + .isNotEmpty + ? 0 + : widget.mainAxisSpacing); items.add(row); } - return Column( - children: items, - ).withGap(widget.crossAxisSpacing).toSkeleton(); + return items.withGap(widget.crossAxisSpacing); } } diff --git a/lib/widgets/form/form_data.dart b/lib/widgets/form/form_data.dart index 97a2718..658a645 100644 --- a/lib/widgets/form/form_data.dart +++ b/lib/widgets/form/form_data.dart @@ -142,6 +142,10 @@ class NyFormData { _keys.add(formField.key); } + if (init != null) { + initialData(init!); + } + setData(allData, refreshState: false); if (getEnv('APP_ENV') != 'developing') { return; @@ -239,6 +243,9 @@ class NyFormData { /// Returns the load data function for the form Function()? _loadData; + /// Initialize the form + Function()? get init => null; + /// Check if the form is ready formReady() { _ready.add(true); diff --git a/pubspec.yaml b/pubspec.yaml index c35e5c4..55971a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: nylo_support description: Support library for the Nylo framework. This library supports routing, widgets, localization, cli, storage and more. -version: 6.12.2 +version: 6.13.0 homepage: https://nylo.dev repository: https://github.com/nylo-core/support/tree/6.x issue_tracker: https://github.com/nylo-core/support/issues