Flutter技术问题:如何在ChangeNotifier类中传递Context
解决方案
方案一:将点击事件移至UI层处理(推荐)
状态管理类的核心职责是维护数据,不应直接处理UI交互逻辑。正确的做法是在地图组件中监听Circle的点击事件,在UI层调用BottomSheet——因为UI层可以直接获取BuildContext。
修改步骤:
- 更新GoogleMapModel:移除Circle的
onTap回调,只保留数据逻辑:
void EarthQuakeItems() async { _circleItems.clear(); _markerItems.clear(); _sheetItems.clear(); try { List<EarthQuake> value = await client.getEarthQuakeRecent(); if (value.isNotEmpty) { for (int i = 0; i < value.length; i++) { _circleItems.add(Circle( circleId: CircleId(value[i].id.toString()), center: LatLng(value[i].latitude, value[i].longitude), fillColor: Colors.blue.withOpacity(0.5), radius: value[i].magnitude * 10000, strokeColor: Colors.blueAccent, strokeWidth: 1, consumeTapEvents: true, // 保留此属性确保Circle优先接收点击 )); } } } catch (error) { // 错误处理逻辑 } notifyListeners(); }
- 在Google_Map组件中监听点击:
假设你的Google_Map是基于官方GoogleMap封装的,添加onCircleTap回调:
class Google_Map extends StatelessWidget { final List<Circle> circleItems; final List<ClusterData> markerItems; final GoogleMapMode mode; const Google_Map({ super.key, required this.circleItems, required this.markerItems, required this.mode, }); @override Widget build(BuildContext context) { return GoogleMap( circles: Set<Circle>.from(circleItems), markers: // 你的Marker处理逻辑 onCircleTap: (CircleId circleId) { final earthQuakeId = circleId.value; BottomSheets.showItemBottomSheet(context, earthQuakeId); }, // 其他地图配置项 ); } }
方案二:在Model中维护点击回调函数
如果需要在Model中触发点击逻辑,可以在GoogleMapModel中定义一个回调字段,由UI层传入带Context的实现。
修改步骤:
- 更新GoogleMapModel:添加回调字段并在Circle点击时调用:
class GoogleMapModel with ChangeNotifier { // 新增点击回调字段 Function(String)? onCircleTap; // ... 现有代码 void EarthQuakeItems() async { _circleItems.clear(); _markerItems.clear(); _sheetItems.clear(); try { List<EarthQuake> value = await client.getEarthQuakeRecent(); if (value.isNotEmpty) { for (int i = 0; i < value.length; i++) { _circleItems.add(Circle( circleId: CircleId(value[i].id.toString()), center: LatLng(value[i].latitude, value[i].longitude), fillColor: Colors.blue.withOpacity(0.5), radius: value[i].magnitude * 10000, strokeColor: Colors.blueAccent, strokeWidth: 1, onTap: () { onCircleTap?.call(value[i].id.toString()); }, consumeTapEvents: true, )); } } } catch (error) { // 错误处理逻辑 } notifyListeners(); } }
- 在UI层绑定回调:在
EQ_Info的initState中设置回调:
class _EQ_InfoState extends State<EQ_Info> { @override void initState() { super.initState(); final mapModel = context.read<GoogleMapModel>(); mapModel.onCircleTap = (earthQuakeId) { BottomSheets.showItemBottomSheet(context, earthQuakeId); }; } // ... 现有代码 }
方案三:正确使用GlobalKey获取Context
若坚持使用GlobalKey,需确保其正确关联到MaterialApp的Navigator:
修改步骤:
- 定义全局GlobalKey:在
main.dart中声明:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); void main() async { WidgetsFlutterBinding.ensureInitialized(); // ... 其他初始化逻辑 runApp(MaterialApp( navigatorKey: navigatorKey, // 关联key到Navigator home: RootScreen(), )); }
- 在GoogleMapModel中使用GlobalKey:
// 导入main.dart中的navigatorKey import 'main.dart'; class GoogleMapModel with ChangeNotifier { // ... 现有代码 void EarthQuakeItems() async { // ... 循环创建Circle onTap: () { final context = navigatorKey.currentState?.context; if (context != null) { BottomSheets.showItemBottomSheet(context, value[i].id.toString()); } }, // ... 其他Circle属性 } }
其他显示容器的实现方式
如果不想使用showBottomSheet,还可以通过以下方式实现点击Circle显示容器:
1. 自定义可动画底部面板(Stack + AnimatedPositioned)
在EQ_Info的Stack中添加一个可动画的容器,通过Model维护选中的地震ID,控制容器的显示/隐藏:
// 在GoogleMapModel中新增状态字段 String? _selectedEarthQuakeId; String? get selectedEarthQuakeId => _selectedEarthQuakeId; void setSelectedEarthQuakeId(String? id) { _selectedEarthQuakeId = id; notifyListeners(); } // 在EQ_Info的Stack中添加: AnimatedPositioned( duration: const Duration(milliseconds: 300), bottom: context.watch<GoogleMapModel>().selectedEarthQuakeId != null ? 0 : -300, left: 0, right: 0, height: 300, child: Container( color: Colors.white, padding: const EdgeInsets.all(16), child: // 渲染选中的地震信息 ), ) // 在地图的onCircleTap中触发状态变更: onCircleTap: (CircleId circleId) { context.read<GoogleMapModel>().setSelectedEarthQuakeId(circleId.value); }
2. 使用DraggableScrollableSheet
通过Model控制可拖拽底部面板的显示状态:
// 在GoogleMapModel中新增状态字段 bool _showSheet = false; bool get showSheet => _showSheet; String? _selectedEarthQuakeId; String? get selectedEarthQuakeId => _selectedEarthQuakeId; void toggleSheet(String? id) { _selectedEarthQuakeId = id; _showSheet = id != null; notifyListeners(); } // 在EQ_Info的Stack中添加: if (context.watch<GoogleMapModel>().showSheet) { DraggableScrollableSheet( initialChildSize: 0.3, maxChildSize: 0.8, minChildSize: 0.3, builder: (context, scrollController) { return Container( color: Colors.white, child: ListView( controller: scrollController, children: // 渲染地震信息列表 ), ); }, ); }
内容的提问来源于stack exchange,提问作者jungeyonoh
相关产品推荐
相关产品推荐

