现代移动应用几乎都离不开网络请求,用于获取远程数据、与后端服务交互。Flutter 提供了多种进行网络请求的方式,最常用的是 Dart 内置的 http 包和第三方库 dio。数据解析通常涉及 JSON 格式,Dart 提供了内置的 dart:convert 库来处理 JSON。
1. http 包
http 是 Dart 官方推荐的用于进行 HTTP 请求的包。它提供了 get、post、put、delete 等方法,以及处理请求头、请求体和响应的能力。
pubspec.yaml 中添加 http 依赖。
dependencies:
http: ^1.1.0 # 使用最新版本
```
运行 flutter pub get。
import 'package:http/http.dart' as http;
import 'dart:convert'; // 用于 JSON 解析
Future<void> fetchData() async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
// 请求成功,解析 JSON 数据
final Map<String, dynamic> data = jsonDecode(response.body);
print('数据: $data');
print('标题: ${data["title"]}');
} else {
// 请求失败
print('请求失败,状态码: ${response.statusCode}');
}
} catch (e) {
// 捕获网络错误
print('网络请求错误: $e');
}
}
void main() {
fetchData();
}
2. dio 库
dio 是一个功能强大的 Dart HTTP 客户端,它支持拦截器、全局配置、FormData、请求取消、文件上传/下载、超时等功能,更适合复杂的网络请求场景。
pubspec.yaml 中添加 dio 依赖。
dependencies:
dio: ^5.4.0 # 使用最新版本
```
运行 flutter pub get。
import 'package:dio/dio.dart';
Future<void> fetchDataWithDio() async {
final dio = Dio();
try {
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
// dio 自动解析 JSON,response.data 就是解析后的数据
final Map<String, dynamic> data = response.data;
print('数据: $data');
print('标题: ${data["title"]}');
} else {
print('请求失败,状态码: ${response.statusCode}');
}
} on DioException catch (e) {
// 捕获 Dio 异常
if (e.response != null) {
print('Dio 错误响应: ${e.response!.statusCode}');
print('Dio 错误数据: ${e.response!.data}');
} else {
print('Dio 请求错误: ${e.message}');
}
} catch (e) {
print('其他错误: $e');
}
}
void main() {
fetchDataWithDio();
}
3. 数据解析 (JSON)
Dart 的 dart:convert 库提供了 jsonEncode 和 jsonDecode 函数,用于 JSON 字符串和 Dart 对象之间的转换。
jsonDecode(String source):将 JSON 字符串解析为 Dart 对象(Map<String, dynamic> 或 List<dynamic>)。jsonEncode(Object object):将 Dart 对象编码为 JSON 字符串。对于复杂的 JSON 结构,手动解析会变得繁琐且容易出错。推荐使用 JSON 序列化工具,例如 json_serializable,它可以自动生成 Dart 模型的 fromJson 和 toJson 方法。
json_serializable:pubspec.yaml 中添加 json_annotation、json_serializable 和 build_runner 依赖。
dependencies:
json_annotation: ^4.8.1 # 最新版本可能不同
dev_dependencies:
build_runner: ^2.4.8 # 最新版本可能不同
json_serializable: ^6.7.1 # 最新版本可能不同
运行 `flutter pub get`。 2. **创建模型类**:使用 `json_annotation` 注解。 dart
import 'package:json_annotation/json_annotation.dart';
part 'post.g.dart'; // 这行是自动生成的文件,需要手动添加
@JsonSerializable()
class Post {
final int userId;
final int id;
final String title;
final String body;
Post({
required this.userId,
required this.id,
required this.title,
required this.body,
});
factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
Map<String, dynamic> toJson() => _$PostToJson(this);
}
```
3. **运行代码生成器**:在终端运行 `flutter pub run build_runner build`。这会生成 `post.g.dart` 文件。
4. **使用模型**:
```dart
final Map<String, dynamic> jsonMap = jsonDecode(response.body);
final Post post = Post.fromJson(jsonMap);
print(post.title);
```
网络请求和数据解析是几乎所有真实世界 Flutter 应用的核心功能。无论是获取新闻、商品列表、用户数据,还是提交表单,都离不开与后端服务的交互。
案例:构建一个简单的商品列表应用,从 API 获取数据并显示
我们将创建一个应用,从一个模拟的 REST API 获取商品列表,并使用 http 包进行网络请求,手动解析 JSON 数据,然后显示在列表中。
步骤 1: 创建商品模型
// lib/models/product.dart
class Product {
final int id;
final String name;
final double price;
final String imageUrl;
Product({
required this.id,
required this.name,
required this.price,
required this.imageUrl,
});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json["id"] as int,
name: json["name"] as String,
price: (json["price"] as num).toDouble(), // JSON 可能返回 int 或 double
imageUrl: json["imageUrl"] as String,
);
}
}
步骤 2: 创建商品服务
// lib/services/product_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/product.dart';
class ProductService {
static const String _baseUrl = 'https://my-json-server.typicode.com/typicode/demo/posts'; // 模拟 API
Future<List<Product>> fetchProducts() async {
final response = await http.get(Uri.parse(_baseUrl));
if (response.statusCode == 200) {
// 模拟的 API 返回的是 posts,我们将其视为 products
final List<dynamic> jsonList = jsonDecode(response.body);
return jsonList.map((json) => Product.fromJson(json)).toList();
} else {
throw Exception('Failed to load products');
}
}
}
步骤 3: 在 UI 中使用服务
import 'package:flutter/material.dart';
import 'package:my_app/models/product.dart'; // 假设你的文件路径
import 'package:my_app/services/product_service.dart'; // 假设你的文件路径
class ProductListScreen extends StatefulWidget {
const ProductListScreen({super.key});
@override
State<ProductListScreen> createState() => _ProductListScreenState();
}
class _ProductListScreenState extends State<ProductListScreen> {
late Future<List<Product>> _productsFuture;
@override
void initState() {
super.initState();
_productsFuture = ProductService().fetchProducts();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('商品列表'),
),
body: FutureBuilder<List<Product>>(
future: _productsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('错误: ${snapshot.error}'));
} else if (snapshot.hasData) {
final products = snapshot.data!;
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
// 模拟图片,实际应用中会加载网络图片
// Image.network(product.imageUrl, width: 80, height: 80, fit: BoxFit.cover),
Container(
width: 80,
height: 80,
color: Colors.grey[300],
child: Center(child: Text('图片\n${product.id}')),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
product.name,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Text('价格: \$${product.price.toStringAsFixed(2)}'),
],
),
),
],
),
),
);
},
);
} else {
return const Center(child: Text('没有商品数据。'));
}
},
),
);
}
}
void main() {
runApp(const MaterialApp(home: ProductListScreen()));
}
案例分析:
Product 模型:定义了从 API 获取的商品数据结构。fromJson 工厂构造函数负责将 JSON Map 转换为 Product 对象。ProductService:封装了网络请求逻辑。fetchProducts 方法使用 http.get 发送请求,并手动 jsonDecode 响应体,然后将 JSON 列表映射为 Product 对象列表。FutureBuilder:在 UI 层,FutureBuilder 是处理异步操作结果的强大 Widget。它监听一个 Future(这里是 _productsFuture),并根据 Future 的状态(waiting、hasError、hasData)来构建不同的 UI。ConnectionState.waiting:显示加载指示器。snapshot.hasError:显示错误信息。snapshot.hasData:显示商品列表。ListView.builder 用于高效地构建可滚动的列表。这个案例展示了 Flutter 应用中网络请求和数据解析的完整流程。从定义数据模型、封装网络服务,到在 UI 中使用 FutureBuilder 处理异步数据,这些都是构建数据驱动型 Flutter 应用的基础。