dpad 2.0.0
dpad: ^2.0.0 copied to clipboard
Flutter TV D-pad navigation system - as simple as native Android development
đş Dpad
Flutter TV Navigation System
[Dpad Logo]
⨠Features #
- đŻ Simple Setup: Just 3 steps to get started
- đ¨ Customizable Effects: Built-in focus effects + custom builders
- đş Platform Support: Android TV, Fire TV, Apple TV, and more
- ⥠Performance: Optimized for smooth navigation
- đ§ Programmatic Control: Full API for programmatic navigation
- đŽ Game Controller Support: Works with standard controllers
- đ Sequential Navigation: Previous/Next support for media and lists
đ Quick Start #
1. Add Dependency #
dependencies:
dpad: any
2. Wrap Your App #
import 'package:dpad/dpad.dart';
void main() {
runApp(
DpadNavigator(
enabled: true,
child: MaterialApp(
home: MyApp(),
),
),
);
}
3. Make Widgets Focusable #
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
DpadFocusable(
autofocus: true,
onFocus: () => print('Focused'),
onSelect: () => print('Selected'),
builder: (context, isFocused, child) {
return AnimatedContainer(
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
border: Border.all(
color: isFocused ? Colors.blue : Colors.transparent,
width: 3,
),
borderRadius: BorderRadius.circular(8),
),
child: child,
);
},
child: ElevatedButton(
onPressed: () => print('Pressed'),
child: Text('Button 1'),
),
),
DpadFocusable(
onSelect: () => print('Button 2 selected'),
child: ElevatedButton(
onPressed: () => print('Pressed'),
child: Text('Button 2'),
),
),
],
);
}
}
đ¨ Focus Effects #
Built-in Effects #
// Border highlight
DpadFocusable(
builder: FocusEffects.border(color: Colors.blue),
child: MyWidget(),
)
// Glow effect
DpadFocusable(
builder: FocusEffects.glow(glowColor: Colors.blue),
child: MyWidget(),
)
// Scale effect
DpadFocusable(
builder: FocusEffects.scale(scale: 1.1),
child: MyWidget(),
)
// Gradient background
DpadFocusable(
builder: FocusEffects.gradient(
focusedColors: [Colors.blue, Colors.purple],
),
child: MyWidget(),
)
// Combine multiple effects
DpadFocusable(
builder: FocusEffects.combine([
FocusEffects.scale(scale: 1.05),
FocusEffects.border(color: Colors.blue),
]),
child: MyWidget(),
)
Custom Effects #
DpadFocusable(
builder: (context, isFocused, child) {
return Transform.scale(
scale: isFocused ? 1.1 : 1.0,
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
decoration: BoxDecoration(
boxShadow: isFocused ? [
BoxShadow(
color: Colors.blue.withValues(alpha: 0.6), // ignore: deprecated_member_use
blurRadius: 20,
spreadRadius: 2,
),
] : null,
),
child: child,
),
);
},
child: Container(
child: Text('Custom Effect'),
),
)
đ§ Advanced Usage #
đ Auto-Scroll (New in v1.2.2) #
DpadFocusable now automatically scrolls to ensure the focused widget is fully visible, including focus effects like glow and borders.
DpadFocusable(
autoScroll: true, // Enable auto-scroll (default: true)
scrollPadding: 24.0, // Extra padding for focus effects (default: 24.0)
builder: FocusEffects.glow(glowColor: Colors.blue),
child: MyWidget(),
)
// Disable auto-scroll for specific widgets
DpadFocusable(
autoScroll: false,
child: MyWidget(),
)
// Programmatic scroll control
Dpad.scrollToFocus(
focusNode,
padding: 32.0,
duration: Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
);
đ§ Focus Memory (Updated in v1.2.2) #
The focus memory system intelligently remembers user's focus positions and restores them when navigating back, providing a more natural TV navigation experience.
Quick Setup #
DpadNavigator(
focusMemory: FocusMemoryOptions(
enabled: true,
maxHistory: 20,
),
onNavigateBack: (context, previousEntry, history) {
if (previousEntry != null) {
previousEntry.focusNode.requestFocus();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: MyApp(),
)
Region Identification #
// Tab bar
DpadFocusable(
region: 'tabs',
child: TabButton(),
)
// Filter area
DpadFocusable(
region: 'filters',
child: FilterOption(),
)
// Content cards
DpadFocusable(
region: 'cards',
child: ContentCard(),
)
Use Cases #
- Tab Navigation: Tab A â Browse â Tab B â Back â Tab B (restores previous tab)
- Filter Navigation: Filter A â Browse â Filter A â Back â Filter A (restores previous filter)
- Cross-Route Navigation: Maintains separate focus history per route
đŻ Region-based Navigation (New in v2.0.0) #
Region-based navigation solves the common TV UX problem where Flutter's default geometric navigation doesn't match user expectations.
The Problem #
With default Flutter navigation:
- Tab â Content: might focus any card based on distance
- Content â Tab: might jump to unexpected tab
- Sidebar â Grid: focus could land anywhere
The Solution #
DpadNavigator(
regionNavigation: RegionNavigationOptions(
enabled: true,
rules: [
// Tab â Content: always focus first card
RegionNavigationRule(
fromRegion: 'tabs',
toRegion: 'content',
direction: TraversalDirection.down,
strategy: RegionNavigationStrategy.fixedEntry,
bidirectional: true,
reverseStrategy: RegionNavigationStrategy.memory,
),
// Sidebar â Grid: always focus first card
RegionNavigationRule(
fromRegion: 'sidebar',
toRegion: 'grid',
direction: TraversalDirection.right,
strategy: RegionNavigationStrategy.fixedEntry,
),
],
),
child: MyApp(),
)
Mark Entry Points #
// First card in content area - entry point
DpadFocusable(
region: 'content',
isEntryPoint: true,
child: ContentCard(),
)
// Other cards in the same region
DpadFocusable(
region: 'content',
child: ContentCard(),
)
Navigation Strategies #
| Strategy | Behavior |
|---|---|
geometric |
Flutter's default distance-based navigation |
fixedEntry |
Always focus the widget marked as isEntryPoint |
memory |
Restore last focused widget, fallback to entry point |
custom |
Use custom resolver function |
Custom Shortcuts #
DpadNavigator(
customShortcuts: {
LogicalKeyboardKey.keyG: () => _showGridView(),
LogicalKeyboardKey.keyL: () => _showListView(),
LogicalKeyboardKey.keyR: () => _refreshData(),
LogicalKeyboardKey.keyS: () => _showSearch(),
},
onMenuPressed: () => _showMenu(),
onBackPressed: () => _handleBack(),
child: MyApp(),
)
Default Keyboard Shortcuts (v1.1.0+):
- Arrow Keys: Directional navigation (up, down, left, right)
- Tab/Shift+Tab: Sequential navigation (next/previous)
- Media Track Next/Previous: Media control navigation
- Channel Up/Down: TV remote sequential navigation
- Enter/Select/Space: Trigger selection action
- Escape/Back: Navigate back
- ContextMenu: Show menu
Programmatic Navigation #
// Navigate in directions
Dpad.navigateUp(context);
Dpad.navigateDown(context);
Dpad.navigateLeft(context);
Dpad.navigateRight(context);
// Sequential navigation (new in v1.1.0)
Dpad.navigateNext(context); // Tab / Media Track Next
Dpad.navigatePrevious(context); // Shift+Tab / Media Track Previous
// Focus management
final currentFocus = Dpad.currentFocus;
Dpad.requestFocus(myFocusNode);
Dpad.clearFocus();
Platform-Specific Handling #
DpadNavigator(
onMenuPressed: () {
// Handle menu button on TV remotes
_showMenu();
},
onBackPressed: () {
// Handle back button
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
},
child: MyApp(),
)
đą Platform Support #
- Android TV: Full native D-pad support
- Amazon Fire TV: Compatible with Fire TV remotes
- Apple TV: Works with Siri Remote (Flutter web)
- Game Controllers: Standard controller navigation
- Generic TV Platforms: Any D-pad compatible input
đĄ Best Practices #
- Always set
autofocus: trueon one widget per screen for initial focus - Test with real D-pad hardware, not just keyboard arrows
- Consider focus order - arrange widgets logically for navigation
- Provide clear visual feedback - use prominent focus indicators
- Handle edge cases - what happens when navigation fails?
đď¸ Architecture #
The system consists of three main components:
- DpadNavigator: Root widget that captures D-pad events
- DpadFocusable: Wrapper that makes widgets focusable
- Dpad: Utility class for programmatic control
All components work together seamlessly with Flutter's focus system.
đ Migration #
Coming from other TV navigation libraries?
- â No complex configuration needed
- â Works with standard Flutter widgets
- â No custom FocusNode management required
- â Built-in support for all TV platforms
- â Extensive customization options
đ Example #
Check out the example app for a complete implementation showing:
- Grid navigation
- List navigation
- Custom focus effects
- Programmatic navigation
- Platform-specific handling
đ¤ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
đ License #
This project is licensed under the MIT License - see the LICENSE file for details.