深入 Flutter 开发:构建一个带网络请求的天气应用(含完整代码)
技术点说明用于需要动态更新的界面(如加载、数据变化)http.get()发送网络请求获取天气数据Future与异步处理网络调用setState()触发 UI 重绘用户输入处理JSON 解析使用factory构造函数映射 API 响应错误处理try-catch 捕获异常并友好提示UI 构建 → 网络请求 → 数据解析 → 状态更新 → 用户交互Flutter 不仅让跨平台开发变得简单高效,更以接近原
标题:深入 Flutter 开发:构建一个带网络请求的天气应用(含完整代码)
引言
在移动开发领域,Flutter 凭借其高性能、跨平台能力和出色的 UI 表现力,已成为 Google 主推的现代应用开发框架。它使用 Dart 语言,通过自绘引擎 Skia 实现原生级渲染,支持一套代码运行在 iOS、Android、Web 和桌面端。
本文将带你从零开始,使用 Flutter 构建一个真实的天气查询应用,涵盖 UI 布局、HTTP 请求、JSON 解析、状态管理与错误处理,并附有完整可运行代码。
一、环境准备
确保已安装:
- Flutter SDK(v3.19+)
- Dart
- 编辑器(推荐 VS Code 或 Android Studio)
- 模拟器或真机调试设备
创建项目:
flutter create flutter_weather_app
cd flutter_weather_app
二、添加依赖
编辑 pubspec.yaml 文件,添加网络请求和图标支持:
dependencies:
flutter:
sdk: flutter
http: ^1.0.0 # 用于发送 HTTP 请求
intl: ^0.19.0 # 格式化日期等(可选)
保存后运行:
flutter pub get
三、获取免费天气 API
我们使用 OpenWeatherMap 的免费 API:
- 注册账号并获取 API Key
- 使用如下接口:
https://api.openweathermap.org/data/2.5/weather?q=城市名&appid=你的APIKey&units=metric&lang=zh_cn
示例响应(JSON):
{
"name": "Beijing",
"main": {
"temp": 22.5,
"humidity": 60
},
"weather": [
{
"description": "晴朗",
"icon": "01d"
}
]
}
四、项目结构
lib/
├── main.dart
├── models/
│ └── weather.dart
├── services/
│ └── weather_service.dart
└── screens/
└── weather_screen.dart
五、完整代码实现
1. 模型类:models/weather.dart
// lib/models/weather.dart
class Weather {
final String cityName;
final double temperature;
final String description;
final String iconCode;
Weather({
required this.cityName,
required this.temperature,
required this.description,
required this.iconCode,
});
// 工厂构造函数:从 JSON 创建对象
factory Weather.fromJson(Map<String, dynamic> json) {
return Weather(
cityName: json['name'],
temperature: json['main']['temp'].toDouble(),
description: json['weather'][0]['description'],
iconCode: json['weather'][0]['icon'],
);
}
}
2. 网络服务:services/weather_service.dart
// lib/services/weather_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/weather.dart';
class WeatherService {
final String apiKey;
WeatherService(this.apiKey);
Future<Weather> fetchWeather(String city) async {
final url = Uri.parse(
'https://api.openweathermap.org/data/2.5/weather?q=$city&appid=$apiKey&units=metric&lang=zh_cn');
final response = await http.get(url);
if (response.statusCode == 200) {
return Weather.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load weather data');
}
}
}
3. 主界面:screens/weather_screen.dart
// lib/screens/weather_screen.dart
import 'package:flutter/material.dart';
import '../models/weather.dart';
import '../services/weather_service.dart';
class WeatherScreen extends StatefulWidget {
const WeatherScreen({super.key});
State<WeatherScreen> createState() => _WeatherScreenState();
}
class _WeatherScreenState extends State<WeatherScreen> {
final TextEditingController _controller = TextEditingController();
Weather? _weather;
bool _loading = false;
String? _error;
// 替换为你的 OpenWeatherMap API Key
final WeatherService service = WeatherService("YOUR_API_KEY_HERE");
void _searchWeather() async {
final city = _controller.text.trim();
if (city.isEmpty) return;
setState(() {
_loading = true;
_error = null;
});
try {
final weather = await service.fetchWeather(city);
setState(() {
_weather = weather;
_loading = false;
});
} catch (e) {
setState(() {
_error = e.toString();
_loading = false;
});
}
}
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: (_) => _searchWeather(),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _searchWeather,
child: const Icon(Icons.search),
),
],
),
const SizedBox(height: 20),
// 加载状态
if (_loading)
const CircularProgressIndicator()
else if (_error != null)
Text(
'错误: $_error',
style: TextStyle(color: Colors.red[700]),
)
else if (_weather != null)
_buildWeatherCard(_weather!)
else
const Text("请输入城市查询天气")
],
),
),
);
}
// 显示天气信息卡片
Widget _buildWeatherCard(Weather weather) {
return Card(
elevation: 4,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
weather.cityName,
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,
height: 100,
),
Text(
'${weather.temperature.toStringAsFixed(1)}°C',
style: const TextStyle(fontSize: 40, color: Colors.orange),
),
Text(
weather.description,
style: const TextStyle(fontSize: 18, color: Colors.grey),
),
],
),
),
);
}
void dispose() {
_controller.dispose();
super.dispose();
}
}
4. 入口文件:main.dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'screens/weather_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 天气应用',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const WeatherScreen(),
debugShowCheckedModeBanner: false,
);
}
}
六、运行应用
flutter run
在输入框中输入城市如 “Shanghai” 或 “纽约”,点击搜索,即可看到实时天气信息。
七、核心知识点总结
| 技术点 | 说明 |
|---|---|
StatefulWidget |
用于需要动态更新的界面(如加载、数据变化) |
http.get() |
发送网络请求获取天气数据 |
Future 与 async/await |
异步处理网络调用 |
setState() |
触发 UI 重绘 |
TextFormField |
用户输入处理 |
JSON 解析 |
使用 factory 构造函数映射 API 响应 |
| 错误处理 | try-catch 捕获异常并友好提示 |
八、优化建议(进阶方向)
- ✅ 使用
Provider/Riverpod进行全局状态管理 - ✅ 添加地理位置定位自动获取城市
- ✅ 支持天气图标本地缓存
- ✅ 增加未来几天预报(调用 5-day forecast API)
- ✅ 支持主题切换(深色/浅色模式)
九、结语
通过这个实战项目,你已经掌握了 Flutter 应用开发的核心流程:
UI 构建 → 网络请求 → 数据解析 → 状态更新 → 用户交互
Flutter 不仅让跨平台开发变得简单高效,更以接近原生的性能和丰富的组件生态,成为现代移动开发的理想选择。
🎯 下一步:将本项目部署到 Web 平台(flutter build web),让你的天气应用在浏览器中也能运行!
📌 提示:请将 YOUR_API_KEY_HERE 替换为你在 OpenWeatherMap 获取的真实密钥。
GitHub 示例地址(参考):github.com/yourname/flutter-weather
本文代码已在 Flutter 3.22 上测试通过,支持 Android、iOS 与 Web 平台。
更多推荐


所有评论(0)