blob: 112ba35ba8d070018949e509297d1aa7b79340fc [file] [log] [blame]
// 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.
// Flutter code sample for TextInputControl
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
MyStatefulWidgetState createState() => MyStatefulWidgetState();
}
class MyStatefulWidgetState extends State<MyStatefulWidget> {
final TextEditingController _controller = TextEditingController();
final FocusNode _focusNode = FocusNode();
@override
void dispose() {
super.dispose();
_controller.dispose();
_focusNode.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(
autofocus: true,
controller: _controller,
focusNode: _focusNode,
decoration: InputDecoration(
suffix: IconButton(
icon: const Icon(Icons.clear),
tooltip: 'Clear and unfocus',
onPressed: () {
_controller.clear();
_focusNode.unfocus();
},
),
),
),
),
bottomSheet: const MyVirtualKeyboard(),
);
}
}
class MyVirtualKeyboard extends StatefulWidget {
const MyVirtualKeyboard({super.key});
@override
MyVirtualKeyboardState createState() => MyVirtualKeyboardState();
}
class MyVirtualKeyboardState extends State<MyVirtualKeyboard> {
final MyTextInputControl _inputControl = MyTextInputControl();
@override
void initState() {
super.initState();
_inputControl.register();
}
@override
void dispose() {
super.dispose();
_inputControl.unregister();
}
void _handleKeyPress(String key) {
_inputControl.processUserInput(key);
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: _inputControl.visible,
builder: (_, bool visible, __) {
return Visibility(
visible: visible,
child: FocusScope(
canRequestFocus: false,
child: TextFieldTapRegion(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
for (final String key in <String>['A', 'B', 'C'])
ElevatedButton(
child: Text(key),
onPressed: () => _handleKeyPress(key),
),
],
),
),
),
);
},
);
}
}
class MyTextInputControl with TextInputControl {
TextEditingValue _editingState = TextEditingValue.empty;
final ValueNotifier<bool> _visible = ValueNotifier<bool>(false);
/// The input control's visibility state for updating the visual presentation.
ValueListenable<bool> get visible => _visible;
/// Register the input control.
void register() => TextInput.setInputControl(this);
/// Restore the original platform input control.
void unregister() => TextInput.restorePlatformInputControl();
@override
void show() => _visible.value = true;
@override
void hide() => _visible.value = false;
@override
void setEditingState(TextEditingValue value) => _editingState = value;
/// Process user input.
///
/// Updates the internal editing state by inserting the input text,
/// and by replacing the current selection if any.
void processUserInput(String input) {
_editingState = _editingState.copyWith(
text: _insertText(input),
selection: _replaceSelection(input),
);
// Request the attached client to update accordingly.
TextInput.updateEditingValue(_editingState);
}
String _insertText(String input) {
final String text = _editingState.text;
final TextSelection selection = _editingState.selection;
return text.replaceRange(selection.start, selection.end, input);
}
TextSelection _replaceSelection(String input) {
final TextSelection selection = _editingState.selection;
return TextSelection.collapsed(offset: selection.start + input.length);
}
}