Last active
August 12, 2022 12:20
-
-
Save iapicca/58d67ce83695b65db788f4a213115ff7 to your computer and use it in GitHub Desktop.
issue_109437
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:flutter/services.dart'; | |
| void main() => runApp(const MaterialApp(home: HomePage())); | |
| extension HumanReadableRawKeyEventX on RawKeyEvent { | |
| String get details => [ | |
| this is RawKeyDownEvent ? 'down' : 'up', | |
| '$logicalKey', | |
| '$physicalKey', | |
| ].join('\n'); | |
| } | |
| class KeyLogger extends StatefulWidget { | |
| final FocusNode focus; | |
| const KeyLogger({ | |
| required this.focus, | |
| super.key, | |
| }); | |
| @override | |
| State<KeyLogger> createState() => _KeyLoggerState(); | |
| } | |
| class _KeyLoggerState extends State<KeyLogger> { | |
| late final ValueNotifier<List<RawKeyEvent>> _events; | |
| late final ScrollController _controller; | |
| @override | |
| void initState() { | |
| _events = ValueNotifier([])..addListener(_onEventsUpdated); | |
| _controller = ScrollController(); | |
| super.initState(); | |
| } | |
| @override | |
| void dispose() { | |
| _events..removeListener(_onEventsUpdated)..dispose(); | |
| _controller.dispose(); | |
| super.dispose(); | |
| } | |
| void addEvent(RawKeyEvent event) => _events.value = [..._events.value, event]; | |
| void _onEventsUpdated() { | |
| _controller.animateTo( | |
| _controller.position.maxScrollExtent, | |
| duration: kThemeAnimationDuration, | |
| curve: Curves.easeIn, | |
| ); | |
| } | |
| @override | |
| Widget build(context) => RawKeyboardListener( | |
| focusNode: widget.focus, | |
| onKey: addEvent, | |
| autofocus: true, | |
| child: ValueListenableBuilder<List<RawKeyEvent>>( | |
| valueListenable: _events, | |
| builder: (context, events, child) => ListView.separated( | |
| controller: _controller, | |
| itemCount: events.length, | |
| itemBuilder: (context, index) => Text(events[index].details), | |
| separatorBuilder: (context, index) => const Divider(), | |
| ), | |
| ), | |
| ); | |
| } | |
| class HomePage extends StatefulWidget { | |
| const HomePage({Key? key}) : super(key: key); | |
| @override | |
| State<HomePage> createState() => _HomePageState(); | |
| } | |
| class _HomePageState extends State<HomePage> { | |
| late final FocusNode _focus; | |
| late final ValueNotifier<bool> _hasFocus; | |
| @override | |
| void initState() { | |
| _focus = FocusNode()..addListener(_onFocusChanged); | |
| _hasFocus = ValueNotifier(_focus.hasFocus); | |
| super.initState(); | |
| } | |
| @override | |
| void dispose() { | |
| _focus | |
| ..removeListener(_onFocusChanged) | |
| ..dispose(); | |
| _hasFocus.dispose(); | |
| super.dispose(); | |
| } | |
| void _onFocusChanged() { | |
| _hasFocus.value = _focus.hasFocus; | |
| } | |
| void _changeFocus() { | |
| _focus.hasFocus ? _focus.unfocus() : _focus.requestFocus(); | |
| } | |
| @override | |
| Widget build(context) => Scaffold( | |
| body: KeyLogger(focus: _focus), | |
| floatingActionButton: FloatingActionButton( | |
| onPressed: _changeFocus, | |
| child: ValueListenableBuilder<bool>( | |
| valueListenable: _hasFocus, | |
| builder: (context, hasFocus, child) => | |
| hasFocus ? const Icon(Icons.done) : const Icon(Icons.error), | |
| ), | |
| ), | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment