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

如何根据屏幕键盘高度调整Flutter DropdownMenu的高度?

解决DropdownMenu高度适配键盘显示/隐藏的问题

问题根源

  1. InkWell的onTap不触发:TextField本身会处理点击事件,外层InkWell的点击事件被拦截,导致你的print语句无法执行,也无法更新键盘高度。
  2. 键盘高度获取时机错误:点击TextField时键盘还未弹出,此时MediaQuery.of(context).viewInsets.bottom仍为0,无法拿到正确的键盘高度。
  3. 未实时监听键盘状态变化:键盘弹出/收起是异步过程,需要监听MediaQuery的变化来实时更新DropdownMenu高度。

解决方案

通过监听键盘状态变化,实时计算DropdownMenu高度,以下是修改后的完整代码:

import 'dart:ui';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

const List<String> list = <String>[
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
  "J",
];

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey? _key01 = GlobalKey();
  final GlobalKey? _key02 = GlobalKey();

  late final TextEditingController firstController;

  late Size screenSize;
  double screenWidth = 0;
  double screenHeight = 0;

  double heightAppBar = 0;
  double heightTextField = 0;
  double heightOthersCummulative = 0;
  double heightAdjustment = 60;
  double heightDropdownMenu = 150; // 默认高度

  late String dropdownValue = list.first;

  String result = "";
  String effective_available_screen_height = "";
  String dimensions = "";

  FlutterView view = WidgetsBinding.instance.platformDispatcher.views.first;

  late Size size;
  double width = 0;
  double height = 0;
  double height01 = 0;

  @override
  void initState() {
    super.initState();
    firstController = TextEditingController();

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      setState(() {
        heightAppBar = _key01?.currentContext?.size?.height ?? 0;
        heightTextField = _key02?.currentContext?.size?.height ?? 0;
        updateDropdownHeight();
        dimensions =
            "KeyboardHeight=${MediaQuery.of(context).viewInsets.bottom}\nDropdownMenuHeight=${heightDropdownMenu}\nAppBarHeight=${heightAppBar}\nTextFieldHeight=${heightTextField}";
      });
    });

    // 监听每帧变化,实时更新键盘高度和下拉菜单高度
    WidgetsBinding.instance.addPersistentFrameCallback((_) {
      if (mounted) {
        setState(() {
          updateDropdownHeight();
        });
      }
    });
  }

  // 统一计算DropdownMenu高度
  void updateDropdownHeight() {
    final currentKeyboardHeight = MediaQuery.of(context).viewInsets.bottom;
    if (heightAppBar > 0 && heightTextField > 0) {
      heightDropdownMenu = screenHeight -
          heightAppBar -
          heightTextField -
          currentKeyboardHeight -
          heightOthersCummulative -
          heightAdjustment;
      // 确保高度不会为负数
      heightDropdownMenu = heightDropdownMenu.clamp(100, double.infinity);
    } else {
      heightDropdownMenu = 150;
    }
  }

  @override
  void dispose() {
    firstController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    screenSize = MediaQuery.of(context).size;
    screenWidth = screenSize.width;
    screenHeight = screenSize.height;

    Size ph_size = view.physicalSize;
    double ph_height = ph_size.height;

    size = MediaQuery.of(context).size;
    width = size.width;
    height = size.height;

    final padding = MediaQuery.of(context).viewPadding;
    height01 = height - padding.top - padding.bottom;

    effective_available_screen_height = "$height01";

    double keyboard_height = MediaQuery.of(context).viewInsets.bottom;

    return Scaffold(
      appBar: AppBar(
        key: _key01,
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.all(5.0),
        child: Column(
          children: [
            Container(
              child: Row(
                children: [
                  Expanded(
                    flex: 5,
                    child: Container(
                      padding: const EdgeInsets.all(5.0),
                      // 移除无效的InkWell
                      child: TextField(
                        key: _key02,
                        style: const TextStyle(
                          fontSize: 16,
                        ),
                        controller: firstController,
                        keyboardType: TextInputType.number,
                        onChanged: (value) {
                          setState(() {
                            result = value;
                          });
                        },
                      ),
                    ),
                  ),
                  Expanded(
                    flex: 5,
                    child: Container(
                      padding: const EdgeInsets.all(5.0),
                      child: DropdownMenu<String>(
                        menuHeight: heightDropdownMenu,
                        textStyle: const TextStyle(
                          fontSize: 16.0,
                        ),
                        initialSelection: dropdownValue,
                        onSelected: (String? value) {
                          if (value != null) {
                            setState(() {
                              dropdownValue = value;
                            });
                          }
                        },
                        dropdownMenuEntries:
                            list.map<DropdownMenuEntry<String>>((String value) {
                          return DropdownMenuEntry<String>(
                              value: value, label: value);
                        }).toList(),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            Text("Result = $result"),
            Text("Total physical height = $ph_height"),
            Text("Total logical height = $height"),
            Text("Onscreen Keyboard height = $keyboard_height"),
            Text(
                "Working Height Available (logical) = $effective_available_screen_height"),
            Text("Dimensions: $dimensions"),
          ],
        ),
      ),
    );
  }
}

关键修改说明

  1. 移除无用的InkWell:TextField的点击事件会优先响应,外层InkWell的onTap不会触发,直接删除即可。
  2. 添加键盘状态监听:通过addPersistentFrameCallback监听每帧变化,实时获取当前键盘高度并更新DropdownMenu高度。
  3. 统一高度计算逻辑:新增updateDropdownHeight方法集中处理高度计算,同时用clamp确保高度不会为负数。
  4. 初始化时获取控件真实高度:在addPostFrameCallback中获取AppBar和TextField的实际高度,保证计算基础准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 03:45:08