native_virtual_keyboard 0.2.6 copy "native_virtual_keyboard: ^0.2.6" to clipboard
native_virtual_keyboard: ^0.2.6 copied to clipboard

An almost pixel perfect Flutter replica of iOS and Android native keyboards.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:native_virtual_keyboard/native_virtual_keyboard.dart';

void main() {
  runApp(const MyApp());
}

enum AnimationType {
  none('No Animation'),
  simultaneous('Simultaneous'),
  staggeredSequential('Staggered (Sequential)'),
  staggeredDiagonal('Staggered (Diagonal)');

  final String label;
  const AnimationType(this.label);
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  KeyboardPlatform? _selectedPlatform;
  bool _useCustomTheme = false;
  bool _showEnter = true;
  bool _showBackspace = true;
  AnimationType _animationType = AnimationType.simultaneous;

  final _textController = TextEditingController();
  late final VirtualKeyboardController _keyboardController;

  static const _demoEnabledKeys = {
    VirtualKeyboardKey.q,
    VirtualKeyboardKey.w,
    VirtualKeyboardKey.e,
    VirtualKeyboardKey.r,
    VirtualKeyboardKey.t,
    VirtualKeyboardKey.y,
  };

  @override
  void initState() {
    super.initState();
    _keyboardController = VirtualKeyboardController(
      layout: EnglishQwertyKeyboardLayout(),
      onKeyPress: _onKeyPress,
    );
  }

  void _onKeyPress(VirtualKeyboardKey key) {
    final text = _textController.text;
    if (key == VirtualKeyboardKey.backspace) {
      if (text.isNotEmpty) {
        _textController.text = text.substring(0, text.length - 1);
      }
      return;
    }
    if (key == VirtualKeyboardKey.enter) {
      _textController.text = '$text\n';
      return;
    }
    _textController.text = text + key.text;
  }

  void _toggleKeys() {
    final currentlyRestricted = _keyboardController.enabledKeys.value != null;
    _keyboardController.setEnabledKeys(
      currentlyRestricted ? null : _demoEnabledKeys,
    );
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  KeyboardAnimationConfig? get _animationConfig {
    return switch (_animationType) {
      AnimationType.none => null,
      AnimationType.simultaneous => const KeyboardAnimationConfig(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      ),
      AnimationType.staggeredSequential => const KeyboardAnimationConfig(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        staggerDelay: Duration(milliseconds: 30),
        staggerPattern: StaggerPattern.sequential,
      ),
      AnimationType.staggeredDiagonal => const KeyboardAnimationConfig(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
        staggerDelay: Duration(milliseconds: 80),
        staggerPattern: StaggerPattern.diagonal,
      ),
    };
  }

  KeyboardTheme get _customTheme {
    return KeyboardTheme(
      backgroundColor: const Color(0xFF0F172A),
      keyTheme: const KeyboardKeyTheme(
        backgroundColor: Color(0xFF1E293B),
        pressedBackgroundColor: Color(0xFF334155),
        foregroundColor: Colors.white,
        shadows: [
          BoxShadow(color: Colors.black45, offset: Offset(0, 2), blurRadius: 2),
        ],
        overlayBackgroundColor: Color(0xFF1E293B),
        overlayTextColor: Colors.white,
        keyTextStyle: TextStyle(
          fontSize: 22,
          fontWeight: FontWeight.w500,
          fontFamily: 'Roboto',
        ),
      ),
      specialKeyTheme: const KeyboardSpecialKeyTheme(
        backgroundColor: Color(0xFF334155),
        pressedBackgroundColor: Color(0xFF475569),
        foregroundColor: Colors.white,
        pressedOverlayColor: Colors.white12,
        pressedFillIcon: true,
        shadows: [
          BoxShadow(color: Colors.black45, offset: Offset(0, 2), blurRadius: 2),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      themeMode: ThemeMode.system,
      theme: ThemeData.light(useMaterial3: true),
      darkTheme: ThemeData.dark(useMaterial3: true),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Native Virtual Keyboard'),
          centerTitle: true,
        ),
        body: Column(
          children: [
            Expanded(
              child: ListView(
                padding: const EdgeInsets.all(24),
                children: [
                  const Text(
                    'Configuration',
                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(height: 16),

                  // Platform Selector
                  InputDecorator(
                    decoration: const InputDecoration(
                      labelText: 'Simulated Platform',
                      border: OutlineInputBorder(),
                      contentPadding: EdgeInsets.symmetric(
                        horizontal: 12,
                        vertical: 4,
                      ),
                    ),
                    child: DropdownButtonHideUnderline(
                      child: DropdownButton<KeyboardPlatform?>(
                        value: _selectedPlatform,
                        isExpanded: true,
                        items: [
                          const DropdownMenuItem(
                            value: null,
                            child: Text('Auto-detect (System Default)'),
                          ),
                          ...KeyboardPlatform.values.map(
                            (e) =>
                                DropdownMenuItem(value: e, child: Text(e.name)),
                          ),
                        ],
                        onChanged: (value) =>
                            setState(() => _selectedPlatform = value),
                      ),
                    ),
                  ),
                  const SizedBox(height: 16),

                  // Animation Type Selector
                  InputDecorator(
                    decoration: const InputDecoration(
                      labelText: 'Animation Type',
                      border: OutlineInputBorder(),
                      contentPadding: EdgeInsets.symmetric(
                        horizontal: 12,
                        vertical: 4,
                      ),
                    ),
                    child: DropdownButtonHideUnderline(
                      child: DropdownButton<AnimationType>(
                        value: _animationType,
                        isExpanded: true,
                        items: AnimationType.values
                            .map(
                              (e) => DropdownMenuItem(
                                value: e,
                                child: Text(e.label),
                              ),
                            )
                            .toList(),
                        onChanged: (value) =>
                            setState(() => _animationType = value!),
                      ),
                    ),
                  ),
                  const SizedBox(height: 16),

                  // Toggle Keys Button — uses ValueListenableBuilder to avoid
                  // rebuilding the keyboard widget tree on toggle.
                  ValueListenableBuilder<Set<VirtualKeyboardKey>?>(
                    valueListenable: _keyboardController.enabledKeys,
                    builder: (context, enabledKeys, child) {
                      final isRestricted = enabledKeys != null;
                      return SizedBox(
                        height: 48,
                        child: FilledButton.icon(
                          onPressed: _toggleKeys,
                          icon: Icon(
                            isRestricted
                                ? Icons.visibility
                                : Icons.visibility_off,
                          ),
                          label: Text(
                            isRestricted
                                ? 'Enable All Keys'
                                : 'Disable Some Keys (keep Q W E R T Y)',
                          ),
                        ),
                      );
                    },
                  ),
                  const SizedBox(height: 16),

                  // Theme Toggle
                  SwitchListTile(
                    title: const Text('Use Custom Theme'),
                    subtitle: const Text('Demonstrates full customization API'),
                    value: _useCustomTheme,
                    onChanged: (v) => setState(() => _useCustomTheme = v),
                  ),
                  SwitchListTile(
                    title: const Text('Show Enter Key'),
                    value: _showEnter,
                    onChanged: (v) => setState(() => _showEnter = v),
                  ),
                  SwitchListTile(
                    title: const Text('Show Backspace Key'),
                    value: _showBackspace,
                    onChanged: (v) => setState(() => _showBackspace = v),
                  ),

                  const Divider(height: 32),

                  // Text Input visualization
                  TextField(
                    controller: _textController,
                    readOnly: true,
                    autofocus: true,
                    showCursor: true,
                    decoration: const InputDecoration(
                      labelText: 'Typed Text',
                      hintText: 'Type using the virtual keyboard below...',
                      border: OutlineInputBorder(),
                      filled: true,
                    ),
                    minLines: 3,
                    maxLines: 5,
                    style: const TextStyle(fontSize: 18),
                  ),
                ],
              ),
            ),

            // The Keyboard Widget
            Container(
              decoration: BoxDecoration(
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withValues(alpha: 0.1),
                    blurRadius: 10,
                    offset: const Offset(0, -2),
                  ),
                ],
              ),
              child: VirtualKeyboard(
                platform: _selectedPlatform,
                theme: _useCustomTheme ? _customTheme : null,
                showEnter: _showEnter,
                showBackspace: _showBackspace,
                animationConfig: _animationConfig,
                controller: _keyboardController,
              ),
            ),
          ],
        ),
      ),
    );
  }
}