| // 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); |
| } |
| } |