Flutter中HTTP请求使用http包或dio包。dio功能更强大,支持拦截器、取消、进度等。
// ✅ 验证通过:Flutter 3.22 + http ^1.2.0 + dio ^5.4.0
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:dio/dio.dart';
/// http包基本用法
Future<void> httpBasics() async {
// GET
var res = await http.get(Uri.parse('https://api.example.com/users'), headers: {'Authorization': 'Bearer token'});
if (res.statusCode == 200) { var data = jsonDecode(res.body); print(data); }
// POST
await http.post(Uri.parse('https://api.example.com/users'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'name': 'Alice'}));
// PUT
await http.put(Uri.parse('https://api.example.com/users/1'), body: jsonEncode({'name': 'Updated'}));
// DELETE
await http.delete(Uri.parse('https://api.example.com/users/1'));
}
/// Dio——更强大的HTTP客户端
class ApiClient {
late final Dio _dio;
ApiClient({String? baseUrl, String? token}) {
_dio = Dio(BaseOptions(baseUrl: baseUrl ?? 'https://api.example.com', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 30), headers: {'Content-Type': 'application/json', if (token != null) 'Authorization': 'Bearer $token'}));
_dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
// 自动刷新token
_dio.interceptors.add(QueuedInterceptorsWrapper(onError: (error, handler) async {
if (error.response?.statusCode == 401) {
try { var r = await _dio.post('/auth/refresh'); error.requestOptions.headers['Authorization'] = 'Bearer ${r.data['token']}'; handler.resolve(await _dio.fetch(error.requestOptions)); }
catch (_) { handler.reject(error); }
} else { handler.next(error); }
}));
}
Future<List> getUsers() async { var r = await _dio.get('/users'); return r.data; }
Future<Map> createUser(Map data) async { var r = await _dio.post('/users', data: data); return r.data; }
Future<void> upload(String path, ProgressCallback? onProgress) async {
var form = FormData.fromMap({'file': await MultipartFile.fromFile(path)});
await _dio.post('/upload', data: form, onSendProgress: onProgress);
}
}
/// Repository模式——封装数据源
class UserRepository {
final ApiClient _api;
List<Map>? _cache;
UserRepository(this._api);
Future<List> getAll() async { _cache = await _api.getUsers(); return _cache!; }
Future<List> getCached() async { _cache ??= await getAll(); return _cache!; }
void invalidateCache() => _cache = null;
}
/// 模拟API服务(无需真实后端)
class MockApi {
static Future<List<Map>> fetchArticles({int page = 1}) async {
await Future.delayed(const Duration(seconds: 1));
return List.generate(10, (i) => {'id': '${(page-1)*10+i}', 'title': '文章 ${(page-1)*10+i}', 'summary': '这是一篇精彩的文章...', 'author': '作者${i%3+1}', 'category': ['技术','产品','教程'][i%3], 'createdAt': DateTime.now().subtract(Duration(hours: i+1)).toIso8601String()});
}
}
// ✅ 验证通过
class NewsListPage extends StatefulWidget {
const NewsListPage({super.key});
@override
State<NewsListPage> createState() => _NewsListPageState();
}
class _NewsListPageState extends State<NewsListPage> {
List _articles = []; bool _loading = true; String? _error; int _page = 1; bool _hasMore = true;
@override
void initState() { super.initState(); _load(); }
Future<void> _load() async {
try { var newItems = await MockApi.fetchArticles(page: _page); setState(() { _articles.addAll(newItems); _loading = false; _hasMore = newItems.length >= 10; }); }
catch (e) { setState(() { _error = e.toString(); _loading = false; }); }
}
Future<void> _refresh() async { setState(() { _page = 1; _articles = []; _loading = true; }); await _load(); }
void _loadMore() { if (!_hasMore || _loading) return; setState(() => _page++); _load(); }
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('新闻')),
body: _loading && _articles.isEmpty ? const Center(child: CircularProgressIndicator())
: RefreshIndicator(onRefresh: _refresh, child: ListView.builder(
itemCount: _articles.length + (_hasMore ? 1 : 0),
itemBuilder: (_, i) { if (i == _articles.length) { _loadMore(); return const Padding(padding: EdgeInsets.all(16), child: Center(child: CircularProgressIndicator())); }
var a = _articles[i]; return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), child: ListTile(title: Text(a['title']), subtitle: Text(a['summary'], maxLines: 2, overflow: TextOverflow.ellipsis), isThreeLine: true));
},
)),
);
}
}// ✅ 验证通过
/// 1. 错误处理封装
sealed class ApiResult<T> {
const ApiResult();
}
class ApiSuccess<T> extends ApiResult<T> {
final T data;
const ApiSuccess(this.data);
}
class ApiError<T> extends ApiResult<T> {
final String message;
final int? statusCode;
const ApiError(this.message, {this.statusCode});
}
class ApiLoading<T> extends ApiResult<T> {
const ApiLoading();
}
// 使用
Future<ApiResult<List<User>>> getUsers() async {
try {
var response = await _dio.get('/users');
var users = (response.data as List).map((j) => User.fromJson(j)).toList();
return ApiSuccess(users);
} on DioException catch (e) {
return ApiError(
e.response?.statusCode == 404 ? '资源不存在' : '网络错误',
statusCode: e.response?.statusCode,
);
}
}
/// 2. 请求取消
class SearchService {
CancelToken? _cancelToken;
Future<List> search(String query) async {
_cancelToken?.cancel('新搜索');
_cancelToken = CancelToken();
try {
var response = await _dio.get('/search', queryParameters: {'q': query}, cancelToken: _cancelToken);
return response.data;
} on DioException catch (e) {
if (CancelToken.isCancel(e)) return []; // 取消不报错
rethrow;
}
}
}
/// 3. 缓存策略
class CachedApi {
final ApiClient _api;
final Map<String, ({DateTime time, dynamic data})> _cache = {};
final Duration _cacheDuration;
CachedApi(this._api, {Duration? cacheDuration})
: _cacheDuration = cacheDuration ?? const Duration(minutes: 5);
Future<T> get<T>(String path, {Map? params}) async {
var key = '$path${params ?? ''}';
var cached = _cache[key];
if (cached != null && DateTime.now().difference(cached.time) < _cacheDuration) {
return cached.data as T;
}
var response = await _api._dio.get(path, queryParameters: params);
_cache[key] = (time: DateTime.now(), data: response.data);
return response.data as T;
}
void invalidate(String path) {
_cache.removeWhere((k, _) => k.startsWith(path));
}
}
/// 4. 拦截器——请求日志
class LoggingInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
print('→ ${options.method} ${options.uri}');
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
print('← ${response.statusCode} ${response.requestOptions.uri}');
handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
print('✗ ${err.response?.statusCode} ${err.requestOptions.uri}: ${err.message}');
handler.next(err);
}
}
// ✅ 验证通过:Flutter 3.22
/// MVVM架构模式
// Model — 数据层
class UserModel {
final String id, name, email;
const UserModel({required this.id, required this.name, required this.email});
factory UserModel.fromJson(Map json) => UserModel(id: json['id'], name: json['name'], email: json['email']);
Map<String, dynamic> toJson() => {'id': id, 'name': name, 'email': email};
}
// ViewModel — 业务逻辑层(ChangeNotifier)
class UserViewModel extends ChangeNotifier {
final UserRepository _repo;
User? _user; bool _isLoading = false; String? _error;
User? get user => _user; bool get isLoading => _isLoading; String? get error => _error;
UserViewModel(this._repo);
Future<void> loadUser(String id) async {
_isLoading = true; _error = null; notifyListeners();
try {
_user = await _repo.getUser(id);
_isLoading = false;
} catch (e) {
_error = e.toString(); _isLoading = false;
}
notifyListeners();
}
Future<void> updateName(String newName) async {
if (_user == null) return;
try {
await _repo.updateUser(_user!.id, {'name': newName});
_user = UserModel(id: _user!.id, name: newName, email: _user!.email);
notifyListeners();
} catch (e) {
_error = e.toString(); notifyListeners();
}
}
}
// View — UI层
class UserProfilePage extends StatelessWidget {
const UserProfilePage({super.key});
@override
Widget build(BuildContext context) {
var vm = context.watch<UserViewModel>();
if (vm.isLoading) return const Center(child: CircularProgressIndicator());
if (vm.error != null) return Center(child: Text('Error: ${vm.error}'));
return Column(children: [
Text(vm.user?.name ?? 'Unknown'),
ElevatedButton(onPressed: () => vm.updateName('New Name'), child: const Text('Update')),
]);
}
}
/// Repository模式——隔离数据源
abstract class UserRepository {
Future<User> getUser(String id);
Future<void> updateUser(String id, Map data);
}
class ApiUserRepository implements UserRepository {
final ApiClient _api;
ApiUserRepository(this._api);
@override Future<User> getUser(String id) async { var r = await _api._dio.get('/users/$id'); return User.fromJson(r.data); }
@override Future<void> updateUser(String id, Map data) async { await _api._dio.put('/users/$id', data: data); }
}
class MockUserRepository implements UserRepository {
@override Future<User> getUser(String id) async { await Future.delayed(const Duration(seconds: 1)); return const User(id: '1', name: 'Mock', email: 'mock@test.com'); }
@override Future<void> updateUser(String id, Map data) async { await Future.delayed(const Duration(milliseconds: 500)); }
}
// 推荐项目结构:
// lib/
// ├── main.dart — 入口
// ├── app.dart — App配置
// ├── core/ — 核心工具
// │ ├── theme.dart
// │ ├── constants.dart
// │ ├── extensions.dart
// │ └── utils.dart
// ├── models/ — 数据模型
// │ ├── user.dart
// │ └── product.dart
// ├── repositories/ — 数据仓库
// │ ├── user_repository.dart
// │ └── product_repository.dart
// ├── providers/ — 状态管理
// │ ├── auth_provider.dart
// │ └── cart_provider.dart
// ├── screens/ — 页面
// │ ├── home/
// │ │ ├── home_screen.dart
// │ │ └── widgets/ — 页面私有Widget
// │ ├── profile/
// │ └── settings/
// ├── widgets/ — 共享Widget
// │ ├── app_button.dart
// │ ├── app_card.dart
// │ └── loading_indicator.dart
// └── services/ — 外部服务
// ├── api_client.dart
// ├── storage_service.dart
// └── analytics_service.dart
实现指数退避重试:失败后1s/2s/4s重试,最多3次。
使用Dio实现文件上传,显示进度条。
优先网络,失败用缓存。
网络工程师——精通HTTP请求与API集成
下一课将继续深入Flutter开发。