Context
UI performance review across lib/. Goal: cut unnecessary widget rebuilds and virtualize the heaviest surfaces (result grid, connection tree, code editor). Findings are grouped by severity; each can be tackled incrementally.
Review-only for now — no code changed yet. This issue tracks the implementation.
HIGH
H1. Interface scale rebuilds the whole app on every slider tick
lib/app/app.dart — single ListenableBuilder merges themeController + uiScaleController; setScalePreview() fires notifyListeners() per drag step (lib/core/layout/ui_scale_controller.dart:18-23), rebuilding ShadcnApp → MainScreen → tree → SQL workspaces → editors.
materialThemeFromQuerya() / lightShadcnTheme / darkShadcnTheme / activeTheme reallocate ThemeData (VS Code color merge + applyTokenColorsToEditor) 3x per frame even though only scale changed.
- Fix: local
ValueNotifier<double> for live preview in the preferences dialog; call commitScale() only on onChangeEnd. Split theme-only vs scale-only builders. Memoize ThemeData/QueryaTheme by (preset, overrides, brightness).
H2. Result grid has no virtualization
lib/features/main_screen/results_tab.dart:155-198 — material.Table + IntrinsicColumnWidth + ...rows.map(... SelectableText ...) materializes all cells (cap = 5000 rows). Intrinsic pass is O(rows×cols).
- Fix: virtualized grid (
ListView.builder + fixed/measured-once column widths), Text + "copy cell" instead of SelectableText, pagination. Wrap body in RepaintBoundary.
H3. Connection tree built eagerly, per-row TextPainter
lib/features/connections/connections_panel.dart (SliverToBoxAdapter + Column), connections_panel_pg_tree.dart:957-983 / connections_panel_mysql.dart:447-510 (for loops build all tables/views at once).
connections_panel_pg_tree.dart:40-48 — TextPainter.layout per row per build for overflow detection.
- Fix: flatten visible nodes →
SliverList.builder; replace manual TextPainter with Text(overflow: ellipsis) + Tooltip; add ValueKey on leaf rows.
H4. Syntax highlighting runs synchronously per keystroke
lib/core/editor/querya_highlight_controller.dart — highlighter.highlight(text) runs inside buildTextSpan for buffers < 8KB on every keystroke; no debounce.
lib/core/editor/syntax_highlight_isolate.dart:126-129 — Future.value(syntaxHighlightInIsolate(job)) runs synchronously on the UI thread (no isolate despite the name).
- Fix: debounce (50-150ms) + always
compute/isolate; reuse previous span while pending.
MEDIUM
- M1. Splitter drag
setState rebuilds both panels/editor/grid — main_screen.dart:255-271, workspace_panel.dart:224-231, postgres_sql_workspace.dart:434-441, mysql_sql_workspace.dart. Fix: ValueNotifier<double> + narrow ValueListenableBuilder.
- M2.
AppSettingsRevision.bump() on any setting write (incl. scale commit) reloads all open SQL workspaces — app_settings.dart:222-228, postgres_sql_workspace.dart:92-154. Fix: granular channels / exclude ui_scale.
- M3.
ConnectionsPanel rebuilds on any MainScreenWorkspaceState change though it only needs activeConnection?.id — main_screen.dart:234-251.
- M4. Connection forms
setState(() {}) per keystroke — postgresql_connection_form.dart:62, mysql_connection_form.dart:62, redis_connection_form.dart:53, mongodb_connection_form.dart:93. Fix: ValueNotifier<bool> formValid around buttons only.
- M5. Stats polling rebuilds full page —
redis_view.dart:130-137, postgres_stats_view.dart:124-130, mongo_stats_view.dart:150-167. Fix: compare snapshot, split sections.
- M6. Redis key editor builds all hash/list/set members in
Column inside SingleChildScrollView — redis_key_editor.dart:495-687.
- M7. Mongo documents:
JsonEncoder.withIndent in build when expanded + hover setState + missing keys — mongo_documents_view.dart:220-237,404-481.
- M8. MySQL result row→string conversion is synchronous on UI thread; PG already uses
compute(convertResultRowsToStrings) — mysql_sql_workspace.dart:155-166.
- M9. Theme/highlighter pair recreated on theme change without caching —
syntax_highlight_service.dart, token_colors_highlighter_config.dart, theme_controller.dart:73-79.
LOW
- L1.
RegExp compiled in build — sql_query_history_dialog.dart:72-75, postgres_stats_view.dart:555-557. Hoist to static final.
- L2.
QueryaDropdown.menuChildren rebuilt every build — querya_dropdown.dart:201.
- L3. Hover
setState in list rows (cumulative) — mongo/redis views.
- L4.
TextEditingController not disposed in Redis TTL dialog — redis_key_editor.dart:694-709.
Suggested order
- H1 (scale/theme decoupling) — biggest win, smallest change.
- H4 (highlight debounce + true isolate).
- H2 (result grid virtualization).
- H3 (connection tree virtualization).
- MEDIUM batch (splitter drag, settings revision, form validation).
- LOW cleanups.
Context
UI performance review across
lib/. Goal: cut unnecessary widget rebuilds and virtualize the heaviest surfaces (result grid, connection tree, code editor). Findings are grouped by severity; each can be tackled incrementally.HIGH
H1. Interface scale rebuilds the whole app on every slider tick
lib/app/app.dart— singleListenableBuildermergesthemeController+uiScaleController;setScalePreview()firesnotifyListeners()per drag step (lib/core/layout/ui_scale_controller.dart:18-23), rebuildingShadcnApp→ MainScreen → tree → SQL workspaces → editors.materialThemeFromQuerya()/lightShadcnTheme/darkShadcnTheme/activeThemereallocateThemeData(VS Code color merge +applyTokenColorsToEditor) 3x per frame even though only scale changed.ValueNotifier<double>for live preview in the preferences dialog; callcommitScale()only ononChangeEnd. Split theme-only vs scale-only builders. MemoizeThemeData/QueryaThemeby(preset, overrides, brightness).H2. Result grid has no virtualization
lib/features/main_screen/results_tab.dart:155-198—material.Table+IntrinsicColumnWidth+...rows.map(... SelectableText ...)materializes all cells (cap = 5000 rows). Intrinsic pass is O(rows×cols).ListView.builder+ fixed/measured-once column widths),Text+ "copy cell" instead ofSelectableText, pagination. Wrap body inRepaintBoundary.H3. Connection tree built eagerly, per-row TextPainter
lib/features/connections/connections_panel.dart(SliverToBoxAdapter+Column),connections_panel_pg_tree.dart:957-983/connections_panel_mysql.dart:447-510(forloops build all tables/views at once).connections_panel_pg_tree.dart:40-48—TextPainter.layoutper row per build for overflow detection.SliverList.builder; replace manualTextPainterwithText(overflow: ellipsis)+Tooltip; addValueKeyon leaf rows.H4. Syntax highlighting runs synchronously per keystroke
lib/core/editor/querya_highlight_controller.dart—highlighter.highlight(text)runs insidebuildTextSpanfor buffers < 8KB on every keystroke; no debounce.lib/core/editor/syntax_highlight_isolate.dart:126-129—Future.value(syntaxHighlightInIsolate(job))runs synchronously on the UI thread (no isolate despite the name).compute/isolate; reuse previous span while pending.MEDIUM
setStaterebuilds both panels/editor/grid —main_screen.dart:255-271,workspace_panel.dart:224-231,postgres_sql_workspace.dart:434-441,mysql_sql_workspace.dart. Fix:ValueNotifier<double>+ narrowValueListenableBuilder.AppSettingsRevision.bump()on any setting write (incl. scale commit) reloads all open SQL workspaces —app_settings.dart:222-228,postgres_sql_workspace.dart:92-154. Fix: granular channels / excludeui_scale.ConnectionsPanelrebuilds on anyMainScreenWorkspaceStatechange though it only needsactiveConnection?.id—main_screen.dart:234-251.setState(() {})per keystroke —postgresql_connection_form.dart:62,mysql_connection_form.dart:62,redis_connection_form.dart:53,mongodb_connection_form.dart:93. Fix:ValueNotifier<bool> formValidaround buttons only.redis_view.dart:130-137,postgres_stats_view.dart:124-130,mongo_stats_view.dart:150-167. Fix: compare snapshot, split sections.ColumninsideSingleChildScrollView—redis_key_editor.dart:495-687.JsonEncoder.withIndentin build when expanded + hoversetState+ missing keys —mongo_documents_view.dart:220-237,404-481.compute(convertResultRowsToStrings)—mysql_sql_workspace.dart:155-166.syntax_highlight_service.dart,token_colors_highlighter_config.dart,theme_controller.dart:73-79.LOW
RegExpcompiled in build —sql_query_history_dialog.dart:72-75,postgres_stats_view.dart:555-557. Hoist tostatic final.QueryaDropdown.menuChildrenrebuilt every build —querya_dropdown.dart:201.setStatein list rows (cumulative) — mongo/redis views.TextEditingControllernot disposed in Redis TTL dialog —redis_key_editor.dart:694-709.Suggested order