flutter_custom_keyboard 1.0.0
flutter_custom_keyboard: ^1.0.0 copied to clipboard
A highly customizable Flutter keyboard widget with modern animations, haptic feedback, and cross-platform support. Perfect replacement for system keyboards in forms, login screens, and search fields.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_custom_keyboard/flutter_custom_keyboard.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Keyboard Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const KeyboardScaffold(
child: KeyboardDemoScreen(),
),
);
}
}
class KeyboardDemoScreen extends StatefulWidget {
const KeyboardDemoScreen({super.key});
@override
State<KeyboardDemoScreen> createState() => _KeyboardDemoScreenState();
}
class _KeyboardDemoScreenState extends State<KeyboardDemoScreen> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _searchController = TextEditingController();
final TextEditingController _notesController = TextEditingController();
@override
void dispose() {
_usernameController.dispose();
_passwordController.dispose();
_emailController.dispose();
_phoneController.dispose();
_searchController.dispose();
_notesController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Flutter Custom Keyboard Demo'),
elevation: 0,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Section
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.deepPurple.shade100,
Colors.deepPurple.shade50,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.deepPurple.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.keyboard_alt_outlined,
color: Colors.deepPurple.shade700,
size: 28,
),
const SizedBox(width: 12),
Text(
'Custom Keyboard Package',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.deepPurple.shade800,
),
),
],
),
const SizedBox(height: 8),
Text(
'Tap any input field below to see the custom keyboard in action!',
style: TextStyle(
fontSize: 14,
color: Colors.deepPurple.shade600,
),
),
],
),
),
const SizedBox(height: 32),
// Login Form Section
_buildSection(
title: 'π Login Form',
subtitle: 'Username and password fields',
children: [
CustomTextField(
controller: _usernameController,
labelText: 'Username',
hintText: 'Enter your username',
prefixIcon: const Icon(Icons.person_outline),
),
const SizedBox(height: 16),
CustomTextField(
controller: _passwordController,
labelText: 'Password',
hintText: 'Enter your password',
obscureText: true,
prefixIcon: const Icon(Icons.lock_outline),
),
],
),
const SizedBox(height: 32),
// Contact Information Section
_buildSection(
title: 'π§ Contact Information',
subtitle: 'Email and phone number',
children: [
CustomTextField(
controller: _emailController,
labelText: 'Email Address',
hintText: 'your.email@example.com',
keyboardType: TextInputType.emailAddress,
prefixIcon: const Icon(Icons.email_outlined),
),
const SizedBox(height: 16),
CustomTextField(
controller: _phoneController,
labelText: 'Phone Number',
hintText: '+1 (555) 123-4567',
keyboardType: TextInputType.phone,
prefixIcon: const Icon(Icons.phone_outlined),
),
],
),
const SizedBox(height: 32),
// Search Section
_buildSection(
title: 'π Search',
subtitle: 'Quick search functionality',
children: [
CustomTextField(
controller: _searchController,
labelText: 'Search',
hintText: 'What are you looking for?',
prefixIcon: const Icon(Icons.search),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () => _searchController.clear(),
),
),
],
),
const SizedBox(height: 32),
// Notes Section
_buildSection(
title: 'π Notes',
subtitle: 'Multi-line text input',
children: [
CustomTextField(
controller: _notesController,
labelText: 'Notes',
hintText: 'Enter your notes here...',
maxLines: 4,
prefixIcon: const Icon(Icons.note_outlined),
),
],
),
const SizedBox(height: 32),
// Features Section
_buildFeaturesSection(),
const SizedBox(height: 32),
// Action Buttons
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: _clearAllFields,
icon: const Icon(Icons.clear_all),
label: const Text('Clear All'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton.icon(
onPressed: _showSummary,
icon: const Icon(Icons.summarize),
label: const Text('Show Summary'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
const SizedBox(height: 40),
],
),
),
);
}
Widget _buildSection({
required String title,
required String subtitle,
required List<Widget> children,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 16),
...children,
],
);
}
Widget _buildFeaturesSection() {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.auto_awesome,
color: Colors.amber.shade700,
size: 24,
),
const SizedBox(width: 12),
const Text(
'Package Features',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
const SizedBox(height: 16),
_buildFeatureItem(
icon: Icons.animation,
title: 'Smooth Animations',
description: 'Multi-layer animations with scale, ripple, and flash effects',
),
_buildFeatureItem(
icon: Icons.vibration,
title: 'Haptic Feedback',
description: 'Different vibration patterns for different key types',
),
_buildFeatureItem(
icon: Icons.keyboard_alt,
title: 'Multiple Layouts',
description: 'QWERTY, numeric, and symbols layouts',
),
_buildFeatureItem(
icon: Icons.devices,
title: 'Cross-Platform',
description: 'Works seamlessly on Android and iOS',
),
_buildFeatureItem(
icon: Icons.auto_fix_high,
title: 'Smart Features',
description: 'Auto-scrolling, focus management, and more',
),
],
),
);
}
Widget _buildFeatureItem({
required IconData icon,
required String title,
required String description,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.deepPurple.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
size: 20,
color: Colors.deepPurple.shade700,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
Text(
description,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
);
}
void _clearAllFields() {
_usernameController.clear();
_passwordController.clear();
_emailController.clear();
_phoneController.clear();
_searchController.clear();
_notesController.clear();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('All fields cleared!'),
behavior: SnackBarBehavior.floating,
),
);
}
void _showSummary() {
final summary = '''
Username: ${_usernameController.text}
Password: ${'β’' * _passwordController.text.length}
Email: ${_emailController.text}
Phone: ${_phoneController.text}
Search: ${_searchController.text}
Notes: ${_notesController.text}
'''.trim();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Input Summary'),
content: SingleChildScrollView(
child: Text(summary.isEmpty ? 'No input provided.' : summary),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'),
),
],
),
);
}
}