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

Flutter从JSON加载Marker到GoogleMap失败,报build返回空异常

解决Flutter GoogleMap标记不显示及Build返回Null的问题

我来帮你排查下问题所在,你遇到的两个问题其实是相关的,咱们一步步解决:

问题1:Build函数返回Null的异常

你的FutureBuilder的builder方法只做了JSON解析和标记列表赋值,没有返回任何Widget,这就违反了Flutter的Build规则——builder必须返回一个Widget,所以控制台抛出了A build function returned null的异常。

问题2:标记不显示

就算没有这个异常,你的标记也不会显示,因为解析完JSON生成标记列表后,没有调用setState()通知Flutter框架更新UI,地图组件不会感知到allMarkers的变化,自然不会渲染新标记。

另外,你还没处理Future的不同状态(加载中、错误、数据就绪),这会导致在数据还没加载完成时就尝试解析snapshot.data,可能引发空指针问题。


修改后的完整代码

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String>? _future;
  Future<String> loadString() async => await rootBundle.loadString('assets/data.json');
  List<Marker> allMarkers = [];
  GoogleMapController? _controller;

  @override
  void initState() {
    super.initState();
    _future = loadString();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: Stack(
          children: <Widget>[
            GoogleMap(
              initialCameraPosition: const CameraPosition(target: LatLng(40.7128, -74.0060), zoom: 1.0),
              markers: Set.from(allMarkers),
              onMapCreated: mapCreated,
            ),
            // 将FutureBuilder放在地图上层,不影响地图显示,同时处理数据
            FutureBuilder(
              future: _future,
              builder: (context, AsyncSnapshot<String> snapshot) {
                // 处理加载状态
                if (snapshot.connectionState == ConnectionState.waiting) {
                  return const Center(child: CircularProgressIndicator());
                }
                // 处理错误状态
                if (snapshot.hasError) {
                  return Center(child: Text('加载数据失败: ${snapshot.error}'));
                }
                // 数据就绪时解析并更新标记
                if (snapshot.hasData) {
                  List<dynamic> parsedJson = jsonDecode(snapshot.data!);
                  setState(() {
                    allMarkers = parsedJson.map((element) {
                      return Marker(
                        markerId: MarkerId(element['id']),
                        position: LatLng(element['x'], element['y']),
                      );
                    }).toList();
                  });
                  // 返回空Widget,因为我们不需要额外UI
                  return const SizedBox.shrink();
                }
                // 默认返回空Widget
                return const SizedBox.shrink();
              },
            ),
          ],
        ),
      ),
    );
  }

  void mapCreated(GoogleMapController controller) {
    setState(() {
      _controller = controller;
    });
  }
}

关键修改点

  • 修复FutureBuilder的返回值:在builder的所有分支都返回了合法的Widget(加载指示器、错误文本、空Widget),解决了返回Null的异常。
  • 添加setState更新标记:解析完JSON后调用setState(),让Flutter重新渲染地图,加载新的标记。
  • 完善Future状态处理:增加了加载中、错误状态的UI提示,避免数据未就绪时的异常。
  • 调整组件层级:把GoogleMap放在底层,FutureBuilder放在上层,不影响地图显示逻辑。

另外,别忘了确认你的pubspec.yaml已经正确配置了assets路径:

assets:
  - assets/data.json

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:02:49