// 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/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Flutter code sample for [TextInputControl]. void main() => runApp(const TextInputControlExampleApp()); class TextInputControlExampleApp extends StatelessWidget { const TextInputControlExampleApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: TextInputControlExample(), ); } } class TextInputControlExample extends StatefulWidget { const TextInputControlExample({super.key}); @override MyStatefulWidgetState createState() => MyStatefulWidgetState(); } class MyStatefulWidgetState extends State { 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 { 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( valueListenable: _inputControl.visible, builder: (_, bool visible, __) { return Visibility( visible: visible, child: FocusScope( canRequestFocus: false, child: TextFieldTapRegion( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ for (final String key in ['A', 'B', 'C']) ElevatedButton( child: Text(key), onPressed: () => _handleKeyPress(key), ), ], ), ), ), ); }, ); } } class MyTextInputControl with TextInputControl { TextEditingValue _editingState = TextEditingValue.empty; final ValueNotifier _visible = ValueNotifier(false); /// The input control's visibility state for updating the visual presentation. ValueListenable 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); } }