首页 > 解决方案 > 如何关闭 Flutter 中的打开下拉菜单?

问题描述

我的屏幕上有一个下拉菜单,每次用户最小化应用程序时我都想关闭它(暂停状态)。我查看了 DropDownButton 小部件,它似乎使用 Navigator.push() 方法,所以如果我没记错的话,理想情况下应该使用 Navigator.pop() 将其关闭。

这是我已经尝试过的-

  1. 使用全局键获取下拉小部件的上下文并实现Navigator.pop(key.currentContext)内部didChangeAppLifecycleState()功能。
  2. 使用 focusNode 来实现.unfocus()从下拉列表中移除焦点。
  3. 维护一个我设置为on和on的isDropdownDialogOpen布尔值。然后很简单,如果它在应用程序最小化。但是当用户打开下拉菜单然后通过在下拉对话框外部点击将其关闭时,这种方法会失败。在这种情况下,我无法将布尔值设置为。trueonTap()falseonChange()pop()truefalse

我的要求是 - 每当用户最小化应用程序时,我必须关闭下拉菜单,如果它首先打开的话。

我经历了一堆 SO 问题和 GitHub 评论,它们甚至很相似,但找不到任何有用的东西。

松散代码 -

class Auth extends State<Authentication> with WidgetsBindingObserver {

    bool isDropdownOpen = false;
    GlobalKey _dropdownKey = GlobalKey();
    FocusNode _focusNode;

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

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  // To pop the open dropdown dialog whenever app is minimised (and opened back on again)
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        if(isDropdownOpen)
          pop();
          // Also tried pop(_dropdownKey.currentContext)
        break;
      case AppLifecycleState.paused:
        break;
      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.detached:
        break;
    }
  }


  build() {
    return Scaffold(
      // Dummy Dropdown button with random values
      body: DropdownButton(
        key: _dropdownKey,
        focusNode: _focusNode,
        value: "one",
        items: ["one", "two", "three", "four", "five"],
        //boolean values changing fine, but when user taps outside the dropdown dialog, dropdown is closed and neither of onChanged() and onTap() is called. Need a call back (or something similar) for this particular case.
        onChange(value) => isDropdownOpen = false;
        onTap() => isDropdownOpen = ! isDropdownOpen;
      )
    );
  }

}

对于您的参考 - https://github.com/flutter/flutter/issues/87989

标签: flutterdart

解决方案


这对你有用吗?

import 'package:flutter/material.dart';

void main() async {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Home(),
    );
  }
}

class Home extends StatefulWidget {
  const Home({Key key}) : super(key: key);

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  AppLifecycleState currentAppLifecycleState;
  bool isDropdownMenuShown = false;

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    setState(
      () => currentAppLifecycleState = state,
    );
  }

  String value;
  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (<AppLifecycleState>[
            AppLifecycleState.inactive,
            AppLifecycleState.detached,
            AppLifecycleState.paused,
          ].contains(currentAppLifecycleState) &&
          isDropdownMenuShown) {
        print('Closing dropdown');
        Navigator.pop(context);
      }
    });
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo'),
      ),
      body: Center(
        child: GestureDetector(
          onLongPressStart: (LongPressStartDetails details) async {
            final left = details.globalPosition.dx;
            final top = details.globalPosition.dy;

            setState(() => isDropdownMenuShown = true);
            final value = await showMenu<String>(
              context: context,
              position: RelativeRect.fromLTRB(left, top, left, top),
              items: <PopupMenuEntry<String>>[
                PopupMenuItem(
                  child: const Text('Option 1'),
                  value: 'Option 1',
                ),
                PopupMenuItem(
                  child: const Text('Option 2'),
                  value: 'Option 2',
                ),
                PopupMenuItem(
                  child: const Text('Option 3'),
                  value: 'Option 3',
                ),
              ],
            );

            setState(() => isDropdownMenuShown = false);

            if (value == null) return;

            print('You chose: $value');
          },
          child: InkWell(
            onTap: () {},
            child: Container(
              alignment: Alignment.center,
              child: Text(
                'Long press to show dropdown',
                textAlign: TextAlign.center,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

它是如何工作的?

didChangeAppLifecycleState我们使用该方法跟踪当前的应用程序状态。

每当显示下拉菜单时,isDropdownMenuShown变量就会设置为true. 每当下拉菜单关闭/退出时,变量都会设置为false.

我们添加一个后帧回调(在每次重建后调用),并检查应用程序是否进入后台以及是否显示下拉菜单。如果是,则关闭下拉菜单。


推荐阅读