You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter计算器App自定义Widget模拟原生键盘实现方案咨询

在Flutter中实现自定义计算器键盘Widget

核心实现思路

通过禁用输入框的原生键盘,监听输入框焦点状态,在底部弹出自定义Widget作为键盘,将键盘输入操作与输入框的文本控制器绑定,同时模拟原生键盘的弹出/收起动画与样式。


步骤1:配置输入框,禁用原生键盘

给TextField设置keyboardType: TextInputType.none屏蔽原生键盘,用FocusNode监听焦点变化以控制自定义键盘的显示/隐藏,通过TextEditingController处理输入文本的更新。

final FocusNode _focusNode = FocusNode();
final TextEditingController _controller = TextEditingController();
bool _isKeyboardVisible = false;

@override
void initState() {
  super.initState();
  _focusNode.addListener(() {
    setState(() {
      _isKeyboardVisible = _focusNode.hasFocus;
    });
  });
}

@override
void dispose() {
  _focusNode.dispose();
  _controller.dispose();
  super.dispose();
}

// 输入框Widget
TextField(
  controller: _controller,
  focusNode: _focusNode,
  keyboardType: TextInputType.none, // 禁用原生键盘
  decoration: const InputDecoration(
    hintText: "输入计算内容",
    border: OutlineInputBorder(),
  ),
  textAlign: TextAlign.right,
  style: const TextStyle(fontSize: 24),
  readOnly: false, // 保持可点击聚焦
)

步骤2:实现自定义键盘的弹出动画与原生样式

用AnimatedContainer结合SlideTransition实现底部滑入动画,高度设为原生键盘常规高度(300dp左右),添加圆角、阴影模仿原生样式,用GridView实现计算器按键布局。

// 自定义键盘Widget
Widget _buildCustomKeyboard() {
  return AnimatedContainer(
    duration: const Duration(milliseconds: 200), // 匹配原生动画时长
    height: _isKeyboardVisible ? 300 : 0,
    child: SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(0, 1),
        end: Offset.zero,
      ).animate(CurvedAnimation(
        parent: ModalRoute.of(context)!.animation!,
        curve: Curves.easeOut,
      )),
      child: Container(
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(16),
            topRight: Radius.circular(16),
          ),
          boxShadow: [
            BoxShadow(
              color: Colors.black12,
              blurRadius: 10,
              spreadRadius: 2,
            )
          ],
        ),
        child: GridView.count(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          crossAxisCount: 4,
          children: [
            _buildKeyButton("1"),
            _buildKeyButton("2"),
            _buildKeyButton("3"),
            _buildKeyButton("÷", color: Colors.orange),
            _buildKeyButton("4"),
            _buildKeyButton("5"),
            _buildKeyButton("6"),
            _buildKeyButton("×", color: Colors.orange),
            _buildKeyButton("7"),
            _buildKeyButton("8"),
            _buildKeyButton("9"),
            _buildKeyButton("-", color: Colors.orange),
            _buildKeyButton("C", color: Colors.grey),
            _buildKeyButton("0"),
            _buildKeyButton("←", color: Colors.grey),
            _buildKeyButton("+", color: Colors.orange),
            _buildKeyButton("=", color: Colors.orange, crossAxisCount: 4),
          ],
        ),
      ),
    ),
  );
}

// 键盘按键Widget
Widget _buildKeyButton(String text, {Color color = Colors.black, int crossAxisCount = 1}) {
  return InkWell(
    onTap: () => _handleKeyPress(text),
    child: Container(
      alignment: Alignment.center,
      child: Text(
        text,
        style: TextStyle(fontSize: 24, color: color),
      ),
    ),
  );
}

步骤3:处理键盘输入逻辑

编写_handleKeyPress方法,根据按键类型实现数字输入、运算符输入、清空、删除、计算等功能:

void _handleKeyPress(String key) {
  switch (key) {
    case "C":
      _controller.clear();
      break;
    case "←":
      if (_controller.text.isNotEmpty) {
        _controller.text = _controller.text.substring(0, _controller.text.length - 1);
      }
      break;
    case "=":
      // 这里可添加表达式解析与计算逻辑,示例为简化处理
      try {
        // 实际开发中需处理运算符优先级、异常输入等,可引入计算库
        double result = double.parse(_controller.text); // 仅示例,需替换为真实计算逻辑
        _controller.text = result.toString();
      } catch (e) {
        _controller.text = "输入错误";
      }
      break;
    default:
      _controller.text += key;
      break;
  }
}

步骤4:整合页面布局

将输入框和自定义键盘整合到Scaffold中,键盘放在底部区域:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text("计算器")),
    body: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            focusNode: _focusNode,
            keyboardType: TextInputType.none,
            decoration: const InputDecoration(
              hintText: "输入计算内容",
              border: OutlineInputBorder(),
            ),
            textAlign: TextAlign.right,
            style: const TextStyle(fontSize: 24),
            readOnly: false,
          ),
          const SizedBox(height: 20),
          // 可添加结果显示区域等其他UI
        ],
      ),
    ),
    bottomSheet: _buildCustomKeyboard(),
  );
}

额外优化建议

  • 点击空白区域收起键盘:给页面添加GestureDetector,点击时调用_focusNode.unfocus()。
  • 适配屏幕:用MediaQuery.of(context).size动态调整键盘高度。
  • 按键反馈:添加振动或按压动画,增强原生交互体验。

内容的提问来源于stack exchange,提问作者user21766581

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 00:22:35