مُنشئ fromJson
جزء من قسم البيانات والشبكة في رحلة Flutter على Coddy. الدرس 17 من 27.
تصبح قراءة user['name'] as String في جميع شاشاتك فوضوية، كما أن خطأً مطبعيًا واحدًا في المفتاح يؤدي إلى تعطّلها. والحل المعتاد هو إنشاء صنف للبيانات، مع مُنشئ واحد يعرف كيفية التعامل مع JSON. ويُسمّى fromJson:
class User {
User({required this.name, required this.age});
final String name;
final int age;
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
}User.fromJson هو مُنشئ مُسمّى، وطريقة ثانية لإنشاء User. تتيح الكلمة factory للمُنشئ تشغيل التعليمات البرمجية وreturn الكائن الذي ينشئه. لا يمكن للمُنشئ العادي أن يُرجع أي شيء باستخدام return.
أصبح JSON الآن يُقرأ في موضع واحد. ويحصل باقي التطبيق على كائن User حقيقي، حيث يكون user.name من النوع String، ويؤدي الخطأ المطبعي إلى خطأ في وقت الترجمة بدلًا من null:
final body = await fetch('/user');
return User.fromJson(jsonDecode(body));تحتوي جميع التطبيقات تقريبًا التي تتواصل مع خادم على fromJson لكل نوع من البيانات التي تتلقاها.
التحدي
صعبيعرض الملف الشخصي Unknown: تتجاهل loadUser بيانات JSON. أضف إلى User مُنشئًا من النوع factory User.fromJson(Map<String, dynamic> json) يقرأ name وemail وage، واجعل loadUser تُرجع User.fromJson(jsonDecode(body)). استورد dart:convert.
جرّب بنفسك
import 'package:flutter/material.dart';
import 'api.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: ProfileScreen(),
);
}
}
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late Future<User> _user;
@override
void initState() {
super.initState();
_user = loadUser();
}
Future<User> loadUser() async {
final body = await fetch('/user');
print(body);
return User(name: 'Unknown', email: 'unknown', age: 0);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Profile')),
body: Center(
child: FutureBuilder<User>(
future: _user,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Could not load: ${snapshot.error}');
}
if (snapshot.hasData) {
final user = snapshot.data!;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
user.name,
style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(user.email, style: const TextStyle(fontSize: 18)),
const SizedBox(height: 8),
Text('${user.age} years old'),
],
);
}
return const CircularProgressIndicator();
},
),
),
);
}
}
class User {
User({required this.name, required this.email, required this.age});
final String name;
final String email;
final int age;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البيانات والشبكة
تدرّب بنفسك: مترجم Flutter عبر الإنترنت