1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/foundation.dart';
6import 'package:flutter/services.dart';
7
8import 'focus_manager.dart';
9import 'focus_scope.dart';
10import 'framework.dart';
11
12export 'package:flutter/services.dart' show RawKeyEvent;
13
14/// A widget that calls a callback whenever the user presses or releases a key
15/// on a keyboard.
16///
17/// The [RawKeyboardListener] is deprecated and will be removed. Use
18/// [KeyboardListener] instead.
19///
20/// A [RawKeyboardListener] is useful for listening to raw key events and
21/// hardware buttons that are represented as keys. Typically used by games and
22/// other apps that use keyboards for purposes other than text entry.
23///
24/// For text entry, consider using a [EditableText], which integrates with
25/// on-screen keyboards and input method editors (IMEs).
26///
27/// See also:
28///
29/// * [EditableText], which should be used instead of this widget for text
30/// entry.
31/// * [KeyboardListener], a similar widget based on the newer [HardwareKeyboard]
32/// API.
33@Deprecated(
34 'Use KeyboardListener instead. '
35 'This feature was deprecated after v3.18.0-2.0.pre.',
36)
37class RawKeyboardListener extends StatefulWidget {
38 /// Creates a widget that receives raw keyboard events.
39 ///
40 /// For text entry, consider using a [EditableText], which integrates with
41 /// on-screen keyboards and input method editors (IMEs).
42 @Deprecated(
43 'Use KeyboardListener instead. '
44 'This feature was deprecated after v3.18.0-2.0.pre.',
45 )
46 const RawKeyboardListener({
47 super.key,
48 required this.focusNode,
49 this.autofocus = false,
50 this.includeSemantics = true,
51 this.onKey,
52 required this.child,
53 });
54
55 /// Controls whether this widget has keyboard focus.
56 final FocusNode focusNode;
57
58 /// {@macro flutter.widgets.Focus.autofocus}
59 final bool autofocus;
60
61 /// {@macro flutter.widgets.Focus.includeSemantics}
62 final bool includeSemantics;
63
64 /// Called whenever this widget receives a raw keyboard event.
65 final ValueChanged<RawKeyEvent>? onKey;
66
67 /// The widget below this widget in the tree.
68 ///
69 /// {@macro flutter.widgets.ProxyWidget.child}
70 final Widget child;
71
72 @override
73 State<RawKeyboardListener> createState() => _RawKeyboardListenerState();
74
75 @override
76 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
77 super.debugFillProperties(properties);
78 properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode));
79 }
80}
81
82class _RawKeyboardListenerState extends State<RawKeyboardListener> {
83 @override
84 void initState() {
85 super.initState();
86 widget.focusNode.addListener(_handleFocusChanged);
87 }
88
89 @override
90 void didUpdateWidget(RawKeyboardListener oldWidget) {
91 super.didUpdateWidget(oldWidget);
92 if (widget.focusNode != oldWidget.focusNode) {
93 oldWidget.focusNode.removeListener(_handleFocusChanged);
94 widget.focusNode.addListener(_handleFocusChanged);
95 }
96 }
97
98 @override
99 void dispose() {
100 widget.focusNode.removeListener(_handleFocusChanged);
101 _detachKeyboardIfAttached();
102 super.dispose();
103 }
104
105 void _handleFocusChanged() {
106 if (widget.focusNode.hasFocus) {
107 _attachKeyboardIfDetached();
108 } else {
109 _detachKeyboardIfAttached();
110 }
111 }
112
113 bool _listening = false;
114
115 void _attachKeyboardIfDetached() {
116 if (_listening) {
117 return;
118 }
119 RawKeyboard.instance.addListener(_handleRawKeyEvent);
120 _listening = true;
121 }
122
123 void _detachKeyboardIfAttached() {
124 if (!_listening) {
125 return;
126 }
127 RawKeyboard.instance.removeListener(_handleRawKeyEvent);
128 _listening = false;
129 }
130
131 void _handleRawKeyEvent(RawKeyEvent event) {
132 widget.onKey?.call(event);
133 }
134
135 @override
136 Widget build(BuildContext context) {
137 return Focus(
138 focusNode: widget.focusNode,
139 autofocus: widget.autofocus,
140 includeSemantics: widget.includeSemantics,
141 child: widget.child,
142 );
143 }
144}
145