Flutter中DropdownSearch展开时隐藏AppBar返回按钮的问题
解决方案:DropdownSearch展开时隐藏AppBar返回按钮
问题原因
你之前使用FocusNode的方案失效,核心问题有两个:
- 每次执行
_buildDropdownSearch都会重新创建FocusNode,导致监听逻辑重复绑定、状态混乱; - 你监听的是搜索框的焦点变化,而非下拉弹窗的实际展开/关闭状态,两者并不完全同步。
正确实现思路
DropdownSearch的PopupProps提供了专门监听弹窗展开和关闭的回调:onPopupOpened和onPopupClosed,用这两个回调控制返回按钮的显示/隐藏是最准确的方案。
修改步骤与代码示例
1. 优化Controller状态管理(可选但更符合GetX规范)
将showBackButton改为Rx变量,无需手动调用update(),GetX会自动响应状态变化:
class QuestionnaireController extends GetxController with GetSingleTickerProviderStateMixin { RxList<String> choices = <String>[].obs; RxBool showBackButton = true.obs; // 改为Rx布尔变量 }
如果不想修改变量类型,保持原bool showBackButton = true也可以,但需要在修改状态后手动调用controller.update()。
2. 配置DropdownSearch的弹窗回调
在PopupPropsMultiSelection.bottomSheet中添加弹窗展开/关闭的回调,直接修改Controller状态:
popupProps: PopupPropsMultiSelection.bottomSheet( // 保留原有配置... onPopupOpened: () { // 弹窗展开时隐藏返回按钮 controller.showBackButton.value = false; // 若使用普通bool变量,替换为: // controller.showBackButton = false; // controller.update(); }, onPopupClosed: () { // 弹窗关闭时显示返回按钮 controller.showBackButton.value = true; // 若使用普通bool变量,替换为: // controller.showBackButton = true; // controller.update(); }, // 保留原有配置... ),
3. 移除无用的FocusNode代码
删除_buildDropdownSearch中创建FocusNode、添加监听器的逻辑,以及搜索框的focusNode: _dropdownFocusNode配置。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:get/get.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return GetMaterialApp( title: 'MultiSelectDropDown', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'MultiSelectDropDown'), initialBinding: QuestionnaireBinding(), ); } } class QuestionnaireBinding extends Bindings { @override void dependencies() { Get.lazyPut<QuestionnaireController>(() => QuestionnaireController()); } } class QuestionnaireController extends GetxController with GetSingleTickerProviderStateMixin { RxList<String> choices = <String>[].obs; RxBool showBackButton = true.obs; } 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 dropdownSearchTheme = ThemeData( colorScheme: const ColorScheme( primary: Color(0xFF485946), onPrimary: Colors.white, secondary: Colors.black, onSecondary: Colors.deepOrange, error: Colors.transparent, onError: Colors.transparent, background: Colors.transparent, onBackground: Colors.black, brightness: Brightness.light, onSurface: Colors.black, surface: Colors.white, )); @override Widget build(BuildContext context) { return Obx(() { final controller = Get.find<QuestionnaireController>(); return Scaffold( appBar: AppBar( leading: controller.showBackButton.value ? BackButton() : null, backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ const Text( 'Let\'s start choosing', textAlign: TextAlign.center, ), const SizedBox(height: 30), Theme( data: dropdownSearchTheme, child: _buildDropdownSearch( context, controller.choices, dropdownCustomBuilder: _customChoice, ), ), ], ), ), ), ); }); } Widget _buildDropdownSearch( BuildContext context, RxList<String> selectedChoice, {Widget Function(BuildContext, List<String>)? dropdownCustomBuilder}) { final controller = Get.find<QuestionnaireController>(); return DropdownSearch<String>.multiSelection( selectedItems: selectedChoice, items: [ "choice1", "choice2", "choice3", "choice4", "choice5", "choice6", "choice7", "choice8", "choice9", "choice10", "choice11" ], dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( hintText: 'pickChoice', hintStyle: TextStyle(color: Colors.black), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), borderSide: BorderSide(color: Colors.grey), ), )), dropdownBuilder: dropdownCustomBuilder, popupProps: PopupPropsMultiSelection.bottomSheet( searchFieldProps: TextFieldProps( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20), decoration: InputDecoration( hintText: 'Search for choice', hintStyle: TextStyle(color: Colors.black), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), borderSide: BorderSide(color: Colors.grey), ), suffixIcon: IconButton( icon: Icon(Icons.clear), onPressed: () => controller.choices.clear(), ), ), style: TextStyle(color: Colors.black), autofocus: true, ), showSearchBox: true, showSelectedItems: true, isFilterOnline: false, onItemAdded: (selectedItems, addedItem) => selectedChoice.add(addedItem), onItemRemoved: (selectedItems, removedItem) => selectedChoice.remove(removedItem), onPopupOpened: () => controller.showBackButton.value = false, onPopupClosed: () => controller.showBackButton.value = true, ), ); } Widget _customChoice(BuildContext context, List<String> selectedItems) { final controller = Get.find<QuestionnaireController>(); return Obx(() { if (selectedItems.isEmpty) return Text("Pick a choice"); return Wrap( spacing: 2, runSpacing: -10, children: selectedItems.map((e) { return Chip( backgroundColor: Colors.lightGreen, label: Text(e.toString().trim()), onDeleted: () { selectedItems.remove(e.toString().trim()); controller.choices.remove(e.toString().trim()); }, ); }).toList(), ); }); } }
说明
onPopupOpened和onPopupClosed能精准监听弹窗的展开/关闭状态,比FocusNode方案更可靠;- 使用Rx变量+
Obx可以自动响应状态变化,无需手动调用update(),代码更简洁; - 若坚持使用普通
bool变量,只需在回调中修改状态后调用controller.update()即可。
内容的提问来源于stack exchange,提问作者Gladiator
相关产品推荐
相关产品推荐

