Flutter开发天气应用报latitude字段LateInitializationError未初始化错误
Flutter 天气应用LateInitializationError 字段未初始化问题解决
报错信息
LateError (LateInitializationError: Field 'latitude' has not been initialized.)
问题复现代码
import 'package:flutter/material.dart'; import 'package:climate/services/location.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; const apiKey = '78c0a5319f932d3e171aa34ab51dd7e3'; class LoadingScreen extends StatefulWidget { @override _LoadingScreenState createState() => _LoadingScreenState(); } class _LoadingScreenState extends State<LoadingScreen> { late double latitude; late double longitude; @override void initState() { super.initState(); getLocation(); } void getLocation() async { Location location = Location(); await location.getCurrentLocation(); latitude = location.latitude; longitude = location.longitude; } void getData() async { http.Response reponse = await http.get(Uri.parse( "https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$apiKey")); if (reponse.statusCode == 200) { String data = reponse.body; int condition = jsonDecode(data)['weather'][0]['id']; print(condition); double temp = jsonDecode(data)['main']['temp']; //main.temp print(temp); String city = jsonDecode(data)['name']; //name print(city); } else { print(reponse.statusCode); } print(reponse.body); } @override Widget build(BuildContext context) { getData(); return Scaffold(); } }
问题原因
late关键字的作用是承诺非空字段会在首次被访问前完成赋值,不会在类实例创建时强制要求初始化,但如果访问时还没赋值就会抛出LateInitializationError。- 代码执行时序错误:
initState中调用的getLocation()是异步方法,内部await getCurrentLocation()需要等待系统定位返回才会给latitude、longitude赋值,但build方法会在initState执行结束后立刻触发,此时定位还未返回,build中直接调用getData()访问两个未赋值的late字段,直接触发报错。 - 逻辑位置错误:
build方法会在组件生命周期内被多次触发(状态更新、父组件重绘、键盘弹出等场景),将网络请求getData()写在build中会导致重复发起无效请求。 - 移除
late触发编译错误是Dart空安全机制的强制要求:非空类型的类字段必须在声明时、构造函数初始化列表中完成赋值,既不初始化、也不用late标记延迟初始化,就会直接编译失败。
修复方案
核心原则:所有副作用逻辑(定位、网络请求)放在initState或事件回调中,不要放在build方法里;严格保证访问字段前异步赋值逻辑已经执行完成,用加载态做过渡。
修复后可运行代码:
import 'package:flutter/material.dart'; import 'package:climate/services/location.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; const apiKey = '78c0a5319f932d3e171aa34ab51dd7e3'; class LoadingScreen extends StatefulWidget { @override _LoadingScreenState createState() => _LoadingScreenState(); } class _LoadingScreenState extends State<LoadingScreen> { bool isLoading = true; // 用可空类型承接未赋值状态,比late更安全,空指针风险会被编译器提示 double? latitude; double? longitude; dynamic weatherData; @override void initState() { super.initState(); // 初始化时串行执行定位、拉取天气逻辑 fetchLocationAndWeather(); } Future<void> fetchLocationAndWeather() async { try { Location location = Location(); await location.getCurrentLocation(); latitude = location.latitude; longitude = location.longitude; // 拿到经纬度后再发起天气请求,此时字段必然已经赋值 await getWeatherData(); } catch (e) { print('获取数据异常: $e'); } finally { // 组件未销毁时才更新状态 if (mounted) { setState(() { isLoading = false; }); } } } Future<void> getWeatherData() async { // 空值兜底,避免异常 if (latitude == null || longitude == null) return; // 修正原代码拼写错误:reponse -> response http.Response response = await http.get(Uri.parse( "https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$apiKey")); if (response.statusCode == 200) { String data = response.body; weatherData = jsonDecode(data); int condition = weatherData['weather'][0]['id']; print(condition); double temp = weatherData['main']['temp']; print(temp); String city = weatherData['name']; print(city); } else { print('请求失败,状态码: ${response.statusCode}'); } print(response.body); } @override Widget build(BuildContext context) { // build方法仅做UI渲染,根据加载状态返回不同组件 return Scaffold( body: Center( child: isLoading ? const CircularProgressIndicator() : const Text('天气数据加载完成'), ), ); } }
额外优化说明
- 用可空类型
double?替代late double,编译器会自动提示空判断逻辑,比late的运行时报错更安全。 - 增加加载状态,定位、请求过程中显示加载指示器,避免空白页。
- 增加异常捕获和空值兜底,避免定位权限拒绝、网络异常时直接崩溃。
- 异步更新状态前判断
mounted属性,避免组件销毁后调用setState触发内存泄漏和报错。
内容的提问来源于stack exchange,提问作者Parv Vijay
相关产品推荐
相关产品推荐

