Menu
CoddyTech
flag Ar iconالعربيةdown icon

مُنشئ 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 لكل نوع من البيانات التي تتلقاها.

challenge icon

التحدي

صعب

يعرض الملف الشخصي 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;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البيانات والشبكة

تدرّب بنفسك: مترجم Flutter عبر الإنترنت