Flutter中如何通过下拉选项选择多个房间组件并更新价格?
问题解决:多房间选择下拉菜单无法添加房间并更新价格
问题现象
尝试通过下拉菜单选择多个房间组件的数量,以此更新总价,但下拉选项变更时,无法将房间正确添加到列表中,功能失效。

现有代码分析
房间组件(包含Dropdown)代码:
Container( padding: EdgeInsets.symmetric( horizontal: ScreenUtils.horizontalSpace12!, vertical: ScreenUtils.verticalSpace12!), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Choose rooms", style: textThemes(context).headlineMedium!.copyWith( fontSize: ScreenUtils.fontSize18!, letterSpacing: -0.8, ), ), SizedBox( height: ScreenUtils.horizontalSpace8, ), CustomContainer.rectangle( color: MyColors.dropdownColor, borderColor: MyColors.greyDivider, padding: 0, radius: 4, widget: Consumer<RoomProvider>( builder: (context, value, child) { return Row( children: [ Expanded( child: Dropdown( hintText: "1 (NPR. ${roomlist.pricePerNight})", selectedValue: value.selectedRoomNo, optionList: value.roomNo .map((e) => DropdownMenuItem( child: Text( "$e (NPR. ${calculatePrice(e)})", style: textThemes(context) .bodyLarge! .copyWith( fontSize: ScreenUtils .fontSize16), ), value: e)) .toList(), onchanged: onchanged)), ], ); }), ), ], ), ),
调用房间组件及Dropdown逻辑代码:
Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Select your rooms", style: textThemes(context).displayMedium!.copyWith( fontSize: ScreenUtils.fontSize20!, letterSpacing: -0.8), ), SizedBox( height: ScreenUtils.verticalSpace16!, ), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Consumer<RoomProvider>(builder: (context, value, child) { List<Rooms> rooms = value.roomList; return Wrap( spacing: ScreenUtils.horizontalSpace8!, children: [ ...List.generate(rooms.length, (index) { if (value.isLoading) { return const Center(child: CircularProgressIndicator()); } else { if (rooms.isEmpty) { return const Text("data"); } if (index == rooms.length) { return SizedBox( width: ScreenUtils.horizontalSpace8, ); } return RoomComponent( onchanged: (val) { if (!rooms.contains(val)) { roomsList.add(rooms[index]); } print(rooms); print(rooms.length); // value.changeRoomNo(val); // var selected = val; // // sel.insert(index, selected!); // // print(sel); // sel.add(rooms[index]); // print(sel.length); // var price = sel // .map((e) => e.pricePerNight) // .reduce((value, element) => value! + element!); // print(price); // (sel.map((e) => {e.pricePerNight!})); // SelectedRoom selectedRoom = SelectedRoom(); // Provider.of<UIHelper>(context, listen: false) // .calculateTotal(price, int.parse(val!)); }, roomlist: value.roomList[index], ); } }) ], ); })), ], ),
核心问题点
- 下拉选择状态共享冲突:所有
RoomComponent共用value.selectedRoomNo全局状态,导致多个房间的下拉选项互相干扰,无法独立控制每个房间的选择数量。 - 房间存在判断逻辑错误:
rooms.contains(val)中,val是下拉选择的数字(如1、2),而rooms是Rooms对象列表,用数值匹配对象必然判断错误,无法正确过滤重复添加。 - 未正确更新状态触发UI刷新:
roomsList是局部变量,未通过RoomProvider管理状态,更新后无法通知UI重新渲染,导致列表和价格不更新。 - 添加房间逻辑不符合需求:直接添加
rooms[index]对象,未关联选择数量,无法正确统计每个房间的订购数和总价。
修复方案
1. 修改RoomProvider,支持多房间独立状态
在RoomProvider中用Map存储每个房间对应的选择数量,替代单个变量:
class RoomProvider extends ChangeNotifier { // 存储每个房间ID对应的选择数量 Map<String, int> selectedRoomCounts = {}; List<Rooms> roomList = []; bool isLoading = false; // 更新指定房间的选择数量 void updateRoomCount(String roomId, int count) { selectedRoomCounts[roomId] = count; notifyListeners(); // 通知UI刷新 } // 计算总价 int calculateTotalPrice() { int total = 0; for (var room in roomList) { int count = selectedRoomCounts[room.id] ?? 0; total += (room.pricePerNight ?? 0) * count; } return total; } // 其他原有逻辑... }
2. 修改RoomComponent,绑定单个房间的选择状态
每个RoomComponent根据当前房间ID获取对应选择数量,不再共享全局变量:
Container( padding: EdgeInsets.symmetric( horizontal: ScreenUtils.horizontalSpace12!, vertical: ScreenUtils.verticalSpace12!), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Choose rooms", style: textThemes(context).headlineMedium!.copyWith( fontSize: ScreenUtils.fontSize18!, letterSpacing: -0.8, ), ), SizedBox(height: ScreenUtils.horizontalSpace8), CustomContainer.rectangle( color: MyColors.dropdownColor, borderColor: MyColors.greyDivider, padding: 0, radius: 4, widget: Consumer<RoomProvider>( builder: (context, value, child) { // 获取当前房间的已选数量,默认1 int currentCount = value.selectedRoomCounts[roomlist.id] ?? 1; return Row( children: [ Expanded( child: Dropdown( hintText: "1 (NPR. ${roomlist.pricePerNight})", selectedValue: currentCount.toString(), optionList: value.roomNo .map((e) => DropdownMenuItem( child: Text( "$e (NPR. ${calculatePrice(e)})", style: textThemes(context) .bodyLarge! .copyWith(fontSize: ScreenUtils.fontSize16), ), value: e.toString(), )) .toList(), onchanged: (val) { if (val != null) { int count = int.parse(val); // 调用Provider更新当前房间的选择数量 value.updateRoomCount(roomlist.id, count); } }, ), ), ], ); }, ), ), ], ), );
3. 调整父组件,移除错误添加逻辑,直接通过Provider计算总价
不需要维护局部roomsList,直接用RoomProvider的calculateTotalPrice()获取总价:
Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Select your rooms", style: textThemes(context).displayMedium!.copyWith( fontSize: ScreenUtils.fontSize20!, letterSpacing: -0.8, ), ), SizedBox(height: ScreenUtils.verticalSpace16!), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Consumer<RoomProvider>( builder: (context, value, child) { List<Rooms> rooms = value.roomList; if (value.isLoading) { return const Center(child: CircularProgressIndicator()); } if (rooms.isEmpty) { return const Text("暂无房间数据"); } return Wrap( spacing: ScreenUtils.horizontalSpace8!, children: rooms.map((room) { return RoomComponent( roomlist: room, onchanged: (val) {}, ); }).toList(), ); }, ), ), // 显示总价 Consumer<RoomProvider>( builder: (context, value, child) { int total = value.calculateTotalPrice(); return Text( "总价: NPR. $total", style: textThemes(context).headlineMedium, ); }, ), ], );
关键优化点
- 每个房间的选择状态独立存储,避免下拉选项互相干扰
- 通过Provider统一管理状态,更新后自动触发UI刷新
- 总价计算逻辑封装在Provider中,保证数据一致性
- 移除错误的对象判断逻辑,直接基于房间ID关联选择数量
内容的提问来源于stack exchange,提问作者Rozan Shrestha
相关产品推荐
相关产品推荐

