أتقِن Future وasync/await وFutureBuilder دون تجميد الواجهة أو إعادة إطلاق طلباتك عن غير قصد.
افتح هذا الدرس في Kodokonتُشغِّل Dart تطبيقك على خيط واحد، تقوده حلقة أحداث. إن Future ليس خيطًا: إنه وعد بقيمة قادمة. ولا يحجب await ذلك الخيط أبدًا - بل يعلّق الدالة الحالية، ويعيد التحكم إلى الحلقة (فتظل الواجهة تعمل)، ثم يستأنف عندما تصل النتيجة. ونتيجةً لذلك: لا يوازي async/await أيّ شيء في الحساب الصرف؛ أما العمل المُكثِّف لوحدة المعالجة (CPU)، فيتطلب عازلًا (isolate).
Future<double> fetchRate() async {
await Future<void>.delayed(
const Duration(milliseconds: 300),
);
return 1.08;
}
Future<void> main() async {
try {
final rate = await fetchRate();
print('EUR/USD rate: $rate');
} on Exception catch (error) {
print('Rate loading failed: $error');
}
}يُنفَّذ استدعاءان متتاليان لـ await بالتسلسل: فلا يبدأ الثاني إلا بعد الأول. وعندما تكون العمليات مستقلة، أطلِقها معًا باستخدام Future.wait. والمقايضة التي ينبغي معرفتها: إن Future.wait سريع الفشل - فأول خطأ يرفض العملية بأكملها وتضيع نتائج الـ futures الأخرى، حتى وإن نجحت. فإذا احتجت نتائج جزئية، فعالِج الخطأ داخل كل future.
Future<String> fetchProfile() async => 'profile';
Future<String> fetchOrders() async => 'orders';
Future<void> main() async {
final [profile, orders] = await Future.wait(
[fetchProfile(), fetchOrders()],
);
print('$profile / $orders');
}في جانب الواجهة، يربط FutureBuilder بين Future وbuild عبر snapshot. والمزلق الأول عمليًا: إنشاء الـ future مباشرةً داخل build. فكل إعادة بناء - ويكفي setState بسيط من أب - سيعيد عندئذٍ إطلاق الطلب. خزِّن الـ future في initState، واختبر دائمًا الخطأ قبل البيانات.
import 'package:flutter/material.dart';
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
State<ProfileScreen> createState() =>
_ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late final Future<String> _nameFuture;
@override
void initState() {
super.initState();
_nameFuture = _loadName();
}
Future<String> _loadName() async {
await Future<void>.delayed(
const Duration(seconds: 1),
);
return 'Ada Lovelace';
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _nameFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Text(snapshot.data!);
},
);
}
}await فعليًا على الخيط الرئيسي؟Future.Future.wait([a(), b()])، ماذا يحدث إذا فشل a()؟Future.wait مع خطأ a()؛ وتضيع نتيجة b().Future.wait انتهاء b()، ثم يُرجِع نتيجته وحدها.b() ينجح.future: fetchUser() مباشرةً داخل build مع FutureBuilder؟build على استدعاء لاتزامني، فلا يُصرَّف الكود.Future جديدًا، ومن ثمّ تعيد إطلاق طلب الشبكة.snapshot عالقًا في حالة انتظار.