Flutter中MapScreen组件找不到Provider<Position> 求助
问题分析与修复方案
核心问题
你遇到的ProviderNotFoundException本质是上下文层级错误:你在_MapScreenState的context(对应MapScreen组件的上下文)中调用Provider.of<Position>(context),但这个上下文位于你创建的MultiProvider之上,无法访问到MultiProvider内部提供的Position实例。此外代码里还有几处Provider配置错误,进一步加剧了问题。
具体修复步骤
1. 修正FutureProvider的初始值
FutureProvider的initialData需要传入对应类型的同步值,而不是Future对象。你原来写的initialData: locatorService.getLocation()返回的是Future<Position>,和FutureProvider要求的Position?类型不匹配,改成:
FutureProvider<Position?>( create: (context) => locatorService.getLocation(), initialData: null, // 用null作为初始值,或者传入一个默认Position实例 )
2. 使用子上下文读取Provider
不要直接用State的context去读取MultiProvider内部的Provider,而是在MultiProvider的子树中使用新的上下文。可以通过Builder组件或者将逻辑内联到MaterialApp的home属性中,确保读取Provider的上下文处于MultiProvider之下。
3. 修复ProxyProvider的空安全逻辑
当position为null时,直接调用position!会触发空指针异常,需要先做null判断:
ProxyProvider<Position?, Future<List<Place>>>( update: (context, position, previousPlaces) { if (position == null) { return Future.value([]); // 位置为空时返回空列表 } return placesService.getPlaces(position.latitude, position.longitude); }, )
4. 简化嵌套的FutureProvider
你在原代码里嵌套的FutureProvider是冗余的,直接用FutureBuilder处理Future<List<Place>>即可,不需要额外包装。
修改后的完整代码
class MapScreen extends StatefulWidget { const MapScreen({Key? key}) : super(key: key); @override State<MapScreen> createState() => _MapScreenState(); } final locatorService = GeolocatorService(); final placesService = PlacesService(); class _MapScreenState extends State<MapScreen> { @override Widget build(BuildContext context) { return MultiProvider( providers: [ FutureProvider<Position?>( create: (context) => locatorService.getLocation(), initialData: null, ), ProxyProvider<Position?, Future<List<Place>>>( update: (context, position, previousPlaces) { if (position == null) { return Future.value([]); } return placesService.getPlaces(position.latitude, position.longitude); }, ) ], child: MaterialApp( home: Builder( // 使用Builder获取MultiProvider子树的上下文 builder: (innerContext) { return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: const Icon(Icons.arrow_back_ios_new_rounded)), title: const Text('Tìm nhà hàng'), elevation: 0, backgroundColor: Colors.purple, centerTitle: true, ), body: Consumer<Position?>( builder: (context, currentPosition, child) { if (currentPosition == null) { return const Center(child: CircularProgressIndicator()); } return Consumer<Future<List<Place>>>( builder: (context, placesFuture, child) { return FutureBuilder<List<Place>>( future: placesFuture, initialData: const [], builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } final places = snapshot.data ?? []; return Column( children: <Widget>[ SizedBox( height: MediaQuery.of(context).size.height / 3, width: MediaQuery.of(context).size.width, child: GoogleMap( initialCameraPosition: CameraPosition( target: LatLng(currentPosition.latitude, currentPosition.longitude), zoom: 16.0), zoomGesturesEnabled: true, ), ), const SizedBox(height: 10.0), Expanded( child: ListView.builder( itemCount: places.length, itemBuilder: (context, index) { return Card( child: ListTile( title: Text(places[index].name), ), ); }, ), ) ], ); }, ); }, ); }, ), ); }, ), ), ); } }
关键改动说明
- 用
Builder获取MultiProvider子树的上下文,确保能正确读取内部的Provider实例 - 替换了错误的
FutureProvider初始值 - 增加了
position为null时的安全处理 - 用
FutureBuilder直接处理Future<List<Place>>,去掉冗余的FutureProvider嵌套
内容的提问来源于stack exchange,提问作者Trung Phạm
相关产品推荐
相关产品推荐

