نظِّم استدعاءً شبكيًا احترافيًا باستخدام حزمة http: مهلة زمنية، ورموز الحالة، وفك ترميز ذي أنواع، وحالات شاملة.
افتح هذا الدرس في Kodokonتغطّي حزمة http الأساسيات: الطلبات، والترويسات، والجسم. وتضيف dio المعترِضات، والإلغاء، وإعادة المحاولة، على حساب اعتمادية أثقل - وهو خيار جيد بمجرد أن ينمو التطبيق ليتجاوز بضع نقاط نهاية. ومهما كان العميل، لا تتغير بنية الاستدعاء الشبكي الجادّ: مهلة زمنية، وفحص رمز حالة HTTP، وفك ترميز ذي أنواع، وثلاث حالات للشاشة (تحميل، خطأ، بيانات) تُعالَج صراحةً.
flutter pub add httpimport 'dart:convert';
import 'package:http/http.dart' as http;
class Article {
const Article({required this.title});
factory Article.fromJson(Map<String, dynamic> json) =>
Article(title: json['title'] as String);
final String title;
}
Future<List<Article>> fetchArticles() async {
final uri = Uri.https('api.example.com', '/articles');
final response = await http
.get(uri)
.timeout(const Duration(seconds: 8));
if (response.statusCode != 200) {
throw Exception('HTTP ${response.statusCode}');
}
final body = utf8.decode(response.bodyBytes);
final data = jsonDecode(body) as List<dynamic>;
return [
for (final item in data)
Article.fromJson(item as Map<String, dynamic>),
];
}سلوكان كثيرًا ما يفاجئان الناس. الأول: لا تُطلق http أيّ استثناء عند الحالة 404 أو 500 - فالطلب "نجح" من وجهة نظر الشبكة، لذا يقع عليك فحص statusCode. الثاني: تُرجِع jsonDecode قيمة dynamic؛ فحوِّل فورًا (as List<dynamic> وas Map<String, dynamic>) لاستعادة الأنواع. أما الحمولات الكبيرة، فأحِل فك ترميزها إلى عازل (isolate) باستخدام compute كي لا تجمّد الواجهة.
يبقى تمثيل حالة الشاشة. فالأعلام المنطقية مثل isLoading مقترنةً بـ error != null تنتهي دائمًا إلى حالات غير متسقة (تحميل وخطأ في آن واحد). أما الصنف sealed فيجعل الحالات متنافية والـ switch شاملًا: فإذا أضفت حالة غدًا، سيشير المُصرِّف إلى كل شاشة يجب تحديثها.
import 'package:flutter/material.dart';
sealed class ArticlesState {
const ArticlesState();
}
class ArticlesLoading extends ArticlesState {
const ArticlesLoading();
}
class ArticlesError extends ArticlesState {
const ArticlesError(this.message);
final String message;
}
class ArticlesLoaded extends ArticlesState {
const ArticlesLoaded(this.titles);
final List<String> titles;
}
Widget buildArticlesBody(ArticlesState state) {
return switch (state) {
ArticlesLoading() => const Center(
child: CircularProgressIndicator(),
),
ArticlesError(:final message) => Center(
child: Text(message),
),
ArticlesLoaded(:final titles) => ListView.builder(
itemCount: titles.length,
itemBuilder: (context, index) => ListTile(
title: Text(titles[index]),
),
),
};
}http.get استجابة 500. ماذا تفعل حزمة http؟try/catch.Response بشكل طبيعي: فعليك أنت فحص statusCode.jsonDecode مع UTF-8.response.body عندئذٍ على أنه latin-1؛ استخدم utf8.decode(response.bodyBytes).sealed في نمذجة حالة الشاشة؟switch شاملًا: إذ يُبلِّغ المُصرِّف عن أيّ حالة غير مُعالَجة.