keystroke_listener

A comprehensive Flutter package for capturing and handling keystroke events with support for basic and advanced keyboard shortcuts. This package provides a KeystrokeListener widget that wraps your Flutter widgets with keyboard event detection, making it easy to implement keyboard-driven interactions.

Demo

Demo

Features

  • 🎹 Comprehensive Intent System: 18+ built-in Intents for common keyboard actions
  • ⌨️ Keyboard Shortcut Handling: Support for standard shortcuts (Ctrl/Cmd+S, Ctrl/Cmd+C, etc.)
  • 🎯 FocusNode Management: Automatic focus handling with custom FocusNode support
  • 🐛 Visual Debug Mode: Optional visual feedback for debugging keyboard events
  • 🌐 Web Compatible: Special handling for Flutter Web focus requirements
  • Accessible: Built-in accessibility support through Flutter's FocusableActionDetector
  • 📱 Cross-Platform: Works on Android, iOS, Web, Windows, macOS, and Linux

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  keystroke_listener: 
    path: ../my_extensions/keystroke_listener

Then run:

flutter pub get

Quick Start

import 'package:flutter/material.dart';
import 'package:keystroke_listener/keystroke_listener.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: KeystrokeListener(
          onKeyEvent: (event) {
            print('Key pressed: ${event.logicalKey.keyLabel}');
          },
          child: const Center(
            child: Text('Press any key!'),
          ),
        ),
      ),
    );
  }
}

Available Intents

Basic Navigation Intents

  • NavigateUpIntent: Arrow up key
  • NavigateDownIntent: Arrow down key
  • NavigateLeftIntent: Arrow left key
  • NavigateRightIntent: Arrow right key

System Intents

  • EscapeIntent: Escape key
  • SubmitIntent: Enter/Return key
  • DeleteIntent: Backspace key
  • SpaceIntent: Space key
  • TabIntent: Tab key
  • ReverseTabIntent: Shift + Tab

Edit Intents (Ctrl/Cmd + key)

  • SaveIntent: Ctrl/Cmd + S
  • UndoIntent: Ctrl/Cmd + Z
  • RedoIntent: Ctrl/Cmd + Y
  • SelectAllIntent: Ctrl/Cmd + A
  • CopyIntent: Ctrl/Cmd + C
  • PasteIntent: Ctrl/Cmd + V
  • CutIntent: Ctrl/Cmd + X
  • ToggleCommentIntent: Ctrl/Cmd + /

Function Key Intents

  • HelpIntent: F1 key

Usage Examples

Basic Usage with Event Callback

KeystrokeListener(
  onKeyEvent: (event) {
    debugPrint('Key: ${event.logicalKey.keyLabel}');
    debugPrint('Is pressed: ${event is KeyDownEvent}');
  },
  child: MyWidget(),
)

With Visual Debug Mode

Enable visual feedback (SnackBar) when keys are pressed:

KeystrokeListener(
  enableVisualDebug: true,
  onKeyEvent: (event) {
    // Your key handling logic
  },
  child: MyWidget(),
)

With Custom FocusNode

Manage focus programmatically:

final focusNode = FocusNode();

KeystrokeListener(
  focusNode: focusNode,
  onKeyEvent: (event) {
    // Your key handling logic
  },
  requestFocusOnInit: true,
  autoFocus: true,
  child: MyWidget(),
)

With State Management

Integrate with your app's state management:

class MyPage extends StatefulWidget {
  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  void _handleKeyEvent(KeyEvent event) {
    if (event.logicalKey == LogicalKeyboardKey.escape) {
      Navigator.pop(context);
    } else if (event.logicalKey == LogicalKeyboardKey.enter) {
      _submitForm();
    }
  }

  @override
  Widget build(BuildContext context) {
    return KeystrokeListener(
      onKeyEvent: _handleKeyEvent,
      child: MyFormWidget(),
    );
  }
}

Advanced Usage

Custom Intent Actions

While KeystrokeListener provides default actions, you can override them:

// The widget automatically handles shortcuts through FocusableActionDetector
// with default CallbackActions that log to debug output

Focus Management

The widget handles focus automatically but provides customization:

KeystrokeListener(
  requestFocusOnInit: true,  // Request focus when widget initializes
  autoFocus: true,            // Set autofocus on the FocusableActionDetector
  child: MyWidget(),
)

Integration with Forms

Perfect for custom form navigation:

KeystrokeListener(
  onKeyEvent: (event) {
    if (event.logicalKey == LogicalKeyboardKey.tab) {
      FocusScope.of(context).nextFocus();
    } else if (event.logicalKey == LogicalKeyboardKey.shiftLeft &&
               event is KeyDownEvent) {
      FocusScope.of(context).previousFocus();
    }
  },
  child: Form(
    child: Column(
      children: [
        TextFormField(label: Text('Field 1')),
        TextFormField(label: Text('Field 2')),
      ],
    ),
  ),
)

How It Works

The KeystrokeListener widget:

  1. Creates a FocusableActionDetector to capture keyboard shortcuts
  2. Manages FocusNode automatically or uses your provided FocusNode
  3. Maps keyboard events to Intent objects
  4. Executes Actions associated with each Intent
  5. Provides callbacks through the optional onKeyEvent parameter
  6. Includes a hidden TextField to ensure focus on Flutter Web (often hidden via SOffstage)

The widget is transparent to your UI layout, adding no visual overhead.

Properties

KeystrokeListener({
  required Widget child,
  void Function(KeyDownEvent keyDownEvent)? onKeyEvent,
  bool enableVisualDebug = false,
  FocusNode? focusNode,
  bool requestFocusOnInit = true,
  bool autoFocus = true,
  Key? key,
})
  • child: The widget to wrap with keyboard event handling
  • onKeyEvent: Optional callback when a key event occurs
  • enableVisualDebug: Show SnackBar with pressed key name
  • focusNode: Custom FocusNode for advanced focus management
  • requestFocusOnInit: Request focus when the widget initializes
  • autoFocus: Set autofocus on the FocusableActionDetector

Platform-Specific Notes

Flutter Web

The widget includes a hidden TextField to ensure focus works properly on Web, as the browser doesn't auto-focus widgets by default.

Mobile (Android/iOS)

Works well but may need explicit focus management depending on your UI design. The keyboard will still be accessible through the standard platform behavior.

Desktop (Windows/macOS/Linux)

Full keyboard support with all shortcuts working as expected.

Testing

The package includes comprehensive unit tests covering:

  • Intent creation and validation
  • Widget building and lifecycle
  • FocusNode management
  • Callback invocation
  • Edge cases and state changes

Run tests with:

flutter test

Example App

Check the example/ directory for a complete working example demonstrating:

  • Basic keystroke handling
  • Navigation with arrow keys
  • Keyboard shortcuts (Save, Copy, Paste, etc.)
  • Visual debug mode
  • Event logging

Run the example:

cd example
flutter run

Best Practices

  1. Use with Focus: Ensure your widget tree has proper focus management
  2. Avoid Nesting: Don't nest multiple KeystrokeListener widgets - it can cause conflicts
  3. Platform Testing: Test on your target platforms as keyboard behavior varies
  4. Custom Actions: For complex keyboard handling, implement custom Intent/Action pairs
  5. Accessibility: Always provide alternative input methods for accessibility

Troubleshooting

Keys not being captured on Web

  • Ensure the KeystrokeListener has focus
  • The widget includes a hidden TextField to help with Web focus
  • Consider wrapping your entire app's main widget with KeystrokeListener

FocusNode disposal errors

  • If providing a custom FocusNode, dispose it properly in your State's dispose method
  • The widget will handle disposal if you don't provide a focusNode

Multiple widgets receiving key events

  • Only one widget with primary focus receives key events
  • Ensure proper focus management if you have multiple KeystrokeListener instances

License

This package is licensed under the MIT License - see the LICENSE file for details.

Copyright (c) 2025 SoundSliced Ltd

Contributing

Contributions are welcome! Please feel free to submit issues and enhancement requests.

Changelog

See CHANGELOG.md for a list of changes in each release.

Libraries

keystroke_listener
keystroke_listener package