Virtual Keypad
A fully customizable virtual on-screen keyboard for Flutter with multi-language support. Perfect for kiosk applications, password entry UIs, custom input interfaces, and any application that needs to disable the system keyboard.
Features
- 🎹 Multiple keyboard layouts - Text, numeric, phone, email, URL, or custom layouts
- 🌍 Multi-language support - Built-in English and Bengali, easily extensible
- 📝 Smart TextField - Auto-adapts keyboard based on input type
- ⌨️ Physical keyboard support - Optional dual input (virtual + physical)
- 🎨 Fully themeable - Light, dark, or custom themes
- 📱 Cross-platform - Works on mobile, web, and desktop
- ⚡ Full text editing - Selection, copy/paste, cursor positioning
- 🔒 Password mode - Secure text obscuring
- 🔄 Auto-hide - Animated show/hide when focus changes
- ✨ Input-aware layouts - Email shows @, URL shows /, etc.
Installation
Add to your pubspec.yaml:
dependencies:
virtual_keypad: ^0.1.0
Then run:
flutter pub get
Quick Start
import 'package:virtual_keypad/virtual_keypad.dart';
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final controller = VirtualKeypadController();
@override
void initState() {
super.initState();
initializeKeyboardLayouts(); // Initialize language layouts
}
@override
Widget build(BuildContext context) {
return VirtualKeypadScope(
child: Column(
children: [
VirtualKeypadTextField(
controller: controller,
decoration: InputDecoration(labelText: 'Enter text'),
),
VirtualKeypad(),
],
),
);
}
}
Components
VirtualKeypadScope
Required wrapper that connects text fields to the keyboard. Place it above all VirtualKeypadTextField and VirtualKeypad widgets.
VirtualKeypadScope(
child: Column(
children: [
VirtualKeypadTextField(controller: controller1),
VirtualKeypadTextField(controller: controller2),
VirtualKeypad(), // Automatically connects to focused field
],
),
)
VirtualKeypadTextField
A TextField replacement that integrates with the virtual keyboard.
VirtualKeypadTextField(
controller: controller,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: KeyboardType.emailAddress, // Shows @ on keyboard
textInputAction: TextInputAction.next,
obscureText: false,
maxLength: 50,
maxLines: 1,
allowPhysicalKeyboard: false, // Block system keyboard
onSubmitted: (value) => print('Submitted: $value'),
)
Key Properties:
| Property | Type | Description |
|---|---|---|
controller |
VirtualKeypadController |
Required text controller |
keyboardType |
KeyboardType |
Determines keyboard layout |
textInputAction |
TextInputAction |
Action button behavior |
obscureText |
bool |
Password mode |
allowPhysicalKeyboard |
bool |
Allow system keyboard |
maxLength |
int? |
Character limit |
maxLines |
int |
Single or multi-line |
VirtualKeypad
The on-screen keyboard widget.
VirtualKeypad(
type: KeyboardType.text, // Override keyboard type
height: 280, // Keyboard height
theme: VirtualKeypadTheme.dark, // Visual theme
hideWhenUnfocused: true, // Auto-hide animation
animationDuration: Duration(milliseconds: 200),
onKeyPressed: (key) => print('Key: $key'),
)
Key Properties:
| Property | Type | Description |
|---|---|---|
type |
KeyboardType? |
Override layout (null = auto from text field) |
height |
double |
Keyboard height (default: 280) |
theme |
VirtualKeypadTheme |
Visual styling |
hideWhenUnfocused |
bool |
Animate hide when no focus |
customLayout |
KeyboardLayout? |
Custom key arrangement |
VirtualKeypadController
Extended TextEditingController with additional methods:
final controller = VirtualKeypadController();
// Text manipulation
controller.insertText('Hello');
controller.deleteBackward();
controller.clear();
// Cursor control
controller.cursorPosition = 5;
int pos = controller.cursorPosition;
// Selection
controller.selectAll();
TextSelection sel = controller.selection;
Keyboard Types
The keyboard automatically adapts based on keyboardType:
| KeyboardType | Layout | Special Keys |
|---|---|---|
text |
Full QWERTY | Letters, numbers, symbols |
emailAddress |
QWERTY + email | @ . _ - on primary row |
url |
QWERTY + URL | / : . on primary row |
number |
Number pad | 0-9, decimal |
numberSigned |
Signed number pad | 0-9, decimal, minus |
phone |
Phone dialer | 0-9, *, #, + |
multiline |
Full QWERTY | Enter inserts newline |
custom |
User-defined | Via customLayout |
Theming
Built-in Themes
VirtualKeypad(theme: VirtualKeypadTheme.light) // Light theme
VirtualKeypad(theme: VirtualKeypadTheme.dark) // Dark theme
Custom Theme
VirtualKeypad(
theme: VirtualKeypadTheme(
backgroundColor: Colors.grey[900]!,
keyColor: Colors.grey[800]!,
actionKeyColor: Colors.grey[700]!,
keyTextColor: Colors.white,
keyTextSize: 20,
keyBorderRadius: 8,
horizontalGap: 6,
verticalGap: 8,
keyDecoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8),
boxShadow: [BoxShadow(blurRadius: 2, offset: Offset(0, 1))],
),
),
)
Multi-Language Support
Built-in Languages
- English (
en) - QWERTY layout - Bengali (
bn) - বাংলা layout with Bengali numerals
Switching Languages
// Initialize at app startup
initializeKeyboardLayouts();
// Switch to Bengali
KeyboardLayoutProvider.instance.setLanguage('bn');
// Switch to English
KeyboardLayoutProvider.instance.setLanguage('en');
// Get current language
String code = KeyboardLayoutProvider.instance.currentLanguageCode;
KeyboardLanguage lang = KeyboardLayoutProvider.instance.currentLanguage;
Adding a New Language
- Create a language file in
lib/src/layouts/languages/:
// my_language.dart
import '../../enums.dart';
import '../../models.dart';
import '../keyboard_language.dart';
final KeyboardLayout _textLayoutPrimary = [
[
VirtualKey.character(text: 'a'),
VirtualKey.character(text: 'b'),
// ... more keys
],
// ... more rows
];
final KeyboardLanguage myLanguage = KeyboardLanguage(
code: 'xx',
name: 'My Language',
nativeName: 'Native Name',
textLayouts: KeyboardLayoutSet(
primary: _textLayoutPrimary,
secondary: _textLayoutSecondary,
tertiary: _textLayoutTertiary,
),
emailLayouts: KeyboardLayoutSet(...),
numberLayouts: KeyboardLayoutSet.single(_numberLayout),
phoneLayouts: KeyboardLayoutSet.single(_phoneLayout),
);
- Register the language:
KeyboardLayoutProvider.instance.registerLanguage(myLanguage);
KeyboardLayoutProvider.instance.setLanguage('xx');
Custom Layouts
Create completely custom keyboard layouts:
final myLayout = [
[
VirtualKey.character(text: '1'),
VirtualKey.character(text: '2'),
VirtualKey.character(text: '3'),
],
[
VirtualKey.character(text: '4'),
VirtualKey.character(text: '5'),
VirtualKey.character(text: '6'),
],
[
VirtualKey.action(action: KeyAction.backSpace, flex: 2),
VirtualKey.action(action: KeyAction.done, label: 'OK'),
],
];
VirtualKeypad(
type: KeyboardType.custom,
customLayout: myLayout,
)
VirtualKey Options
// Character key
VirtualKey.character(
text: 'a', // Lowercase character
capsText: 'A', // Uppercase (optional, auto-generated)
flex: 1, // Relative width
)
// Action key
VirtualKey.action(
action: KeyAction.shift,
label: '⇧', // Primary state label
altLabel: '⇪', // Secondary state label
flex: 2, // Relative width
)
Available Actions
| KeyAction | Description |
|---|---|
backSpace |
Delete character before cursor |
enter |
Newline or submit |
shift |
Toggle uppercase |
space |
Insert space |
symbols |
Switch to symbols layout |
symbolsAlt |
Switch to alternate symbols |
done |
Submit and close |
go |
Navigate (for URLs) |
search |
Search action |
send |
Send action |
Examples
Password Entry
VirtualKeypadScope(
child: Column(
children: [
VirtualKeypadTextField(
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
),
VirtualKeypad(hideWhenUnfocused: true),
],
),
)
Multi-Field Form
VirtualKeypadScope(
child: Column(
children: [
VirtualKeypadTextField(
controller: emailController,
keyboardType: KeyboardType.emailAddress,
decoration: InputDecoration(labelText: 'Email'),
),
VirtualKeypadTextField(
controller: phoneController,
keyboardType: KeyboardType.phone,
decoration: InputDecoration(labelText: 'Phone'),
),
VirtualKeypad(), // Auto-switches layout based on focus
],
),
)
Kiosk Mode (No System Keyboard)
VirtualKeypadTextField(
controller: controller,
allowPhysicalKeyboard: false, // Blocks system keyboard
)
Dual Input (Virtual + Physical)
VirtualKeypadTextField(
controller: controller,
allowPhysicalKeyboard: true, // Both keyboards work
)
API Reference
VirtualKeypadScope
| Method | Description |
|---|---|
VirtualKeypadScope.of(context) |
Get scope state from context |
VirtualKeypadController
| Property/Method | Description |
|---|---|
text |
Current text value |
selection |
Current selection |
cursorPosition |
Cursor position (get/set) |
insertText(String) |
Insert at cursor |
deleteBackward() |
Delete before cursor |
selectAll() |
Select all text |
clear() |
Clear all text |
KeyboardLayoutProvider
| Property/Method | Description |
|---|---|
instance |
Singleton instance |
currentLanguage |
Current KeyboardLanguage |
currentLanguageCode |
Current language code |
languages |
All registered languages |
registerLanguage(lang) |
Add a language |
setLanguage(code) |
Switch language |
getLayouts(inputType) |
Get layouts for input type |
License
MIT License - see LICENSE for details.
Contributing
Contributions are welcome! Please submit pull requests to the repository.
Support
Libraries
- virtual_keypad
- A Flutter package for creating customizable virtual on-screen keyboards.