位置:首页 > Dart > Dart核心特性实战:天气API数据解析教程

Dart核心特性实战:天气API数据解析教程

时间:2026-08-21  |  作者:电竞小硕  |  阅读:0

上一节课我们学习了单元测试,掌握了通过全面测试保证代码质量的方法。

今天,我们将进入第 24 课 —— 核心特性实战:天气 API 数据解析

通过这个实战项目,我们会综合运用 Dart 的多个核心特性,包括 HTTP 请求、异步编程、JSON 解析和空安全处理,打造一个能获取并展示天气数据的小应用。

dart学习第 24 节:核心特性实战 —— 天气 API 数据解析

一、项目需求

目标很明确:创建一个 Dart 应用程序,用来获取并展示天气数据。

  • 调用公共 API:我们将使用 Open - Meteo API。它的优势在于,对于非商业用途,无需注册获取 API 密钥,可直接使用,能轻松获取天气数据。
  • 获取天气数据:我们希望检索特定位置的当前天气信息。位置可以通过地理坐标(纬度和经度)或者在某些情况下通过城市名称来指定。
  • 解析 JSON 数据:API 返回的数据采用 JSON 格式。我们需要将此 JSON 数据转换为 Dart 对象,以便在应用程序中方便地处理。
  • 展示天气信息:解析数据后,我们将以有意义的方式展示相关天气信息,如温度、天气描述、湿度等。

二、技术要点

1. Dart 中的 HTTP 请求

在 Dart 中,有多种发送 HTTP 请求的方式。

最常用的方式之一是使用http包。首先,我们需要在pubspec.yaml文件中添加它作为依赖:

dependencies:
  http: ^1.4.0

然后,运行dart pub get安装该包。

下面是一个向 Open - Meteo API 发送简单 GET 请求的示例。假设我们要获取某个具有特定坐标位置的天气。

import 'package:http/http.dart' as http;
import 'dart:convert';

Future<String> getWeatherData(double latitude, double longitude) async {
  final url = Uri.https('api.open-meteo.com', 'v1/forecast', {
    'latitude': latitude.toString(),
    'longitude': longitude.toString(),
    'current_weather': 'true',
  });
  final response = await http.get(url);
  if (response.statusCode == 200) {
    return response.body;
  } else {
    throw Exception('获取天气数据失败');
  }
}

这段代码的核心逻辑如下:

  • 导入http包和dart:convert库,后续解析 JSON 时会用到后者。
  • 定义了一个Future函数getWeatherDataFuture关键字表明此函数是异步的,会在未来某个时刻返回结果。
  • 使用Uri.https构建 API 请求的 URL。传入基础 URL、API 端点(这里是/v1/forecast)以及查询参数,如纬度、经度和是否获取当前天气的标识。
  • 使用http.get方法发送 GET 请求到 API。由于这是一个异步操作,使用await关键字。await会暂停函数的执行,直到http.get操作完成并返回一个response
  • 检查响应的statusCode是否为 200(表示请求成功)。如果是,返回响应体(即包含 JSON 格式天气数据的字符串)。否则,抛出一个Exception表示请求失败。

2. Future 与异步编程

在 Dart 中,异步操作不会立即完成。

例如,HTTP 请求需要通过网络与服务器通信,因此需要一些时间。Dart 使用Future来表示异步操作的结果,而不是在等待请求完成时阻塞程序执行。

在上述getWeatherData函数中,函数本身返回一个Future

这意味着函数的结果,也就是 JSON 格式的天气数据字符串,将在未来可用。

当在async函数内部使用await关键字时,我们是在告诉 Dart:等到Future完成后,再继续执行后续代码。

例如,在getWeatherData函数中,await http.get(url)语句会暂停函数,直到 HTTP 请求完成,然后将结果赋值给response变量。

如果不使用await,直接返回http.get(url)而不等待它,函数会立即返回一个未完成的Future

这不是我们想要的,因为我们需要实际的天气数据来进行后续的解析和展示。

下面再看一个示例,帮助理解Future的工作原理:

Future<int> delayAndReturn() async {
  await Future.delayed(Duration(seconds: 2));
  return 42;
}

void main() async {
  print('在调用delayAndReturn之前');
  int result = await delayAndReturn();
  print('结果是:$result');
  print('在调用delayAndReturn之后');
}

在这段代码中,delayAndReturn函数使用Future.delayed暂停 2 秒,然后返回值 42。

main函数中,我们先打印一条消息,再await delayAndReturn的结果。

等待结果期间,程序不会阻塞。如果还有其他代码,它们可以继续执行。

一旦delayAndReturn中的Future完成,我们打印结果,然后再打印另一条消息。

3. JSON 到模型的转换

从 API 获取到 JSON 格式的天气数据后,我们需要将其转换为 Dart 对象。

这样做之后,数据处理会更方便。

首先,定义一个 Dart 类来表示天气数据。

为简单起见,这里使用一个基本的Weather类,它包含温度和天气描述:

class Weather {
  final double temperature;
  final String description;

  Weather({required this.temperature, required this.description});

  factory Weather.fromJson(Map<String, dynamic> json) {
    double temp = json['current_weather']['temperature']  0.0;
    String desc = json['current_weather']['weathercode'] != null
         getWeatherDescription(json['current_weather']['weathercode'])
        : '未知';
    return Weather(temperature: temp, description: desc);
  }

  // 辅助函数,根据天气代码获取天气描述
  static String getWeatherDescription(int weathercode) {
    // 这里可以根据Open - Meteo的天气代码表进行映射
    // 例如:
    switch (weathercode) {
      case 0:
        return '晴朗';
      case 1:
        return '大部分晴朗';
      // 其他代码的映射...
      default:
        return '未知';
    }
  }
}

这个Weather类主要包含以下内容:

  • 定义了两个最终属性temperaturedescription,用于存储相关的天气信息。
  • 构造函数接受这两个属性作为必需参数。
  • fromJson工厂构造函数用于从 JSON 映射创建一个Weather对象。在 Open - Meteo API 响应中,温度位于current_weather对象内,天气代码也在current_weather中。我们使用空感知运算符()为可能缺失的字段提供默认值。对于天气描述,通过调用辅助函数getWeatherDescription,根据天气代码获取对应的文字描述。

现在,我们可以修改getWeatherData函数,使其返回一个Weather对象,而不是 JSON 字符串:

Future getWeatherData(double latitude, double longitude) async {
  final url = Uri.https('api.open - meteo.com', 'v1/forecast', {
    'latitude': latitude.toString(),
    'longitude': longitude.toString(),
    'current_weather': 'true',
  });
  final response = await http.get(url);
  if (response.statusCode == 200) {
    Map<String, dynamic> json = jsonDecode(response.body);
    return Weather.fromJson(json);
  } else {
    throw Exception('获取天气数据失败');
  }
}

这里,我们使用dart:convert库中的jsonDecode函数,将response.body中的 JSON 字符串转换为 Dart 的Map

然后把这个映射传递给Weather类的fromJson工厂构造函数,创建一个Weather对象。

三、空安全处理

在使用像 Open - Meteo 这样的外部 API 时,处理空值非常重要。

API 响应可能并不总是包含我们期望的所有数据,也可能由于网络问题导致返回空响应。

1. 网络数据的空值处理

getWeatherData函数中,我们已经有了一些基本的空安全处理。

当 HTTP 请求失败(statusCode不是 200)时,我们抛出一个Exception,而不是返回可能为空或不正确的结果。

不过,我们还可以添加更详细的错误处理。

例如,如果 API 返回的数据格式不正确,我们可以进一步处理。在fromJson工厂构造函数中,可以增加对数据完整性的检查:

factory Weather.fromJson(Map<String, dynamic> json) {
  if (!json.containsKey('current_weather')) {
    throw Exception('API返回的数据格式不正确,缺少current_weather字段');
  }
  double temp = json['current_weather']['temperature'] 0.0;
  String desc = json['current_weather']['weathercode']!= null
   getWeatherDescription(json['current_weather']['weathercode'])
      : '未知';
  return Weather(temperature: temp, description: desc);
}

在这个更新后的fromJson构造函数中,我们先检查 JSON 映射是否包含current_weather键。

如果不包含,说明数据格式有问题,此时抛出一个Exception

2. JSON 解析中的空感知运算符

正如在fromJson构造函数中所看到的那样,当我们从JSON映射中提取值时,使用了空感知运算符()。

比如说,double temp = json['current_weather']['temperature'] 0.0;

这样做的好处是,如果在current_weather对象中不存在temperature这个键,我们就会使用默认值0.0

这样不会得到一个可能导致运行时错误的空值。

类似地,对于天气描述,通过检查天气代码是否存在,来决定是获取描述还是使用默认值未知

这种方式可以有效避免因数据缺失而导致的程序崩溃。

四、整合代码

下面创建一个简单的基于控制台的 Dart 应用程序,展示这些概念如何协同工作。

import 'package:http/http.dart' as http;
import 'dart:convert';

class Weather {
  final double temperature;
  final String description;

  Weather({required this.temperature, required this.description});

  factory Weather.fromJson(Map<String, dynamic> json) {
    if (!json.containsKey('current_weather')) {
      throw Exception('API返回的数据格式不正确,缺少current_weather字段');
    }
    double temp = json['current_weather']['temperature']  0.0;
    String desc = json['current_weather']['weathercode'] != null
         getWeatherDescription(json['current_weather']['weathercode'])
        : '未知';
    return Weather(temperature: temp, description: desc);
  }

  static String getWeatherDescription(int weathercode) {
    switch (weathercode) {
      case 0:
        return '晴朗';
      case 1:
        return '大部分晴朗';
      // 其他代码的映射...
      default:
        return '未知';
    }
  }
}

Future getWeatherData(double latitude, double longitude) async {
  final url = Uri.https('api.open-meteo.com', 'v1/forecast', {
    'latitude': latitude.toString(),
    'longitude': longitude.toString(),
    'current_weather': 'true',
  });
  final response = await http.get(url);
  if (response.statusCode == 200) {
    Map<String, dynamic> json = jsonDecode(response.body);
    return Weather.fromJson(json);
  } else {
    throw Exception('获取天气数据失败');
  }
}

void main() async {
  double latitude = 34.0522; // 示例纬度,可替换为你需要的
  double longitude = -118.2437; // 示例经度,可替换为你需要的
  try {
    Weather weather = await getWeatherData(latitude, longitude);
    print('该位置的天气是:${weather.description}');
    print('温度是:${weather.temperature} °C');
  } catch (e) {
    print('错误:$e');
  }
}

在这个main函数中:

  • 我们首先定义了示例的纬度和经度。在实际应用中,你可以根据需要修改这些值,或者通过用户输入获取。
  • 使用try - catch块处理在 API 调用或 JSON 解析过程中可能出现的任何异常。
  • try块内,await getWeatherData函数的结果。如果操作成功,打印天气描述和温度。
  • 如果发生异常(如网络错误、API 返回的数据格式错误等),在catch块中打印错误消息。

五、进一步改进

1. 错误日志记录

目前,我们只是简单地打印错误消息。

在实际应用中,将错误记录到文件或日志服务中会更好。

这样,我们可以在以后查看错误,并更有效地进行调试。

例如,可以使用logging包来实现更高级的日志记录功能。

2. 用户输入功能

现在我们是硬编码了纬度和经度。

我们可以通过dart:io库实现用户输入功能,让用户输入城市名称或坐标。

然后根据用户输入来查询天气。例如:

import 'dart:io';

void main() async {
  stdout.write('请输入纬度:');
  String latitudeStr = stdin.readLineSync();
  stdout.write('请输入经度:');
  String longitudeStr = stdin.readLineSync();

  if (latitudeStr != null && longitudeStr != null) {
    double latitude = double.tryParse(latitudeStr)  0.0;
    double longitude = double.tryParse(longitudeStr)  0.0;
    try {
      Weather weather = await getWeatherData(latitude, longitude);
      print('该位置的天气是:${weather.description}');
      print('温度是:${weather.temperature} °C');
    } catch (e) {
      print('错误:$e');
    }
  } else {
    print('请输入有效的纬度和经度');
  }
}

3. 用户界面集成

若想让用户体验更为出色,不妨把天气数据检索功能融入到图形用户界面中。

要是采用Flutter框架,就能创建出一个精美的应用程序。

借助各种小部件,可以以一种有序且极具视觉吸引力的方式来展示天气信息。

  • 例如,运用ListView来呈现不同的天气信息字段。
  • 利用Image.network来显示与天气状况相匹配的图标等等。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多