# 从零开始构建一个天气应用:Flutter 实战教程
## 一、前言:为什么选择 Flutter 开发天气 App?
移动设备上的天气应用是用户每日必用的功能之一。它结构清晰(数据 + UI)、交互简单,非常适合作为 Flutter 的入门实战项目。
在本篇文章中,我们将使用 **Flutter + Dart + OpenWeatherMap API** 构建一个跨平台的天气应用,支持 Android、iOS 和 Web。
---
## 二、项目预览图
| 页面 | 截图 |
|------|------|
| 主界面 |  |
| 加载状态 |  |
| 错误提示 |  |
> 🔗 实际开发建议替换为真实截图。此处为占位图示意。
---
## 三、环境准备与依赖配置
### 1. 创建项目
```bash
flutter create weather_app
cd weather_app
```
### 2. 添加依赖(`pubspec.yaml`)
```yaml
dependencies:
flutter:
sdk: flutter
http: ^0.15.0 # 网络请求
intl: ^0.19.0 # 格式化时间、数字等
get: ^4.6.6 # 路由与状态管理(可选)
```
运行命令安装:
```bash
flutter pub get
```
---
## 四、获取免费天气 API 密钥
我们使用 [OpenWeatherMap](https://openweathermap.org/api) 提供的免费 API。
1. 注册账号:https://home.openweathermap.org/users/sign_up
2. 获取 API Key(形如 `abc123xyz`)
3. 使用城市天气接口:
```
https://api.openweathermap.org/data/2.5/weather?q=Beijing&appid=YOUR_API_KEY&units=metric&lang=zh_cn
```
参数说明:
- `q=Beijing`:城市名
- `units=metric`:摄氏度
- `lang=zh_cn`:中文语言
---
## 五、定义数据模型(Model)
创建文件 `lib/model/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'],
);
}
}
```
---
## 六、网络请求服务(Service)
创建 `lib/service/weather_service.dart`
```dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../model/weather.dart';
class WeatherService {
final String apiKey = 'YOUR_API_KEY_HERE'; // 替换为你自己的 key
Future<Weather?> getWeather(String cityName) async {
try {
final response = await http.get(Uri.parse(
'https://api.openweathermap.org/data/2.5/weather?q=$cityName&appid=$apiKey&units=metric&lang=zh_cn',
));
if (response.statusCode == 200) {
final data = json.decode(response.body);
return Weather.fromJson(data);
} else {
print('Error: ${response.statusCode}');
return null;
}
} catch (e) {
print('Network Error: $e');
return null;
}
}
}
```
---
## 七、构建 UI 界面
修改 `lib/main.dart`
```dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../service/weather_service.dart';
import '../model/weather.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(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.lightBlue),
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 WeatherService service = WeatherService();
Weather? _weather;
bool _loading = false;
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _fetchWeather() async {
final city = _controller.text.trim();
if (city.isEmpty) return;
setState(() {
_loading = true;
_weather = null;
});
final result = await service.getWeather(city);
setState(() {
_loading = false;
_weather = result;
});
}
@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(
hintText: "输入城市名称,如:北京",
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)
_buildWeatherCard(_weather!)
else if (!_loading && _weather == null)
const Text("暂无数据,请检查城市名或网络", style: TextStyle(color: Colors.grey))
],
),
),
);
}
Widget _buildWeatherCard(Weather weather) {
final temp = weather.temperature.toInt();
final iconUrl = 'https://openweathermap.org/img/wn/${weather.iconCode}@2x.png';
final now = DateFormat.Hm().format(DateTime.now());
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Text(weather.city, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text("$now 更新", style: const TextStyle(color: Colors.grey)),
const SizedBox(height: 16),
Image.network(iconUrl),
Text("$temp°C", style: const TextStyle(fontSize: 40, fontWeight: FontWeight.w300)),
Text(weather.description.toUpperCase(), style: const TextStyle(fontSize: 16)),
],
),
),
);
}
}
```
---
## 八、代码结构解析
```
/lib
├── main.dart # 入口 & 页面逻辑
├── model/
│ └── weather.dart # 数据模型
└── service/
└── weather_service.dart # 网络请求
```
这种分层结构让项目更易于维护和扩展。
---
## 九、运行项目
```bash
# 运行在安卓模拟器
flutter run
# 运行在 iOS 模拟器
flutter run -d iPhone
# 运行在 Chrome 浏览器(Web 版)
flutter run -d chrome
```
✅ 成功运行后,输入“上海”、“纽约”,即可看到实时天气!
---
## 十、优化建议(进阶方向)
| 功能 | 实现方式 |
|------|---------|
| 自动定位 | 使用 `geolocator` 插件获取 GPS |
| 多城市收藏 | `shared_preferences` 存储历史记录 |
| 更多天气信息 | 显示湿度、风速、气压 |
| 状态管理 | 使用 `Provider` 或 `Riverpod` 管理全局状态 |
| 主题切换 | 支持深色/浅色模式 |
---
## 十一、打包发布
### Android
```bash
flutter build apk --release
```
生成路径:`build/app/outputs/flutter-apk/app-release.apk`
### Web
```bash
flutter build web
```
将 `build/web` 文件夹部署到 GitHub Pages、Vercel 或 Netlify。
---
## 十二、总结
通过这个天气 App,你已经掌握了 Flutter 的核心技能:
- ✅ 组件布局(Scaffold、Column、Row、Card)
- ✅ 网络请求(http + JSON 解析)
- ✅ 异步编程(Future)
- ✅ 表单输入与事件处理
- ✅ 第三方 API 集成
> 🎉 **恭喜你完成了第一个完整的 Flutter 应用!**
---
## 十三、常见问题 FAQ
❓ **Q:API 请求失败怎么办?**
👉 检查 API Key 是否正确,是否替换了 `YOUR_API_KEY_HERE`。
❓ **Q:图标不显示?**
👉 确保 `AndroidManifest.xml` 中添加了网络权限:
```xml
<uses-permission android:name="android.permission.INTERNET"/>
```
❓ **Q:如何支持 HTTPS 图片?**
👉 所有现代 Flutter 项目默认支持 HTTPS,无需额外配置。
---
## 十四、结语
Flutter 正在改变跨平台开发的格局。它不仅高效、美观,还能让你用一套代码覆盖多个平台。
下一步你可以尝试:
- 增加未来 7 天预报(调用 `forecast` 接口)
- 使用地图选择城市
- 添加动画过渡效果
更多推荐

所有评论(0)