feat: introduce _SpacingWidget for conditional spacing in SDUIFormWidget, enhance visibility handling with reactive updates, and improve error handling for child widgets
This commit is contained in:
@@ -30,6 +30,101 @@ class _StepperInfo {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper widget that adds spacing only if previous widget is visible
|
||||||
|
class _SpacingWidget extends StatelessWidget {
|
||||||
|
final double spacing;
|
||||||
|
final String? previousVisibleCondition;
|
||||||
|
final RxMap<String, dynamic>? state;
|
||||||
|
|
||||||
|
const _SpacingWidget({
|
||||||
|
required this.spacing,
|
||||||
|
required this.previousVisibleCondition,
|
||||||
|
required this.state,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// If previous widget has no visible condition, it's always visible
|
||||||
|
if (previousVisibleCondition == null || previousVisibleCondition!.isEmpty) {
|
||||||
|
return SizedBox(height: spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Obx to reactively check if previous widget is visible
|
||||||
|
return Obx(() {
|
||||||
|
final isPreviousVisible = _evaluateVisibleConditionStatic(
|
||||||
|
previousVisibleCondition!,
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isPreviousVisible) {
|
||||||
|
return SizedBox(height: spacing);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static version of _evaluateVisibleCondition for use in helper widget
|
||||||
|
bool _evaluateVisibleConditionStatic(
|
||||||
|
String condition,
|
||||||
|
RxMap<String, dynamic>? state,
|
||||||
|
) {
|
||||||
|
if (state == null) {
|
||||||
|
if (condition.contains('activeStepperIndex == 0')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simple condition evaluation
|
||||||
|
// Supports: variable == value
|
||||||
|
|
||||||
|
if (condition.contains(' == ')) {
|
||||||
|
final parts = condition.split(' == ');
|
||||||
|
if (parts.length == 2) {
|
||||||
|
final variable = parts[0].trim();
|
||||||
|
var value = parts[1].trim();
|
||||||
|
|
||||||
|
// Remove quotes if present
|
||||||
|
if ((value.startsWith("'") && value.endsWith("'")) ||
|
||||||
|
(value.startsWith('"') && value.endsWith('"'))) {
|
||||||
|
value = value.substring(1, value.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
final stateValue = state[variable];
|
||||||
|
if (stateValue == null) {
|
||||||
|
// If variable doesn't exist in state, default to showing first step (0)
|
||||||
|
if (variable == 'activeStepperIndex' && value == '0') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle int comparison
|
||||||
|
final intValue = int.tryParse(value);
|
||||||
|
if (intValue != null) {
|
||||||
|
if (stateValue is int) {
|
||||||
|
return stateValue == intValue;
|
||||||
|
}
|
||||||
|
if (stateValue is num) {
|
||||||
|
return stateValue.toInt() == intValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle string comparison
|
||||||
|
return stateValue.toString() == value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If condition format is not recognized, return false
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SDUIFormWidget extends StatelessWidget {
|
class SDUIFormWidget extends StatelessWidget {
|
||||||
final SDUIWidgetModel model;
|
final SDUIWidgetModel model;
|
||||||
final Map<String, TextEditingController>? controllers;
|
final Map<String, TextEditingController>? controllers;
|
||||||
@@ -187,15 +282,25 @@ class SDUIFormWidget extends StatelessWidget {
|
|||||||
if (visibleCondition.contains('activeStepperIndex == 0')) {
|
if (visibleCondition.contains('activeStepperIndex == 0')) {
|
||||||
return builder();
|
return builder();
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
return Visibility(
|
||||||
|
visible: false,
|
||||||
|
maintainSize: false,
|
||||||
|
maintainState: false,
|
||||||
|
maintainAnimation: false,
|
||||||
|
child: builder(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Obx for reactive updates
|
// Use Obx for reactive updates
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
if (!_evaluateVisibleCondition(visibleCondition)) {
|
final isVisible = _evaluateVisibleCondition(visibleCondition);
|
||||||
return const SizedBox.shrink();
|
return Visibility(
|
||||||
}
|
visible: isVisible,
|
||||||
return builder();
|
maintainSize: false,
|
||||||
|
maintainState: false,
|
||||||
|
maintainAnimation: false,
|
||||||
|
child: builder(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +421,6 @@ class SDUIFormWidget extends StatelessWidget {
|
|||||||
double paddingVertical,
|
double paddingVertical,
|
||||||
) {
|
) {
|
||||||
if (children.isEmpty) {
|
if (children.isEmpty) {
|
||||||
iLog('Column has no children');
|
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,31 +432,84 @@ class SDUIFormWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment,
|
crossAxisAlignment,
|
||||||
);
|
);
|
||||||
|
|
||||||
final builtChildren = children.map((child) {
|
// Build all children first and extract their visible conditions
|
||||||
|
final builtChildren = <Widget>[];
|
||||||
|
final visibleConditions = <String?>[];
|
||||||
|
|
||||||
|
for (final child in children) {
|
||||||
try {
|
try {
|
||||||
return _buildWidget(child);
|
// Extract visible_condition from child model
|
||||||
|
final visibleCondition = child.maybeWhen(
|
||||||
|
column:
|
||||||
|
(
|
||||||
|
children,
|
||||||
|
spacing,
|
||||||
|
mainAxisSize,
|
||||||
|
crossAxisAlignment,
|
||||||
|
paddingHorizontal,
|
||||||
|
paddingVertical,
|
||||||
|
visible,
|
||||||
|
visibleCondition,
|
||||||
|
) => visibleCondition,
|
||||||
|
row:
|
||||||
|
(
|
||||||
|
children,
|
||||||
|
spacing,
|
||||||
|
mainAxisAlignment,
|
||||||
|
visible,
|
||||||
|
visibleCondition,
|
||||||
|
) => visibleCondition,
|
||||||
|
cardLabelItem: (data, child, children, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
stepper: (data, children, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
textFormField: (data, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
chipSelection: (data, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
dropdown: (data, visible, visibleCondition) => visibleCondition,
|
||||||
|
imagePicker: (data, visible, visibleCondition) => visibleCondition,
|
||||||
|
pageView: (data, children, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
sizedBox: (width, height, visible, visibleCondition) =>
|
||||||
|
visibleCondition,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
visibleConditions.add(visibleCondition);
|
||||||
|
builtChildren.add(_buildWidget(child));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
iLog('Error building column child: $e');
|
iLog('Error building column child: $e');
|
||||||
return Container(
|
visibleConditions.add(null);
|
||||||
|
builtChildren.add(
|
||||||
|
Container(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
color: Colors.yellow.withAlpha(10),
|
color: Colors.yellow.withAlpha(10),
|
||||||
child: Text('Child Error'),
|
child: Text('Child Error'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}).toList();
|
}
|
||||||
|
|
||||||
// Add spacing between children
|
// Add spacing between children
|
||||||
final columnChildren = spacing > 0 && builtChildren.length > 1
|
// Wrap each child (except first) with spacing that only shows if previous child is visible
|
||||||
? <Widget>[]
|
final columnChildren = <Widget>[];
|
||||||
: builtChildren;
|
|
||||||
|
|
||||||
if (spacing > 0 && builtChildren.length > 1) {
|
|
||||||
for (int i = 0; i < builtChildren.length; i++) {
|
for (int i = 0; i < builtChildren.length; i++) {
|
||||||
columnChildren.add(builtChildren[i]);
|
final widget = builtChildren[i];
|
||||||
if (i < builtChildren.length - 1) {
|
|
||||||
columnChildren.add(SizedBox(height: spacing));
|
if (i > 0 && spacing > 0) {
|
||||||
}
|
// Add spacing that will be removed if previous widget is invisible
|
||||||
|
// Use the visible_condition of the previous widget
|
||||||
|
columnChildren.add(
|
||||||
|
_SpacingWidget(
|
||||||
|
spacing: spacing,
|
||||||
|
previousVisibleCondition: visibleConditions[i - 1],
|
||||||
|
state: state,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
columnChildren.add(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
final column = Column(
|
final column = Column(
|
||||||
@@ -849,6 +1006,8 @@ class SDUIFormWidget extends StatelessWidget {
|
|||||||
'sdui_form_stepper_${stepperInfo.stepperData.key ?? 'default'}';
|
'sdui_form_stepper_${stepperInfo.stepperData.key ?? 'default'}';
|
||||||
SDUIFormWidgetController formController;
|
SDUIFormWidgetController formController;
|
||||||
|
|
||||||
|
fLog('mj ==>build With Stepper Layout');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
formController = Get.find<SDUIFormWidgetController>(tag: controllerTag);
|
formController = Get.find<SDUIFormWidgetController>(tag: controllerTag);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
@@ -931,6 +1090,7 @@ class SDUIFormWidget extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
spacing: 30.h,
|
||||||
children: contentWidgets,
|
children: contentWidgets,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user