When DpadFocusable widgets with a region: are used inside a lazy ListView or GridView, off-screen items get recycled by Flutter and their elements are deactivated then disposed. At that point _unregisterFromRegionManager() (called from dispose()) fails silently because the element is already detached from the tree — DpadNavigator.regionManagerOf(context) can't resolve and the existing catch (_) swallows the error. The focus node is never removed from the region registry.
These ghost nodes accumulate as the user scrolls. When directional navigation later iterates candidates and calls .rect on them, this throws:
- Use
DpadFocusable(region: 'content', ...) inside a GridView with enough items to scroll (e.g. 50+ items)
- Scroll down past the first screen of items so Flutter recycles early widgets
- Navigate with D-pad — the exception above is thrown
What Worked For Us
Moving _unregisterFromRegionManager() from dispose() to an overridden deactivate() method. deactivate() is called before the element is removed from the widget tree, so context is still valid and the lookup succeeds.
// lib/src/focus/dpad_focusable.dart
@override
void deactivate() {
_unregisterFromRegionManager();
super.deactivate();
}
@override
void dispose() {
// unregister is now handled in deactivate() above
_focusNode.removeListener(_onFocusChange);
// ... rest unchanged
This completely eliminates the ghost nodes. No other changes were needed — region_navigation.dart does not need any guards.
Environment:
- dpad: 2.0.2
- Flutter 3.x, Android TV (API 36 emulator)
- Large grid of media items (~500 items)
Thank you for reading this through, please let me know if this is really a problem or am I mistaken? if this is really a problem, would it be okay to craete the PR or are you going to fix this with next version release?
When
DpadFocusablewidgets with aregion:are used inside a lazyListVieworGridView, off-screen items get recycled by Flutter and their elements are deactivated then disposed. At that point_unregisterFromRegionManager()(called fromdispose()) fails silently because the element is already detached from the tree —DpadNavigator.regionManagerOf(context)can't resolve and the existingcatch (_)swallows the error. The focus node is never removed from the region registry.These ghost nodes accumulate as the user scrolls. When directional navigation later iterates candidates and calls
.recton them, this throws:DpadFocusable(region: 'content', ...)inside a GridView with enough items to scroll (e.g. 50+ items)What Worked For Us
Moving
_unregisterFromRegionManager()fromdispose()to an overriddendeactivate()method.deactivate()is called before the element is removed from the widget tree, socontextis still valid and the lookup succeeds.This completely eliminates the ghost nodes. No other changes were needed —
region_navigation.dartdoes not need any guards.Environment:
Thank you for reading this through, please let me know if this is really a problem or am I mistaken? if this is really a problem, would it be okay to craete the PR or are you going to fix this with next version release?