Flutter自定义键盘插件perfect_keyboard的使用

Flutter自定义键盘插件perfect_keyboard的使用

Perfect Keyboard 插件为 Flutter 应用程序提供了一个可定制且交互式的键盘布局。它包括对多种语言的支持和高级按键管理功能,使开发者能够创建动态和本地化的键盘体验。

特性

Flutter App Demo
  • 高级键盘管理: 使用 `SuperKeyboardKey` 来管理按键属性及其关系。
  • 多语言支持: 通过 JSON 文件轻松切换不同语言的键盘布局。
  • 特殊键处理: 包括对空格、制表符、退格等特殊键的支持,使用 `SpecialKeys` 枚举。

入门指南

  1. 在你的 pubspec.yaml 文件中添加 perfect_keyboard 依赖:

    dependencies:
      perfect_keyboard: ^0.0.1
    
  2. 安装包:

    flutter pub get
    
  3. 导入并使用该包:

    import 'package:perfect_keyboard/perfect_keyboard.dart';
    

支持的语言

  • 土耳其语
  • 哈萨克语
  • 俄语
  • 阿拉伯语
  • 德语

示例代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:perfect_keyboard/perfect_keyboard.dart';

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Perfect Keyboard',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const SuperKeyboard(),
    );
  }
}

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

  [@override](/user/override)
  State<SuperKeyboard> createState() => _SuperKeyboardState();
}

class _SuperKeyboardState extends State<SuperKeyboard> with SingleTickerProviderStateMixin {
  List<String> specialKey = [];
  List<Locale> supportedLocales = const [
    Locale('tr'),
    Locale('kk'),
    Locale('ar'),
    Locale('de'),
    Locale('ru'),
  ];
  int index = 0;
  late AnimationController _controller;
  late Animation<Color> _colorAnimation;
  final List<Color> _colors = [
    Colors.lime,
    Colors.orange,
    Colors.purple,
    Colors.red,
    Colors.blue,
    Colors.yellow,
  ];
  ValueNotifier<List<int>> highLightedKeyPosition = ValueNotifier([-1, -1]);

  [@override](/user/override)
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    )..repeat(reverse: true);

    _colorAnimation = ColorTweenSequence(_colors).animate(_controller);
  }

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    highLightedKeyPosition.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.sizeOf(context).width;
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        systemNavigationBarColor: Colors.transparent,
        systemNavigationBarIconBrightness: Brightness.light,
      ),
      child: Scaffold(
        backgroundColor: Colors.grey,
        appBar: AppBar(
          title: const Text("Super Keyboard"),
        ),
        body: FutureBuilder<List<List<SuperKeyboardKey>>>(
          future: getKeyboard(locale: supportedLocales[index]),
          builder: (BuildContext context, AsyncSnapshot<List<List<SuperKeyboardKey>>> snapshot) {
            if (snapshot.hasData) {
              final data = snapshot.data!;
              final maxKeys = data.fold<int>(0, (max, item) => item.length > max ? item.length : max);
              final baseWidth = screenWidth / maxKeys;

              List<Widget> columnChildren = data.map((entry) {
                int specialKeysCount = entry.where((button) {
                  return button.isSpecial;
                }).length;
                double standardWidth = baseWidth - 1;
                double gaps = entry.length * 0.5;
                double extraWidth = screenWidth - (entry.length * standardWidth) - gaps;
                double extraWidthPerSpecialKey = specialKeysCount > 0 ? extraWidth / specialKeysCount : 0;
                List<Widget> rowChildren = entry.map<Widget>((button) {
                  double keyWidth = button.isSpecial
                      ? standardWidth + extraWidthPerSpecialKey
                      : standardWidth;
                  return KeyWidget(
                    keyboardKey: button,
                    width: keyWidth,
                    colorAnimation: _colorAnimation,
                    onSpecialKeyChanged: (List<String> keys) {
                      setState(() {
                        specialKey = keys;
                      });
                    },
                    onIndexChanged: (int newIndex) {
                      setState(() {
                        index = newIndex;
                      });
                    },
                    highLightedKeyPosition: highLightedKeyPosition,
                  );
                }).toList();
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: rowChildren,
                );
              }).toList();

              return SizedBox(
                width: screenWidth,
                height: 400,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: columnChildren,
                ),
              );
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            }
            return const CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

class KeyWidget extends StatefulWidget {
  final SuperKeyboardKey keyboardKey;
  final double width;
  final Animation<Color> colorAnimation;
  final Function(List<String>) onSpecialKeyChanged;
  final Function(int) onIndexChanged;
  final KeyWidget? nextKeyWidget;
  final KeyWidget? previousKeyWidget;
  final KeyWidget? topKeyWidget;
  final KeyWidget? bottomKeyWidget;
  final ValueNotifier<List<int>> highLightedKeyPosition;

  const KeyWidget({
    super.key,
    required this.keyboardKey,
    required this.width,
    required this.colorAnimation,
    required this.onSpecialKeyChanged,
    required this.onIndexChanged,
    this.nextKeyWidget,
    this.previousKeyWidget,
    this.topKeyWidget,
    this.bottomKeyWidget,
    required this.highLightedKeyPosition,
  });

  [@override](/user/override)
  State<KeyWidget> createState() => _KeyWidgetState();
}

class _KeyWidgetState extends State<KeyWidget> {
  List<String> specialKey = [];
  int index = 0;

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final keyLabel = widget.keyboardKey.value;

    return GestureDetector(
      onLongPressStart: (details) {
        setState(() {
          specialKey.add(keyLabel.toLowerCase());
        });
        widget.onSpecialKeyChanged(specialKey);
      },
      onLongPressEnd: (details) {
        setState(() {
          specialKey.clear();
        });
        widget.onSpecialKeyChanged(specialKey);
      },
      onHorizontalDragEnd: (DragEndDetails details) {
        if (keyLabel == 'Space') {
          if (details.velocity.pixelsPerSecond.dx > 500) {
            setState(() {
              index = (index + 1) % 5;
              widget.onIndexChanged(index);
            });
          } else if (details.velocity.pixelsPerSecond.dx < -500) {
            setState(() {
              index = (index - 1 + 6) % 5;
              widget.onIndexChanged(index);
            });
          }
        }
      },
      child: InkWell(
        onTap: () async {
          if (specialKey.isNotEmpty) {
            print("${specialKey.map((element) => '$element+').toString().replaceAll('(', '').replaceAll(')', '').replaceAll(' ', '').replaceAll(',', '')}$keyLabel");
          } else {
            print(keyLabel);
          }
          await animateKey();
        },
        customBorder: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(25),
        ),
        child: Padding(
          padding: const EdgeInsets.only(left: 0.25, right: 0.25),
          child: AnimatedBuilder(
              animation: widget.colorAnimation,
              builder: (context, child) {
                return ValueListenableBuilder(
                  builder: (context, val, child) {
                    return Ink(
                      width: widget.width - 0.5,
                      height: 50,
                      decoration: BoxDecoration(
                        boxShadow: [
                          BoxShadow(
                              color: widget.colorAnimation.value.withAlpha(100),
                              blurRadius: 20.0,
                              spreadRadius: 10.0,
                              offset: const Offset(0, 5)),
                        ],
                        borderRadius: BorderRadius.circular(25),
                        gradient: LinearGradient(
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          colors: [
                            Colors.black.withOpacity(0.5),
                            Colors.black.withOpacity(0.7),
                            Colors.black.withOpacity(0.75),
                            Colors.black.withOpacity(0.7),
                            Colors.black.withOpacity(0.5),
                          ],
                        ),
                        border: Border.all(
                          color: val.first == widget.keyboardKey.rowIndex && val.last == widget.keyboardKey.columnIndex
                              ? Colors.white
                              : widget.colorAnimation.value,
                          width: 1.5,
                        ),
                      ),
                      child: Stack(
                        children: [
                          if (widget.keyboardKey.shiftKey != null)
                            Positioned(
                              top: 5,
                              left: 8,
                              child: Text(
                                widget.keyboardKey.shiftKey!,
                                style: const TextStyle(fontSize: 9, color: Colors.white),
                                maxLines: 1,
                              ),
                            ),
                          Positioned.fill(
                            child: Align(
                              alignment: Alignment.center,
                              child: Builder(builder: (context) {
                                if (keyLabel == 'Space') {
                                  return const Icon(
                                    Icons.space_bar,
                                    color: Colors.white,
                                  );
                                } else if (keyLabel == 'Tab') {
                                  return const Icon(
                                    Icons.keyboard_tab,
                                    color: Colors.white,
                                  );
                                } else if (keyLabel == 'Backspace') {
                                  return const Icon(
                                    Icons.keyboard_backspace,
                                    color: Colors.white,
                                  );
                                } else if (keyLabel == 'Enter') {
                                  return const Icon(
                                    Icons.keyboard_return,
                                    color: Colors.white,
                                  );
                                } else if (keyLabel == 'Win') {
                                  return const Icon(
                                    Icons.window_sharp,
                                    color: Colors.white,
                                  );
                                } else if (keyLabel == 'Menu') {
                                  return const Icon(
                                    Icons.menu,
                                    color: Colors.white,
                                  );
                                } else {
                                  return Text(
                                    keyLabel,
                                    style: const TextStyle(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold),
                                    maxLines: 1,
                                  );
                                }
                              }),
                            ),
                          ),
                          if (widget.keyboardKey.altGrKey != null)
                            Positioned(
                              bottom: 5,
                              right: 8,
                              child: Text(
                                widget.keyboardKey.altGrKey!,
                                style: const TextStyle(fontSize: 9, color: Colors.white),
                                maxLines: 1,
                              ),
                            ),
                        ],
                      ),
                    );
                  },
                  valueListenable: widget.highLightedKeyPosition,
                );
              }),
        ),
      ),
    );
  }

  Future<void> animateKey() async {
    SuperKeyboardKey currentKey = widget.keyboardKey;
    while (currentKey.nextKey != null) {
      currentKey = currentKey.nextKey!;
      widget.highLightedKeyPosition.value = [currentKey.rowIndex, currentKey.columnIndex];
      await Future.delayed(const Duration(milliseconds: 50));
    }
    widget.highLightedKeyPosition.value = [-1, -1];
  }
}

class ColorTweenSequence extends Animatable<Color> {
  final List<Color> colors;

  ColorTweenSequence(this.colors);

  [@override](/user/override)
  Color transform(double t) {
    final int index = (t * (colors.length - 1)).toInt();
    final Color startColor = colors[index];
    final Color endColor = colors[(index + 1) % colors.length];
    final double localT = (t * (colors.length - 1)) - index;

    return Color.lerp(startColor, endColor, localT) ?? startColor;
  }
}

更多关于Flutter自定义键盘插件perfect_keyboard的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义键盘插件perfect_keyboard的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


perfect_keyboard 是一个 Flutter 插件,用于创建自定义键盘。它允许开发者根据应用需求设计自定义的键盘布局,并集成到 Flutter 应用中。以下是如何使用 perfect_keyboard 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 perfect_keyboard 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  perfect_keyboard: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在需要使用自定义键盘的 Dart 文件中导入 perfect_keyboard 插件:

import 'package:perfect_keyboard/perfect_keyboard.dart';

3. 创建自定义键盘

使用 PerfectKeyboard 组件来创建自定义键盘。你可以通过 keyboardType 属性来指定键盘的类型,或者通过 customKeys 属性来自定义键盘的布局。

PerfectKeyboard(
  keyboardType: KeyboardType.numeric,  // 数字键盘
  onTextInput: (String text) {
    // 处理键盘输入
    print("Input: $text");
  },
  onBackspace: () {
    // 处理退格键
    print("Backspace pressed");
  },
);

4. 自定义键盘布局

如果你想完全自定义键盘布局,可以使用 customKeys 属性。例如,创建一个包含字母和数字的键盘:

PerfectKeyboard(
  customKeys: [
    ['1', '2', '3'],
    ['4', '5', '6'],
    ['7', '8', '9'],
    ['0', 'A', 'B'],
    ['C', 'D', 'E'],
  ],
  onTextInput: (String text) {
    print("Input: $text");
  },
  onBackspace: () {
    print("Backspace pressed");
  },
);

5. 处理键盘输入

通过 onTextInputonBackspace 回调函数来处理键盘的输入和退格操作。你可以将这些输入应用到你的应用中,例如更新文本字段的内容。

class MyCustomKeyboard extends StatelessWidget {
  final TextEditingController _controller = TextEditingController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: _controller,
          decoration: InputDecoration(
            labelText: 'Enter text',
          ),
        ),
        PerfectKeyboard(
          keyboardType: KeyboardType.numeric,
          onTextInput: (String text) {
            _controller.text += text;
          },
          onBackspace: () {
            if (_controller.text.isNotEmpty) {
              _controller.text = _controller.text.substring(0, _controller.text.length - 1);
            }
          },
        ),
      ],
    );
  }
}
回到顶部