selection_group 0.2.4
selection_group: ^0.2.4 copied to clipboard
The focus-aware, plug-and-play selection engine for Flutter TV, Desktop and Mobile.
0.2.4 #
- fix: added automatic scroll-to-view for items in
ListViewor otherScrollables. Focus acquisition into the group (via restoration,autofocus, orcontroller.focus()) now triggersScrollable.ensureVisibleto ensure off-screen items are brought into view. Does not trigger during internal D-pad navigation to maintain smooth scrolling.
0.2.3 #
-
feat(controller): added
focus(T value)toSelectionControllerBase— requests focus on the item with the given value directly, without changing selection state or checking press conditions (contrast withselect(), which applies selection state and respectsapplySelectedStateand press guards). If the item'sFocusNodeis not yet registered (e.g. the widget tree hasn't built yet), the focus request is automatically deferred to the next frame viaWidgetsBinding.addPostFrameCallback— callers no longer need to wrap in apostFrameCallbackmanually. -
feat(controller): added
dispose()toSelectionControllerBase— exposes the underlyingValueNotifier.dispose()through the public interface, allowing external controllers to be properly cleaned up without requiring a cast. Removes all registeredFocusNodelisteners and clears internal maps.Note: in 0.2.2,
dispose()was not part ofSelectionControllerBase. The underlyingValueNotifierdid have it, but callers had to cast toChangeNotifierto reach it. This was an oversight — fixed here. -
fix(group):
SelectionGroupnow performs safe cleanup of external controllers ondispose. When a controller was provided externally (_ownsController = false), the group no longer disposes it (unchanged), but it now nulls out any callbacks that reference the widget's closures (onFocusedItemChanged,moveFocusOnPress,onItemToggled) to prevent memory leaks from stale widget references.
0.2.2 #
-
feat(controller): exposed
SelectionControllerBase.single()andSelectionControllerBase.multi()named factories for external instantiation, allowing controllers to be created and managed outside ofSelectionGroup. -
feat: added
controllerparameter toSelectionGroup.single()andSelectionGroup.multi()— pass an externally created controller to take ownership of its lifecycle. When provided, the group will not dispose the controller automatically. -
feat: added
moveFocusOnPresstoSelectionItem— moves focus in the givenTraversalDirectionwhen an item is pressed. Useful for TV navigation. -
feat: added
applySelectedStateparameter toSelectionGroup.single()— whenfalse, the group manages focus only, without applyingWidgetState.selectedto any item.initialValuestill determines which item receives initial focus. -
perf:
SelectionControllerSinglenow caches the focused value internally, replacing O(N) focus lookups in_moveFocusOnBackPressedwith O(1) map lookups.
0.2.1 #
- feat: added
moveFocusOnBackto bothSelectionGroup.single()andSelectionItem— moves focus in the givenTraversalDirectionwhen the back button is pressed. Useful for TV navigation.
0.2.0 #
-
feat: added
SelectionGroup.multi(): multi-selection support withmaxSelection,MaxSelectionBehavior.block/dequeue,onItemToggled,initialItemToFocus, andonFocusedItemChanged. Still in early testing, expect rough edges. -
feat: added
SelectionGroup.single()— same behavior as the originalSelectionGroup, now as a named constructor. The default constructor is deprecated in favor of this. -
fix:
select()now works correctly on all platforms. The fix introduced in 0.1.2 to make touch selection work correctly had regressed D-pad and keyboard navigation, making the package essentially unusable on TV and desktop. -
fix:
moveFocusOnPressno longer triggers on focus traversal events. Previously, navigating with D-pad or keyboard would incorrectly callfocusInDirection, causing focus to escape the group. Now correctly detects press viaWidgetState.pressedfrom the item's ownstatesController, requiring no manual parameter passing. -
refactor: introduced
SelectionControllerBaseinterface and separate single/multi controller implementations. Controllers are now internal — not part of the public API. -
refactor: controllers are now the single source of truth for
WidgetState.selected. Previously the mixin polled the controller via_handleControllerChangeto update each item's state. Now the controller updates allstatesControllers directly and surgically — only the affected items are updated on eachselect()call. -
refactor:
_registernow receives the item'sWidgetStatesController, allowing the controller to manage focus, selection state, and press detection internally without any logic leaking into the mixin or widget layer. -
refactor:
SelectionMixinis now a pure connector, registers/unregisters the item indidChangeDependenciesanddispose, exposesselect(), and nothing else. All logic lives in the controller. -
refactor: introduced
SelectionMixinandSelectionItemas the new widget layer, replacingSelectionGroupItemMixinandSelectionGroupItem. -
deprecated:
SelectionGroup()default constructor — useSelectionGroup.single(). -
deprecated:
SelectionGroupController— controllers are now internal. -
deprecated:
SelectionGroupItemMixin— useSelectionMixin. -
deprecated:
SelectionGroupItem— useSelectionItem. -
deprecated:
SelectionGroupRadio— useSelectionRadio. -
note: the
moveFocusOnPressbug in the legacy API will not be backported. Migrate toSelectionGroup.single()to get the fix. -
note: the
select()fix introduced in 0.1.2 is broken in the legacy API. Either downgrade to 0.1.1 or migrate toSelectionGroup.single().
0.1.2 #
-
fix:
select()now also requests focus for the selected item. This ensures that on touch platforms, tapping an item moves focus correctly, matching the behavior already present on TV and desktop via directional navigation. -
feat: added
moveFocusOnPressparameter toSelectionGroup. When set to aTraversalDirection, pressing an item moves focus in that direction instead of keeping it on the selected item. Useful for sidebar/content layouts on TV. -
refactor: extracted
isSelected()method intoSelectionGroupController, centralizing the logic that determines whetherWidgetState.selectedshould be applied. The mixin no longer accesses_maintainSelectionOnFocusor_groupHasFocusdirectly. No breaking changes, just organization. Going forward,SelectionGroupControlleris treated now as the kernel of the package: core selection and focus logic lives there, and other layers (mixin, widgets) delegate to it. -
docs: added
ROADMAP.mdto the repository. It documents current architectural limitations, planned breaking changes for the next major version (multi-selection, controller exposure, type refactor), and the long-term vision for the package. Intended for contributors and to set expectations about the direction of the project.
0.1.1 #
-
fix: improved focus behavior in SelectionGroup by wrapping the subtree in a FocusTraversalGroup with WidgetOrderTraversalPolicy. This ensures that internal navigation (like moving between items) follows the widget tree order, preventing the focus from skipping items or accidentally jumping to external headers.
-
feat: added
externalStatestoSelectionGroupItemandSelectionGroupRadio. When provided, the item enters passive display mode — non-interactive, bypasses internalstatesController,focusNode, andFilledButtonentirely, and renders using the given states directly. -
docs: Added a critical warning about SelectionGroupItemMixin typing. Users must specify the type in the mixin signature (e.g., with
SelectionGroupItemMixin<MyWidget, String>) to avoid dynamic type mismatching with the SelectionGroup ancestor.
0.1.0 #
The core has proven to be generic and extensible enough to grow. This release begins the widget layer — and marks the start of real-world stress testing in production.
The ideas behind this package are being proposed to Flutter itself in issue #183904, tagged by the Flutter team as c: new feature, c: proposal, f: focus, framework, and team-framework.
0.1.x will focus on built-in widgets, potential new constructors (multi-selection), and hardening the core against real production usage.
- feat: add
SelectionGroupRadio— a ready-to-use, fully themeable radio button built on top ofSelectionGroupItem. All colors (overlayColor,borderColor,dotColor) are driven byWidgetStateProperty, defaulting to transparent so the component is ready to be styled from outside.
0.0.10 #
- feat: add
enabledparameter toSelectionGroupItem— when false, disables the button and appliesWidgetState.disabledautomatically, allowingdisabledandselectedto coexist independently. - docs: rewrite
maintainSelectionOnFocussection to better explain visual behavior on TV/Desktop. - docs: added a note about explicit typing (e.g.,
SelectionGroup<String>) to ensure correct value comparison and state updates.
0.0.9 #
- docs: add missing CHANGELOG entry for 0.0.8
0.0.8 #
- Add
maintainSelectionOnFocus— keepsWidgetState.selectedvisible while the group has focus - Add
focusInitialItem— requests focus on the initial item on the first frame
0.0.7 #
-
feat: add
SelectionGroupItem— a ready-to-use widget that integrates with SelectionGroup and handles visual states automatically. -
feat:
SelectionGroupItemMixin.selectionValueis now nullable, allowingSelectionGroupItemto be used outside of a group while maintaining focus and states. -
refactor: modularized project structure with a navigation summary using part and part of for better maintainability.
0.0.6 #
- feat: add
selectOnFocusflag — set to false to select only on press (e.g. radio buttons on TV)
0.0.5 #
- feat:
onFocusChangereplaced byonFocusedItemChanged— now returns the focused item value ornullwhen the group loses focus - feat: focused item is automatically marked as selected
- fix: focus listeners are now properly removed on unregister
0.0.4 #
- feat: add
onFocusChangecallback toSelectionGroup - feat: suppress
WidgetState.selectedon all items while group has focus
0.0.3 #
- fix: update LICENSE to MIT
- fix: update tests
0.0.2 #
- fix: update LICENSE
0.0.1 #
- initial release