一洼绿地

Flutter 再论编辑器的键盘处理

·3 min read

Flutter 编辑器键盘处理

简介

| 不能说终极版吧 | 比网易,讯飞AI 的编辑器处理得更好

主要逻辑

  // 这里差点意思,不能和屏幕刷新同频,不太自然
  // 最好还是借助 animation 来处理,但互相干扰,处理比较麻烦
  // menubar 滑入
  void slideinMenubar(int x) {
    double m = maxToolbarHeight ?? ui.windowHeight * 0.382; 
    toolbarHeight = toolbarHeight + x;
    if (toolbarHeight>=m-10) {
      toolbarHeight = m;
      setStatus();
    }
    else {
      setStatus();
      Future.delayed(const Duration(milliseconds: 1), (){
        slideinMenubar(x);
      });
    }
  }

  // menubar 滑出
  void slideoutMenubar(int x) {
    toolbarHeight = toolbarHeight - x;
    if (toolbarHeight<=10) {
      toolbarHeight = 0;
      setStatus();
    }
    else {
      setStatus();
      Future.delayed(const Duration(milliseconds: 1), (){
        slideoutMenubar(x);
      });
    }
  }

  // 切换 menuLabel
  void changeMenuLabel(BuildContext context, int choiceMenuLabel) {
    currentMenuLabel = choiceMenuLabel;
    // 菜单在底部
    if (toolbarHeight<=0) {
      isLockKeyboardBottom = selectedMenuLabel= true;
      slideinMenubar(10);
    }
    // 菜单已弹起
    else {
      isLockKeyboardBottom = true;
      selectedMenuLabel = false;
      SystemChannels.textInput.invokeMethod<void>(
        keyboardHeight<=0 ? 'TextInput.show' : 'TextInput.hide'
      );
    }
    setStatus();
  }

  // 切换 返回
  void onPopInvoked<T>(bool didPop, T? result) {
    // 键盘&菜单在底部
    if (toolbarHeight<=0) {
      if (!didPop) {
        saveThenExit();
      }
    }
    // 键盘在底部, 菜单弹起
    else if(keyboardHeight<=0) {
      isLockKeyboardBottom = selectedMenuLabel = false;
      setStatus();
      slideoutMenubar(10);
    }
    // 键盘弹起, 菜单弹起
    else {
      isLockKeyboardBottom = false;
      SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
    }
  }

  Duration? frameCallTime;

  double lastKeyboardHeight = 0;

  BuildContext? editContext;

  void frameCallback(Duration callTime) {
    if (editContext==null || (frameCallTime!=null && frameCallTime!.compareTo(callTime)==0)) {
      return;
    }
    keyboardHeight = MediaQuery.of(editContext!).viewInsets.bottom;
    // show begin
    if (keyboardHeight > lastKeyboardHeight && lastKeyboardHeight <= 0) {
      keyboardStatus = KeyboardStatus.showing;
      selectedMenuLabel = false;
      Future.delayed(const Duration(milliseconds: 270), (){
        keyboardStatus = KeyboardStatus.show;
        isLockKeyboardBottom = selectedMenuLabel = false;
        toolbarHeight = keyboardHeight;
        cacheMaxToolbarHeight(keyboardHeight);
        setStatus();
      });
    }
    // hide end
    else if (keyboardHeight <= 0 && keyboardHeight < lastKeyboardHeight) {
      keyboardStatus = KeyboardStatus.hide;
      // 键盘在底部,说明是为了展示子菜单
      selectedMenuLabel = isLockKeyboardBottom;
    }
    // 剩下的就是 hiding 和 sliding ( 软键盘切换形态使得高度变化 ) 了
    else {
      keyboardStatus = KeyboardStatus.sliding;
    }
    if (isLockKeyboardBottom==false || toolbarHeight<keyboardHeight) {
      toolbarHeight = keyboardHeight;
    }
    lastKeyboardHeight = keyboardHeight;
    frameCallTime = callTime;
    setStatus();
  }