1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MaterialApp(
title: 'Hardware Key Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Hardware Key Demo'),
),
body: const Center(
child: RawKeyboardDemo(),
),
),
));
}
class RawKeyboardDemo extends StatefulWidget {
const RawKeyboardDemo({Key key}) : super(key: key);
@override
_HardwareKeyDemoState createState() => _HardwareKeyDemoState();
}
class _HardwareKeyDemoState extends State<RawKeyboardDemo> {
final FocusNode _focusNode = FocusNode();
RawKeyEvent _event;
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
KeyEventResult _handleKeyEvent(FocusNode node, RawKeyEvent event) {
setState(() {
_event = event;
});
return KeyEventResult.ignored;
}
String _asHex(int value) => value != null ? '0x${value.toRadixString(16)}' : 'null';
String _getEnumName(dynamic enumItem) {
final String name = '$enumItem';
final int index = name.indexOf('.');
return index == -1 ? name : name.substring(index + 1);
}
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return Focus(
focusNode: _focusNode,
onKey: _handleKeyEvent,
autofocus: true,
child: AnimatedBuilder(
animation: _focusNode,
builder: (BuildContext context, Widget child) {
if (!_focusNode.hasFocus) {
return GestureDetector(
onTap: () {
_focusNode.requestFocus();
},
child: Text('Tap to focus', style: textTheme.headline4),
);
}
if (_event == null) {
return Text('Press a key', style: textTheme.headline4);
}
final RawKeyEventData data = _event.data;
final String modifierList = data.modifiersPressed.keys.map<String>(_getEnumName).join(', ').replaceAll('Modifier', '');
final List<Widget> dataText = <Widget>[
Text('${_event.runtimeType}'),
if (_event.character?.isNotEmpty ?? false) Text('character produced: "${_event.character}"'),
Text('modifiers set: $modifierList'),
];
if (data is RawKeyEventDataAndroid) {
const int combiningCharacterMask = 0x7fffffff;
final String codePointChar = String.fromCharCode(combiningCharacterMask & data.codePoint);
dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)}: $codePointChar)'));
final String plainCodePointChar = String.fromCharCode(combiningCharacterMask & data.plainCodePoint);
dataText.add(Text('plainCodePoint: ${data.plainCodePoint} (${_asHex(data.plainCodePoint)}: $plainCodePointChar)'));
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode} (${_asHex(data.scanCode)})'));
dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})'));
dataText.add(Text('source: ${data.eventSource} (${_asHex(data.eventSource)})'));
dataText.add(Text('vendorId: ${data.vendorId} (${_asHex(data.vendorId)})'));
dataText.add(Text('productId: ${data.productId} (${_asHex(data.productId)})'));
dataText.add(Text('flags: ${data.flags} (${_asHex(data.flags)})'));
dataText.add(Text('repeatCount: ${data.repeatCount} (${_asHex(data.repeatCount)})'));
} else if (data is RawKeyEventDataFuchsia) {
dataText.add(Text('codePoint: ${data.codePoint} (${_asHex(data.codePoint)})'));
dataText.add(Text('hidUsage: ${data.hidUsage} (${_asHex(data.hidUsage)})'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataMacOs) {
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('characters: ${data.characters}'));
dataText.add(Text('charactersIgnoringModifiers: ${data.charactersIgnoringModifiers}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataLinux) {
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode}'));
dataText.add(Text('unicodeScalarValues: ${data.unicodeScalarValues}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataWindows) {
dataText.add(Text('keyCode: ${data.keyCode} (${_asHex(data.keyCode)})'));
dataText.add(Text('scanCode: ${data.scanCode}'));
dataText.add(Text('characterCodePoint: ${data.characterCodePoint}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
} else if (data is RawKeyEventDataWeb) {
dataText.add(Text('key: ${data.key}'));
dataText.add(Text('code: ${data.code}'));
dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})'));
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
if (_event.character != null) {
dataText.add(Text('character: ${_event.character}'));
}
for (final ModifierKey modifier in data.modifiersPressed.keys) {
for (final KeyboardSide side in KeyboardSide.values) {
if (data.isModifierPressed(modifier, side: side)) {
dataText.add(
Text('${_getEnumName(side)} ${_getEnumName(modifier).replaceAll('Modifier', '')} pressed'),
);
}
}
}
final List<String> pressed = <String>['Pressed:'];
for (final LogicalKeyboardKey key in RawKeyboard.instance.keysPressed) {
pressed.add(key.debugName);
}
dataText.add(Text(pressed.join(' ')));
return DefaultTextStyle(
style: textTheme.subtitle1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: dataText,
),
);
},
),
);
}
}