feat: integrate customizable smooth scroll duration and update contro… - #11
Conversation
…llers across UI components
|
Warning Review limit reached
Next review available in: 21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR wires ChangesSmooth Scroll & Log Summary Feature
Estimated code review effort: 3 (Moderate) | ~30 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces customizable smooth scrolling duration settings and integrates SmoothScrollController across various scrollable views, converting several widgets to stateful to manage controller lifecycles. It also adds a utility to summarize JSON log messages for cleaner list previews. Key feedback highlights that hardcoding maxLines: null in _SelectableEllipsis breaks its truncation behavior, and warns of potential UI jank from synchronously decoding large JSON payloads on the main thread. Additionally, the reviewer recommends removing the duplicated log-summarization helper in console_page.dart in favor of the new shared utility.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| textAlign: textAlign, | ||
| textDirection: textDirection, | ||
| maxLines: maxLines, | ||
| maxLines: null, |
There was a problem hiding this comment.
Hardcoding maxLines: null inside _SelectableEllipsis disables the ellipsis truncation behavior globally and makes the maxLines parameter of this widget dead code. Any caller passing a specific maxLines constraint will have it silently ignored, which can cause text to wrap unexpectedly and break layouts. If the goal was to prevent vertical space reservation for short values, this should be handled conditionally or at the caller level rather than hardcoding null here.
| maxLines: null, | |
| maxLines: maxLines, |
| dynamic parsed; | ||
| try { | ||
| parsed = jsonDecode(trimmed); |
There was a problem hiding this comment.
Synchronously calling jsonDecode on potentially large log messages on the main thread can block the UI and cause noticeable jank, especially when mapping over many events in providers or lists. Consider adding a length threshold check (e.g., skipping parsing if trimmed.length > 5000) to protect against performance degradation with large payloads.
if (trimmed.length > 5000) return message;\n\n dynamic parsed;\n try {\n parsed = jsonDecode(trimmed);| import 'dart:convert'; | ||
|
|
| const SizedBox(height: 6), | ||
| Text( | ||
| entry.message, | ||
| _summarizeLogMessage(entry.message), |
| /// Convert a raw log message into a one-line preview suitable for the | ||
| /// list row's `Text`. Special handling for JSON objects/arrays so the | ||
| /// user sees something like `Object {3 keys: foo, bar, baz}` instead of | ||
| /// just `{}` (the first character of the pretty-printed payload that the | ||
| /// SDK sent over the wire). | ||
| String _summarizeLogMessage(String message) { | ||
| final trimmed = message.trimLeft(); | ||
| if (trimmed.isEmpty) return message; | ||
| if (trimmed[0] != '{' && trimmed[0] != '[') return message; | ||
|
|
||
| // Try to parse as JSON — RN's `toStr` (and Flutter's `jsonEncode`) ship | ||
| // pretty-printed payloads, so we can't rely on a single line. | ||
| dynamic parsed; | ||
| try { | ||
| parsed = jsonDecode(trimmed); | ||
| } catch (_) { | ||
| return message; // not valid JSON — show the original text | ||
| } | ||
|
|
||
| if (parsed is Map) { | ||
| final keys = parsed.keys.cast<String>().toList(); | ||
| if (keys.isEmpty) return 'Object {}'; | ||
| final preview = keys.take(3).join(', '); | ||
| final more = keys.length > 3 ? ', …' : ''; | ||
| return 'Object {${keys.length} key${keys.length == 1 ? '' : 's'}: $preview$more}'; | ||
| } | ||
| if (parsed is List) { | ||
| if (parsed.isEmpty) return 'Array []'; | ||
| return 'Array [${parsed.length}]'; | ||
| } | ||
| return message; | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
lib/core/utils/log_message_summary.dart (1)
12-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLogic looks solid; add unit tests for this new utility.
Edge cases (empty map/list, singular vs plural "key(s)", >3 keys truncation, invalid JSON) are handled correctly. Since this is a pure, easily-testable function feeding directly into list-row titles across platforms, it's worth locking down behavior with unit tests before other call sites build on it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/core/utils/log_message_summary.dart` around lines 12 - 38, Add unit tests for summarizeLogMessage in log_message_summary.dart to lock down the new behavior. Cover empty/whitespace-only input, non-JSON text, valid JSON object and array summaries, empty map/list, singular versus plural key wording, and truncation when there are more than three object keys. Use summarizeLogMessage as the target symbol and verify the returned strings exactly.lib/features/console/presentation/pages/console_page.dart (3)
1217-1250: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winJSON is re-decoded from scratch on every tab switch.
jsonDecode(widget.message)runs unconditionally inbuild(), so tapping Tree→JSON→Code triggerssetState→ full rebuild → full re-parse of the same payload each time. This is exactly the large-JSON-payload scenario this feature targets, so it's worth parsing once and caching.⚡ Cache the parsed payload
class _LogMessageBlockState extends State<_LogMessageBlock> { /// 0 = Tree, 1 = JSON, 2 = Code. int _mode = 0; + dynamic _parsed; + bool _isJson = false; + + `@override` + void initState() { + super.initState(); + _parse(); + } + + `@override` + void didUpdateWidget(covariant _LogMessageBlock oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.message != widget.message) _parse(); + } + + void _parse() { + try { + _parsed = jsonDecode(widget.message); + } catch (_) { + _parsed = null; + } + _isJson = _parsed is Map || _parsed is List; + } `@override` Widget build(BuildContext context) { final isDark = widget.isDark; - dynamic parsed; - try { - parsed = jsonDecode(widget.message); - } catch (_) { - parsed = null; - } - final isJson = parsed is Map || parsed is List; + final parsed = _parsed; + final isJson = _isJson;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/console/presentation/pages/console_page.dart` around lines 1217 - 1250, The parsing logic in ConsolePage’s build method re-runs jsonDecode(widget.message) on every tab switch, which causes repeated work for the same payload. Move the JSON parsing out of build and cache the result in ConsolePage or its state (for example by storing the parsed value keyed to widget.message), then have the Tree/JSON branches reuse that cached parsed payload instead of decoding again.
919-946: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
_LogDetailPanel/_LogMessageBlockuse plainState/StatefulWidget, notConsumerStatefulWidget.Both new stateful classes in this presentation-layer file skip the Riverpod-flavored base class used elsewhere in this stack (e.g.
DeviceBottomBar/Sidebarwere converted toConsumerStatefulWidget). Even if neither currently reads a provider, keeping the pattern consistent avoids another StatefulWidget→ConsumerStatefulWidget rewrite the next time this panel needsref.As per path instructions,
lib/features/**/presentation/**: "Follow ConsumerStatefulWidget + Riverpod patterns".Also applies to: 1202-1214
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/console/presentation/pages/console_page.dart` around lines 919 - 946, Convert the new presentation widgets `_LogDetailPanel` and `_LogMessageBlock` from plain `StatefulWidget`/`State` to the Riverpod-style `ConsumerStatefulWidget`/`ConsumerState` pattern used elsewhere in this stack. Update the widget and state class declarations together so they follow the same convention as `DeviceBottomBar` and `Sidebar`, keeping `ref` available for future provider reads and avoiding another refactor later.Source: Path instructions
1200-1391: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift
_LogMessageBlock/_DetailTabBarduplicate the All Events detail panel's Tree/JSON/Code toggle.The comments explicitly note this mirrors the pattern in
all_events_page.dart. Consider extracting a shared widget (e.g.MessageModeBlock+DetailTabBar) into a common components location so future tweaks (colors, tab labels, mode logic) land once instead of twice.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/console/presentation/pages/console_page.dart` around lines 1200 - 1391, The _LogMessageBlock and _DetailTabBar implementations duplicate the Tree/JSON/Code toggle already used by the All Events detail panel, so extract the shared mode-switching UI and tab bar into a common reusable widget/component. Move the mode state/rendering logic from _LogMessageBlock and the pill tab rendering from _DetailTabBar into a shared implementation with a single API so both console_page.dart and the All Events panel use the same source of truth for labels, colors, and mode behavior.lib/features/settings/presentation/pages/settings_page.dart (1)
937-948: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winPersist duration on drag end, not every tick.
onChangedfires continuously while dragging and callssmoothScrollDurationProvider.notifier.set(...)on each tick, which triggers anAppPreferences().set()file write per event. Consider updating local/ephemeral UI state inonChangedand only persisting viaonChangeEnd.♻️ Proposed fix
Slider( value: ref.watch(smoothScrollDurationProvider).toDouble(), min: 100, max: 1000, divisions: 18, label: '${ref.watch(smoothScrollDurationProvider)}ms', activeColor: ColorTokens.primary, inactiveColor: isDark ? Colors.white12 : Colors.black12, - onChanged: (v) => ref - .read(smoothScrollDurationProvider.notifier) - .set(v.round()), + onChanged: (v) => ref + .read(smoothScrollDurationProvider.notifier) + .updateEphemeral(v.round()), + onChangeEnd: (v) => ref + .read(smoothScrollDurationProvider.notifier) + .set(v.round()), ),Requires adding an
updateEphemeral(state-only, no persist) method toSmoothScrollDurationNotifier.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/settings/presentation/pages/settings_page.dart` around lines 937 - 948, The Slider in SettingsPage is persisting smooth scroll duration on every drag tick through smoothScrollDurationProvider.notifier.set(...), causing repeated AppPreferences writes. Update the SettingsPage Slider to use ephemeral/local state during onChanged and only commit the value on onChangeEnd, and add an updateEphemeral method to SmoothScrollDurationNotifier so the UI can update without persisting until drag end.lib/components/layout/device_bottom_bar.dart (1)
18-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRepeated scroll-controller boilerplate across ~12 widgets.
The
final _scrollController = SmoothScrollController(); … dispose() { _scrollController.dispose(); super.dispose(); }pattern is duplicated near-identically in this file,sidebar.dart,json_viewer.dart(x2),all_events_page.dart(x7),error_inspector_page.dart(x3),network_inspector_page.dart(x3), andstate_inspector_page.dart(x2). A small mixin (e.g.SmoothScrollControllerMixin) exposing a ready-to-use, auto-disposed controller would cut this duplication significantly across the whole PR.♻️ Sketch of a reusable mixin
mixin SmoothScrollControllerMixin<T extends StatefulWidget> on State<T> { final scrollController = SmoothScrollController(); `@override` void dispose() { scrollController.dispose(); super.dispose(); } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/components/layout/device_bottom_bar.dart` around lines 18 - 25, The scroll controller setup is duplicated across multiple widgets, including _DeviceBottomBarState and the other pages/components noted in the review. Extract this repeated SmoothScrollController creation and disposal pattern into a reusable mixin such as SmoothScrollControllerMixin that owns and disposes the controller automatically, then update each affected State class to use the shared mixin instead of defining its own _scrollController and dispose logic.lib/features/state_inspector/presentation/pages/state_inspector_page.dart (1)
792-812: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSame
StableListViewgap as the diff list inall_events_page.dart.
_DiffView'sListView.builderis being modified here (new_scrollController), but per path instructions this list view should useStableListViewinstead. Flagging alongside the equivalent case inlib/features/all_events/presentation/pages/all_events_page.dartsince it's the same underlying pattern.As per path instructions, "Use StableListView for list views" for
lib/features/**/presentation/**.Also applies to: 821-821
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/features/state_inspector/presentation/pages/state_inspector_page.dart` around lines 792 - 812, _ DiffView in state_inspector_page.dart is introducing a scroll-controlled ListView where the presentation layer requires StableListView for list views. Update the diff list inside _DiffViewState to use StableListView instead of the current ListView.builder pattern, and keep the scroll controller handling aligned with that widget’s expected usage. Apply the same StableListView pattern used in the equivalent all_events_page.dart diff list so the two implementations stay consistent.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/components/layout/device_bottom_bar.dart`:
- Around line 18-25: The horizontal `ListView` in `DeviceBottomBar` still needs
an explicit desktop scroll behavior override because `SmoothScrollController`
does not change the default Shift+wheel handling. Update the `build` path for
`DeviceBottomBar`/`_DeviceBottomBarState` to wrap the horizontal list with a
`ScrollConfiguration` or custom `ScrollBehavior` so mouse-wheel input is handled
as intended on desktop, while keeping the existing `_scrollController` and
disposal logic intact.
In `@lib/features/console/presentation/pages/console_page.dart`:
- Line 854: The console page’s private _summarizeLogMessage helper duplicates
the shared summarizeLogMessage utility, so replace the local copy with calls to
the shared function from lib/core/utils/log_message_summary.dart. Update the
console list and all-events list paths in console_page.dart to reuse that
utility directly, and remove the duplicated doc comments/implementation so the
JSON-preview behavior stays consistent in one place.
In
`@lib/features/network_inspector/presentation/pages/network_inspector_page.dart`:
- Line 2299: The header-key column width is inconsistent between the live
Headers tab and the screenshot renderer. Update `_screenshotHeaderRow`, used by
`_buildFullScreenshotWidget` and `_buildTabScreenshotWidget`, to use the same
key-column width as the live view (170) instead of the hardcoded 180 so
request/response header alignment matches in screenshots.
---
Nitpick comments:
In `@lib/components/layout/device_bottom_bar.dart`:
- Around line 18-25: The scroll controller setup is duplicated across multiple
widgets, including _DeviceBottomBarState and the other pages/components noted in
the review. Extract this repeated SmoothScrollController creation and disposal
pattern into a reusable mixin such as SmoothScrollControllerMixin that owns and
disposes the controller automatically, then update each affected State class to
use the shared mixin instead of defining its own _scrollController and dispose
logic.
In `@lib/core/utils/log_message_summary.dart`:
- Around line 12-38: Add unit tests for summarizeLogMessage in
log_message_summary.dart to lock down the new behavior. Cover
empty/whitespace-only input, non-JSON text, valid JSON object and array
summaries, empty map/list, singular versus plural key wording, and truncation
when there are more than three object keys. Use summarizeLogMessage as the
target symbol and verify the returned strings exactly.
In `@lib/features/console/presentation/pages/console_page.dart`:
- Around line 1217-1250: The parsing logic in ConsolePage’s build method re-runs
jsonDecode(widget.message) on every tab switch, which causes repeated work for
the same payload. Move the JSON parsing out of build and cache the result in
ConsolePage or its state (for example by storing the parsed value keyed to
widget.message), then have the Tree/JSON branches reuse that cached parsed
payload instead of decoding again.
- Around line 919-946: Convert the new presentation widgets `_LogDetailPanel`
and `_LogMessageBlock` from plain `StatefulWidget`/`State` to the Riverpod-style
`ConsumerStatefulWidget`/`ConsumerState` pattern used elsewhere in this stack.
Update the widget and state class declarations together so they follow the same
convention as `DeviceBottomBar` and `Sidebar`, keeping `ref` available for
future provider reads and avoiding another refactor later.
- Around line 1200-1391: The _LogMessageBlock and _DetailTabBar implementations
duplicate the Tree/JSON/Code toggle already used by the All Events detail panel,
so extract the shared mode-switching UI and tab bar into a common reusable
widget/component. Move the mode state/rendering logic from _LogMessageBlock and
the pill tab rendering from _DetailTabBar into a shared implementation with a
single API so both console_page.dart and the All Events panel use the same
source of truth for labels, colors, and mode behavior.
In `@lib/features/settings/presentation/pages/settings_page.dart`:
- Around line 937-948: The Slider in SettingsPage is persisting smooth scroll
duration on every drag tick through
smoothScrollDurationProvider.notifier.set(...), causing repeated AppPreferences
writes. Update the SettingsPage Slider to use ephemeral/local state during
onChanged and only commit the value on onChangeEnd, and add an updateEphemeral
method to SmoothScrollDurationNotifier so the UI can update without persisting
until drag end.
In `@lib/features/state_inspector/presentation/pages/state_inspector_page.dart`:
- Around line 792-812: _ DiffView in state_inspector_page.dart is introducing a
scroll-controlled ListView where the presentation layer requires StableListView
for list views. Update the diff list inside _DiffViewState to use StableListView
instead of the current ListView.builder pattern, and keep the scroll controller
handling aligned with that widget’s expected usage. Apply the same
StableListView pattern used in the equivalent all_events_page.dart diff list so
the two implementations stay consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 3ba153a3-848b-44c9-8c40-ecec0dc1ded2
📒 Files selected for processing (28)
lib/components/layout/device_bottom_bar.dartlib/components/layout/sidebar.dartlib/components/text/text_component.dartlib/components/viewers/json_viewer.dartlib/core/theme/theme_provider.dartlib/core/utils/log_message_summary.dartlib/core/utils/smooth_scroll_controller.dartlib/features/all_events/presentation/pages/all_events_page.dartlib/features/all_events/provider/all_events_provider.dartlib/features/console/presentation/pages/console_page.dartlib/features/error_inspector/presentation/pages/error_inspector_page.dartlib/features/network_inspector/presentation/pages/network_inspector_page.dartlib/features/settings/presentation/pages/settings_page.dartlib/features/state_inspector/presentation/pages/state_inspector_page.dartlib/features/storage_viewer/presentation/pages/storage_viewer_page.dartlib/l10n/app_en.arblib/l10n/app_fr.arblib/l10n/app_ja.arblib/l10n/app_localizations.dartlib/l10n/app_localizations_en.dartlib/l10n/app_localizations_fr.dartlib/l10n/app_localizations_ja.dartlib/l10n/app_localizations_vi.dartlib/l10n/app_localizations_zh.dartlib/l10n/app_vi.arblib/l10n/app_zh.arblib/l10n/app_zh_CN.arblib/l10n/app_zh_TW.arb
| class _DeviceBottomBarState extends ConsumerState<DeviceBottomBar> { | ||
| final _scrollController = SmoothScrollController(); | ||
|
|
||
| @override | ||
| void dispose() { | ||
| _scrollController.dispose(); | ||
| super.dispose(); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does Flutter's default ScrollBehavior route unmodified vertical mouse wheel scrollDelta to horizontal-only ListViews, or does it require Shift by default?
💡 Result:
Yes, Flutter's default ScrollBehavior requires the Shift key to be held down when using a vertical mouse wheel to scroll a horizontal ListView [1][2]. This behavior is governed by the pointerAxisModifiers property in the ScrollBehavior class, which defaults to including LogicalKeyboardKey.shiftLeft and LogicalKeyboardKey.shiftRight [1][3]. When these keys are pressed in combination with a mouse scroll event, Flutter flips the scroll axes, allowing vertical wheel input to move a horizontal ScrollView [1][3]. This is considered the standard desktop convention for horizontal scrolling [2]. If you wish to modify this behavior, you can override the ScrollBehavior in your app (e.g., via ScrollConfiguration or within your MaterialApp/CupertinoApp) to customize the pointerAxisModifiers set [1][4][5]. However, simply clearing the set does not automatically cause a horizontal list to scroll with a vertical wheel; it effectively disables the axis-flipping mechanism [4][6]. Top results: [1], [2], [5]
Citations:
- 1: https://api.flutter.dev/flutter/widgets/ScrollBehavior/pointerAxisModifiers.html
- 2: Scroll views with horzontal scroll axis should scroll by default without pressing
shiftwhen on desktop flutter/flutter#83065 - 3: https://main-api.flutter.dev/flutter/widgets/ScrollBehavior/pointerAxisModifiers.html
- 4: CustomScrollView with scrollDirection horizontal cannot be scrolled with mouse wheel. flutter/flutter#162781
- 5: https://api.flutter.dev/flutter/widgets/ScrollBehavior-class.html
- 6: [Desktop][Web] Option to scroll horizontal scroll views on vertical scroll event (if not nested in vertical scroll view) flutter/flutter#185146
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the target file and nearby scroll-related code.
git ls-files 'lib/components/layout/device_bottom_bar.dart' 'lib/**' | sed -n '1,120p'
echo
echo "== device_bottom_bar.dart =="
wc -l lib/components/layout/device_bottom_bar.dart
cat -n lib/components/layout/device_bottom_bar.dart | sed -n '1,220p'
echo
echo "== Search for SmoothScrollController usage =="
rg -n "SmoothScrollController|pointerScroll|ScrollBehavior|ScrollConfiguration|ListView\.separated|Axis\.horizontal" lib -SRepository: ridelinktechs/devconnect-manage-kit
Length of output: 17929
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# If the smooth-scrolling controller comes from a package or local helper, inspect its API shape.
rg -n "class SmoothScrollController|extends ScrollController|pointerScroll" . -SRepository: ridelinktechs/devconnect-manage-kit
Length of output: 450
🏁 Script executed:
#!/bin/bash
set -euo pipefail
wc -l lib/core/utils/smooth_scroll_controller.dart
cat -n lib/core/utils/smooth_scroll_controller.dart | sed -n '1,220p'Repository: ridelinktechs/devconnect-manage-kit
Length of output: 4391
Horizontal wheel scrolling still needs a behavior override lib/components/layout/device_bottom_bar.dart:80-82 — Flutter keeps horizontal ListView wheel scrolling on Shift+wheel by default, so SmoothScrollController won’t change plain mouse-wheel input here. Add a ScrollBehavior/ScrollConfiguration if desktop wheel scrolling is expected.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/components/layout/device_bottom_bar.dart` around lines 18 - 25, The
horizontal `ListView` in `DeviceBottomBar` still needs an explicit desktop
scroll behavior override because `SmoothScrollController` does not change the
default Shift+wheel handling. Update the `build` path for
`DeviceBottomBar`/`_DeviceBottomBarState` to wrap the horizontal list with a
`ScrollConfiguration` or custom `ScrollBehavior` so mouse-wheel input is handled
as intended on desktop, while keeping the existing `_scrollController` and
disposal logic intact.
| children: [ | ||
| SizedBox( | ||
| width: 180, | ||
| width: 170, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Header-key column width now mismatches the screenshot renderer.
This narrows the key column to 170 for the live Headers tab, but _screenshotHeaderRow (used by _buildFullScreenshotWidget/_buildTabScreenshotWidget) still hardcodes width: 180 for the same header data. Screenshots will now render request/response headers with a visibly different key-column alignment than what the user sees on screen.
🖼️ Suggested alignment fix
Widget _screenshotHeaderRow(String key, String value, bool isDark) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
- width: 180,
+ width: 170,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@lib/features/network_inspector/presentation/pages/network_inspector_page.dart`
at line 2299, The header-key column width is inconsistent between the live
Headers tab and the screenshot renderer. Update `_screenshotHeaderRow`, used by
`_buildFullScreenshotWidget` and `_buildTabScreenshotWidget`, to use the same
key-column width as the live view (170) instead of the hardcoded 180 so
request/response header alignment matches in screenshots.
…event UI jank, while optimizing SelectableText height rendering
✨ Smooth Scrolling & Full Coverage
What
Adds a smooth scrolling feature for mouse wheel events across the entire application, along with configurable scroll animation duration.
Changes
Core
SmoothScrollController — Custom ScrollController that intercepts pointerScroll events and replaces the default instant-jump behavior with an animateTo() call using Curves.easeOutQuart, making every scroll feel fluid and natural. Reads both the enabled flag and duration directly from AppPreferences on each event so settings apply instantly without restart.
Settings
Toggle — On/Off switch under Appearance settings (default: off). Note warns users to disable if they experience performance issues.
Duration Slider — Appears when smooth scroll is enabled. Allows adjusting animation duration from 100ms to 1000ms (default: 250ms, steps of ~50ms).
Persistence — Both settings are saved via AppPreferences and backed by dedicated Riverpod providers: smoothScrollEnabledProvider and smoothScrollDurationProvider.
Full Scroll Coverage
Applied SmoothScrollController to every scrollable widget in the app:
Main event list views: All Events, Console, Network, State, Storage, Errors
Detail panels: Log detail, Network (Headers/Body/Timing tabs), State (Diff/Before/After tabs), Storage value panel, Error (Message/Stack Trace/Details tabs), Fallback event
Settings page, Database Viewer (vertical + horizontal), Performance profiler list, Memory Leaks (list + detail), Benchmark (list + detail)
Last Connected session: history list + all 5 event detail tabs
Shared viewer components: JsonViewer (tree), JsonPrettyViewer (code/raw)
Layout components: Sidebar navigation, Device connection bottom bar (horizontal)
UI Fix
Request Headers spacing — Fixed _SelectableEllipsis in TextComponent incorrectly reserving vertical space for non-overflowing text by setting maxLines: null for short values. Rows now collapse to their actual content height.
Localization
All 7 supported languages updated (EN, VI, JA, FR, ZH, ZH-CN, ZH-TW) for: smoothScrolling, smoothScrollingDesc, smoothScrollingDuration, smoothScrollingDurationDesc.
Developer HTTP terminology (Headers, Request, Response, Timing, etc.) kept as hardcoded English in both Network Inspector and All Events pages for consistency with standard tooling conventions.
Testing
flutter test passes — all tests green
Summary by CodeRabbit
New Features
Bug Fixes
Documentation