From a997a7640ed7a04ac5dd346022046c0c3186d7dd Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sun, 11 Oct 2026 12:09:56 +0300 Subject: [PATCH] fix(grid): eliminate redundant debounce timer and duplicate validation in DataGridValuePanel (#1431) --- .../workspace/data_grid_value_panel.dart | 28 +++++++++++--- .../workspace/data_grid_value_panel_test.dart | 37 +++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/features/workspace/data_grid_value_panel.dart b/lib/features/workspace/data_grid_value_panel.dart index 7729c640..addaf33b 100644 --- a/lib/features/workspace/data_grid_value_panel.dart +++ b/lib/features/workspace/data_grid_value_panel.dart @@ -48,6 +48,7 @@ class _DataGridValuePanelState extends material.State { bool _wordWrap = true; bool _preserveCompact = false; Timer? _debounceTimer; + bool _isProgrammaticUpdate = false; static bool _isCompact(String input) { final trimmed = input.trim(); @@ -83,7 +84,19 @@ class _DataGridValuePanelState extends material.State { _validateContent(); } + void _setControllerTextProgrammatically(String text) { + if (_controller.text == text) return; + _isProgrammaticUpdate = true; + try { + _controller.text = text; + } finally { + _isProgrammaticUpdate = false; + _debounceTimer?.cancel(); + } + } + void _onTextChanged() { + if (_isProgrammaticUpdate) return; _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 150), () { if (mounted) { @@ -96,10 +109,9 @@ class _DataGridValuePanelState extends material.State { void didUpdateWidget(covariant DataGridValuePanel oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.cellValue != widget.cellValue) { - _debounceTimer?.cancel(); _initialDetectedLanguage = _detectLanguage(widget.cellValue); _preserveCompact = _isCompact(widget.cellValue); - _controller.text = _formatInitialValue(widget.cellValue); + _setControllerTextProgrammatically(_formatInitialValue(widget.cellValue)); _validateContent(); } } @@ -187,11 +199,13 @@ class _DataGridValuePanelState extends material.State { try { final parsed = jsonDecode(_controller.text); final pretty = const JsonEncoder.withIndent(' ').convert(parsed); - setState(() => _controller.text = pretty); + setState(() => _setControllerTextProgrammatically(pretty)); + _validateContent(); } catch (_) {} } else if (lang == ValuePanelLanguage.xml) { final pretty = XmlHtmlFormatter.format(_controller.text); - setState(() => _controller.text = pretty); + setState(() => _setControllerTextProgrammatically(pretty)); + _validateContent(); } } @@ -202,16 +216,18 @@ class _DataGridValuePanelState extends material.State { final parsed = jsonDecode(_controller.text); final compact = jsonEncode(parsed); setState(() { - _controller.text = compact; + _setControllerTextProgrammatically(compact); _preserveCompact = true; }); + _validateContent(); } catch (_) {} } else if (lang == ValuePanelLanguage.xml) { final compact = XmlHtmlFormatter.minify(_controller.text); setState(() { - _controller.text = compact; + _setControllerTextProgrammatically(compact); _preserveCompact = true; }); + _validateContent(); } } diff --git a/test/features/workspace/data_grid_value_panel_test.dart b/test/features/workspace/data_grid_value_panel_test.dart index 65c3ecb4..f66621ad 100644 --- a/test/features/workspace/data_grid_value_panel_test.dart +++ b/test/features/workspace/data_grid_value_panel_test.dart @@ -173,5 +173,42 @@ void main() { final button = tester.widget(find.byType(material.ElevatedButton)); expect(button.onPressed, isNull); }); + + testWidgets('didUpdateWidget validates immediately without scheduling redundant debounce', (tester) async { + await tester.pumpWidget( + queryaThemeTestShell( + child: material.Material( + child: DataGridValuePanel( + columnName: 'json_col', + cellValue: '{"valid":true}', + rowIndex: 0, + onClose: () {}, + ), + ), + ), + ); + await tester.pumpAndSettle(); + expect(find.textContaining('Invalid JSON'), findsNothing); + + // Change cellValue to invalid JSON via didUpdateWidget + await tester.pumpWidget( + queryaThemeTestShell( + child: material.Material( + child: DataGridValuePanel( + columnName: 'json_col', + cellValue: '{"invalid":,}', + rowIndex: 1, + onClose: () {}, + ), + ), + ), + ); + // Synchronous validation occurred immediately + await tester.pump(); + expect(find.textContaining('Invalid JSON'), findsOneWidget); + + // Verify no pending timer callback after pump + expect(tester.binding.hasScheduledFrame, isFalse); + }); }); }