Flutter实现软键盘与其它区域丝滑切换

问题产生

本次要解决的问题是实现实现软键盘和其它区域的丝滑切换,我们点击微信的表情和键盘,不难发现输入框可以一直保持稳定,仿佛表情抽屉就藏在软键盘后面。 我刚开始的解决思路是,当软键盘收起展示表情抽屉,当表情抽屉收起后展示软键盘,但很快就发现了问题,软键盘与表情抽屉切换的过程中,会造成页面的震荡

问题分析

我录视频后慢倍速分析,发现原因是键盘收缩同时表情抽屉的container开始上升,但键盘收缩的距离不等于container上升的距离,导致输入框不稳定。 查阅资料后发现安卓的思路是获取软键盘高度,将输入框控制在一个固定高度,但是Flutter各种组件的位置取决于与其它组件的位置关系,除非使用stack图层,才能将输入框设置为固定高度,但这样做会给键盘收起步骤带来困难,因此pass。

解决思路

其实解决方法就在问题分析中,不知道你发现了吗

键盘收缩同时表情抽屉的container开始上升,但键盘收缩的距离不等于container上升的距离,导致输入框不稳定

那我们就让键盘收缩距离等于container上升距离,不就可以保持输入框的高度了吗 我们以语音输入区为例,重要步骤主要有两步

  1. 获取键盘的最大高度,即输入框底部的距离,我们设置为InputHeight
  2. 获取键盘的实时高度,使用InputHeight-currentKeyboardHeight,即可获得voiceInputHeight的高度,使用Provider实时更新container的高度

provider代码如下

dart 复制代码
import 'package:flutter/material.dart';  
class ProviderChatContent with ChangeNotifier {
  double _voiceInputHeight = 0.0;

  double get voiceInputHeight => _voiceInputHeight;

  void update_voiceInputHeight(double newHeight) {
    _voiceInputHeight = newHeight;
    notifyListeners();
  }
}

组件代码如下

dart 复制代码
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class YourClassName extends StatefulWidget {
  @override
  _YourClassNameState createState() => _YourClassNameState();
}

class _YourClassNameState extends State<YourClassName> with WidgetsBindingObserver {
  Timer? _debounceTimer;
  final FocusNode _focusNode = FocusNode();
  double ContainerHeight = 0.0;
  double InputHeight = 0.0; // 假设这是初始输入框的高度
  double voiceInputHeight = 0.0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _debounceTimer?.cancel();
    _focusNode.dispose();
    super.dispose();
  }

  // 获取键盘的实时高度
  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    if (mounted) {
      final provider = Provider.of<ProviderChatContent>(context, listen: false);
      final double currentKeyboardHeight = EdgeInsets.fromWindowPadding(
        WidgetsBinding.instance.window.viewInsets,
        WidgetsBinding.instance.window.devicePixelRatio,
      ).bottom;

      // 设置一个新的计时器
      _debounceTimer?.cancel(); // 取消之前的计时器
      _debounceTimer = Timer(const Duration(milliseconds: 39), () {
        if (currentKeyboardHeight > 0 && _focusNode.hasFocus) {
          setState(() {
            // 更新 ContainerHeight 为 InputHeight 和 currentKeyboardHeight 的最大值
            ContainerHeight = max(InputHeight, currentKeyboardHeight);
          });
        }
      });

      voiceInputHeight = InputHeight - currentKeyboardHeight;
      provider.update_voiceInputHeight(voiceInputHeight);
    }
  }

  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<ProviderChatContent>(context);
    return Scaffold(
      body: Column(
        children: [
          Row(
            // 此处填写输入框和其它代码
          ),
          Container(
            // 此处写你的container
            height: max(provider.voiceInputHeight, 0.0), // 使用最大值确保不会有负数的高度
          ),
        ],
      ),
    );
}

问题升级

我们再往深思考一下,这是软键盘和其中某一个区域转化的思路,如果再加区域2,那区域1、区域2和软键盘直接切换,如何保持输入框高度不变呢 在此我提供一个思路

  1. 在Provider中添加变量命名为Container1Show、Container2Show,为bool类型
  2. 点击按钮为bool值赋值,同时高度计算沿用上面的方法,只是这次输入框距离底部高度最大值为这三部分的高度之和,而不仅仅是两部分高度之和

三区域转化本人并没有尝试,有需要的uu可以尝试后留下评论。

相关推荐
腾讯TNTWeb前端团队29 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom5 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom5 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom6 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom6 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试