You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Place列表无法打印展示及Provider相关问题求助

解决Flutter中Place列表无法展示的问题

问题详情

调试确认数据已成功映射为Place列表,但界面始终显示加载指示器。添加return语句后,出现新错误:找不到此Consumer<List> Widget上方的正确Provider<List>。

原界面代码

class MapScreen extends StatelessWidget {
  const MapScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        FutureProvider(
          create: (context) => GeolocatorService().getLocation(),
          initialData: null,
        ),
        ProxyProvider<Position, Future<List<Place>>>(
          update: (context, position, places) {
            return PlacesService()
                .getPlaces(position.latitude, position.longitude);
          },
        )
      ],
      child: const MaterialApp(
        home: GetScreen(),
      ),
    );
  }
}

class GetScreen extends StatelessWidget {
  const GetScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final currentPosition = Provider.of<Position?>(context);
    if (currentPosition != null) {
      final placeService = Provider.of<Future<List<Place>>>(context);
      FutureProvider(
        create: (context) => placeService,
        initialData: const [],
        child: 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<List<Place>>(
              builder: (context, places, child) {
                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,
                                  ),
                                ),
                              );
                            }))
                  ],
                );
              },
            )),
      );
    }
    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: const Center(child: CircularProgressIndicator()));
  }
}

数据获取与映射代码

class PlacesService {
  final key = 'AIzaSyDM32Kzq-Pz_MPorUAYxWgDX7qb3nt2S6M';

  Future<List<Place>> getPlaces(double lat, double lng) async {
    var response = await http.get(Uri.parse(
        'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=$lat,$lng&type=restaurant&rankby=distance&key=$key '));
    var json = convert.jsonDecode(response.body);
    var jsonResults = json['results'] as List;
    return jsonResults.map((place) => Place.fromJson(place)).toList();
  }
}

class Place {
  final Geometry geometry;
  final String name;
  final double rating;
  final int userRatingCount;
  final String vicinity;

  Place(this.geometry, this.name, this.rating, this.userRatingCount,
      this.vicinity);

  Place.fromJson(Map<dynamic, dynamic> parsedJson)
      : geometry = Geometry.fromJson(parsedJson['geometry']),
        name = parsedJson['name'],
        rating = (parsedJson['rating'] != null)
            ? (parsedJson['rating'])!.toDouble()
            : 0.0,
        userRatingCount = (parsedJson['user_ratings_total'] != null)
            ? parsedJson['user_ratings_total']
            : 0,
        vicinity = parsedJson['vicinity'];
}

问题原因与修复方案

1. 未返回核心Widget(初始加载问题)

在GetScreen的build方法中,当currentPosition != null时,创建了FutureProvider但未return该Widget,导致代码直接走到最后返回加载界面,这是界面一直显示CircularProgressIndicator的根本原因。

2. Provider层级与监听错误(添加return后的新问题)

当前Provider提供的是Future<List<Place>>,但你试图用Consumer<List<Place>>直接监听List,嵌套FutureProvider的方式会导致层级混乱,无法被正确识别。

推荐使用FutureBuilder替代嵌套Provider,直接处理异步状态:

修改后的GetScreen代码:

class GetScreen extends StatelessWidget {
  const GetScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final currentPosition = Provider.of<Position?>(context);
    if (currentPosition != null) {
      final futurePlaces = Provider.of<Future<List<Place>>>(context);
      // 此处添加return返回核心界面
      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: FutureBuilder<List<Place>>(
          future: futurePlaces,
          builder: (context, snapshot) {
            // 根据异步状态处理UI
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            } else if (snapshot.hasError) {
              return Center(child: Text('加载失败: ${snapshot.error}'));
            } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
              return const Center(child: Text('未找到餐厅'));
            } else {
              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),
                              ),
                            );
                          }))
                ],
              );
            }
          },
        ),
      );
    }
    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: const Center(child: CircularProgressIndicator()));
  }
}

额外优化建议

  • 避免在build方法中直接创建PlacesService()实例,可通过Provider注入,提升代码可测试性。
  • 检查Google Places API的请求URL末尾是否有多余空格(原代码中key后有空格),可能导致请求失败。

内容的提问来源于stack exchange,提问作者Trung Phạm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:54:36