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.

pub package License: MIT

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

  1. 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),
);
  1. 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.