إطلاق خطأ
جزء من قسم البيانات والشبكة في رحلة Flutter على Coddy. الدرس 11 من 27.
الأخطاء ليست مقتصرة على الخوادم. يمكن لدوالك أن ترفض المتابعة أيضًا. عندما تتلقى دالة شيئًا لا تستطيع التعامل معه، فإنها ترمي خطأً:
Future<void> saveName(String name) async {
if (name.isEmpty) {
throw Exception('The name cannot be empty');
}
// احفظ الاسم
}توقف throw الدالة في مكانها، مثل return، لكنها تعيد خطأً بدلًا من قيمة. إن Exception('...') هو الخطأ، ومعه رسالة توضّح ما حدث خطأً.
تلتقطه الجهة التي استدعت الدالة باستخدام try وcatch، تمامًا مثل خطأ صادر عن fetch. عند تحويله إلى نص، يبدأ Exception بـ Exception: ، ثم تأتي رسالتك.
وهكذا تعمل fetch الحقيقية أيضًا. تأتي كل إجابة من خادم مصحوبةً بـ رمز الحالة: تعني 200 أن الطلب ناجح، وتعني 404 أن المورد غير موجود. عندما لا تكون الحالة 200، يرمي كودك خطأً، فلا تعرض الشاشة إجابةً معطوبةً كما لو كانت بيانات:
final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception('The server answered ${response.statusCode}');
}
return response.body;التحدي
سهلاضغط على Save والحقل فارغ: ستظهر الرسالة Saved: من دون اسم. اجعل saveName يرفض الاسم الفارغ: بعد الانتظار، إذا كان name فارغًا، اطرح Exception('The name cannot be empty'). تلتقطه الدالة save بالفعل وتعرض الرسالة.
جرّب بنفسك
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: NameScreen(),
);
}
}
class NameScreen extends StatefulWidget {
const NameScreen({super.key});
@override
State<NameScreen> createState() => _NameScreenState();
}
class _NameScreenState extends State<NameScreen> {
final controller = TextEditingController();
String status = '';
@override
void dispose() {
controller.dispose();
super.dispose();
}
Future<void> saveName(String name) async {
await Future.delayed(const Duration(seconds: 1));
}
Future<void> save() async {
try {
await saveName(controller.text);
setState(() {
status = 'Saved: ${controller.text}';
});
} catch (e) {
setState(() {
status = '$e';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: controller,
decoration: const InputDecoration(labelText: 'Name'),
),
const SizedBox(height: 16),
ElevatedButton(onPressed: save, child: const Text('Save')),
const SizedBox(height: 16),
Text(status, style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البيانات والشبكة
تدرّب بنفسك: مترجم Flutter عبر الإنترنت