当前位置: 首页 > news >正文

# Flutter实战:打造一个天气预报应用(附完整代码与界面图)

## 🌤️ 引言

在本篇文章中,我们将使用 **Flutter** 构建一个跨平台的 **天气预报应用**。它将通过调用免费的天气 API 获取实时天气数据,并以美观的卡片形式展示给用户。

我们将涵盖以下关键技术点:

- 网络请求(`http` 包)
- JSON 数据解析
- 异步编程(`Future` 与 `async/await`)
- 状态管理基础
- 自定义 UI 组件

最终效果如下:

![天气应用效果图](https://i.imgur.com/ZKfFwRr.png)

*图:运行在 Android 上的 Flutter 天气应用*

---

## 🔧 准备工作

### 1. 创建项目

```bash
flutter create weather_app
cd weather_app
```

### 2. 添加依赖

编辑 `pubspec.yaml` 文件,添加网络请求库:

```yaml
dependencies:
flutter:
sdk: flutter
http: ^0.16.0 # 用于发送HTTP请求
```

然后运行:

```bash
flutter pub get
```

### 3. 获取天气 API 密钥

我们使用 [OpenWeatherMap](https://openweathermap.org/api) 的免费 API。

注册后获取你的 `API Key`,例如:`your_api_key_here`

> 🔐 提示:不要将密钥硬编码在代码中!此处为教学简化处理。

---

## 🗺️ 定义城市与天气模型

创建文件 `lib/models/weather.dart`:

```dart
class Weather {
final String city;
final double temperature;
final String description;
final String iconCode;

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

// 工厂构造函数:从 JSON 创建对象
factory Weather.fromJson(Map<String, dynamic> json) {
return Weather(
city: json['name'],
temperature: json['main']['temp'].toDouble(),
description: json['weather'][0]['description'],
iconCode: json['weather'][0]['icon'],
);
}
}
```

---

## 🌐 调用天气 API

创建 `lib/services/weather_service.dart`:

```dart
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/weather.dart';

class WeatherService {
static const String baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
static const String apiKey = 'your_api_key_here'; // 替换为你自己的Key

Future<Weather> getWeather(String cityName) async {
final response = await http.get(Uri.parse(
'$baseUrl?q=$cityName&appid=$apiKey&units=metric&lang=zh_cn'));

if (response.statusCode == 200) {
final data = json.decode(response.body);
return Weather.fromJson(data);
} else {
throw Exception('Failed to load weather');
}
}
}
```

---

## 🎨 构建主页面 UI

替换 `lib/main.dart` 内容:

```dart
import 'package:flutter/material.dart';
import 'package:weather_app/models/weather.dart';
import 'package:weather_app/services/weather_service.dart';

void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 天气预报',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const WeatherPage(),
debugShowCheckedModeBanner: false,
);
}
}

class WeatherPage extends StatefulWidget {
const WeatherPage({super.key});

@override
State<WeatherPage> createState() => _WeatherPageState();
}

class _WeatherPageState extends State<WeatherPage> {
final TextEditingController _controller = TextEditingController();
Weather? _weather;
bool _loading = false;
final WeatherService _service = WeatherService();

@override
void dispose() {
_controller.dispose();
super.dispose();
}

Future<void> _fetchWeather() async {
if (_controller.text.isEmpty) return;

setState(() {
_loading = true;
});

try {
final weather = await _service.getWeather(_controller.text);
setState(() {
_weather = weather;
_loading = false;
});
} catch (e) {
setState(() {
_loading = false;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('错误: $e')),
);
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('🌤️ 天气预报'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
labelText: '输入城市名称',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _fetchWeather(),
),
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.search),
onPressed: _fetchWeather,
),
],
),
const SizedBox(height: 20),

// 加载状态
if (_loading)
const CircularProgressIndicator()
else if (_weather != null)
Expanded(
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Text(
'${_weather!.city}',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Image.network(
'https://openweathermap.org/img/wn/${_weather!.iconCode}@2x.png',
width: 100,
),
Text(
'${_weather!.temperature.toStringAsFixed(1)}°C',
style: const TextStyle(
fontSize: 40,
color: Colors.orange,
),
),
Text(
'${_weather!.description}'.toUpperCase(),
style: const TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
),
),
)
else
const Expanded(
child: Center(
child: Text(
'请输入城市名称查询天气',
style: TextStyle(fontSize: 18, color: Colors.grey),
),
),
),
],
),
),
);
}
}
```

---

## 🖼️ 运行效果展示

### 启动应用

```bash
flutter run
```

### 查询“北京”天气

![查询北京天气](https://i.imgur.com/9mXWYvP.png)

*图:成功显示北京当前天气*

### 错误提示示例

如果城市不存在,会弹出提示:

![错误提示](https://i.imgur.com/3QzRZbS.png)

---

## 🔍 技术亮点解析

| 特性 | 实现方式 |
|------|----------|
| **网络请求** | 使用 `http.get()` 获取 JSON 数据 |
| **JSON 解析** | `factory` 构造函数 + `Map` 提取字段 |
| **异步处理** | `FutureBuilder` 可替代手动状态管理 |
| **用户体验** | 显示加载动画和错误提示 |
| **图片加载** | 直接使用 `Image.network` 加载图标 |

---

## 🛡️ 安全建议(进阶)

虽然本例直接写入了 API Key,但在生产环境中应:

- 使用环境变量或配置文件
- 通过后端代理请求避免密钥泄露
- 使用 `dotenv` 包管理敏感信息

```yaml
# 添加到 pubspec.yaml
dev_dependencies:
dotenv: ^5.0.2
```

---

## 🚀 扩展功能建议

你可以继续优化这个应用:

✅ 添加定位功能(获取当前位置天气)
✅ 支持多城市收藏列表
✅ 展示未来几天预报(调用 forecast API)
✅ 深色模式切换
✅ 下拉刷新

---

## 📦 打包发布

构建 APK 发布到手机:

```bash
flutter build apk --release
```

生成 IPA(需 macOS):

```bash
flutter build ipa
```

---

## ✅ 总结

通过这个项目,你学会了如何:

- 使用 Flutter 构建真实世界的应用
- 调用外部 RESTful API
- 解析 JSON 并更新 UI
- 处理加载、成功、错误三种状态
- 设计简洁美观的界面

Flutter 让这一切变得简单而高效!

---

http://www.cnnetsun.cn/news/118724.html

相关文章:

  • 手机系统预装应用包彻底删除
  • EmotiVoice能否实现多人对话同步生成?技术可行性评估
  • FusionCompute 8.0 实验环境搭建:完整资源获取与部署指南
  • AI主播直播间搭建:EmotiVoice语音部分实现
  • 静态代码扫描服务 100分(python、java、c++、js、c
  • Directus周起始日难题:3步从周日切换到周一的技术解决方案
  • 33、网络管理与UUCP使用指南
  • C++核心语法复盘:数据结构编程的底层基石
  • 43、Exim邮件服务器配置与管理全解析
  • 48、互联网新闻服务器INN与NNTP的使用与配置指南
  • 我发现动态时间戳对齐破解跨境急诊预警延迟
  • 面试官:如何提升AIGC生成的可控性?
  • 如何在5分钟内用Mermaid语法轻松生成专业流程图?
  • 大型语言模型服务工具:让AI开发像喝柠檬水一样清爽 [特殊字符]
  • 如何快速掌握Mermaid在线编辑器:面向技术文档编写者的完整教程
  • WGPU性能调优实战:从卡顿到流畅的终极指南
  • 8、iOS 开发中的音频与视频处理
  • 18、构建社交增强现实应用:从坐标存储到社交上下文添加
  • HFT-Orderbook:高性能交易订单簿的终极解决方案
  • veScale:PyTorch原生大语言模型训练框架完整指南
  • Easy Effects终极音效配置指南:50+专业预设深度解析
  • 嵌入式Web服务器实战:STM32Cube与Mongoose完美融合
  • EmotiVoice语音抗噪能力测试:嘈杂环境可用性
  • 拒绝制造虚假情感依赖:产品设计准则
  • 推荐12个中英文降AIGC率工具,亲测有效!(含免费)
  • Taskflow:现代C++并行编程框架深度解析
  • Strapi无头CMS架构深度解析与现代化应用实践
  • 高效实现!分布式链路追踪:TraceIdFilter + MDC + Skywalking
  • EmotiVoice声音克隆功能实测:5秒样本还原度高达90%以上
  • AI服务热更新终极方案:零停机模型动态替换完整指南