Kodokon kodokon.com

مشاركة الحالة: رفع الحالة إلى الأعلى ودوال رد النداء

احتفظ بالحالة في السلف المشترك، وحوّل ويدجاتك الأبناء إلى ويدجات مُتحكَّم بها تُبلِّغ عن التغييرات عبر دوال رد النداء.

8 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

موقف كلاسيكي: مُحدِّد كمية ومجموع معروض في مكان آخر يحتاجان إلى مشاركة البيانات نفسها. القاعدة الذهبية: تعيش الحالة في أقرب سلف مشترك للويدجات التي تعتمد عليها. يملك الأب البيانات، ويمرِّرها إلى الأسفل كوسيط، ويتلقّى التغييرات عبر دالة رد نداء تتدفّق عائدةً إلى الأعلى. وهذا هو "رفع الحالة إلى الأعلى".

DART
import 'package:flutter/material.dart';

class CartScreen extends StatefulWidget {
  const CartScreen({super.key});

  @override
  State<CartScreen> createState() => _CartScreenState();
}

class _CartScreenState extends State<CartScreen> {
  int _quantity = 1;

  void _updateQuantity(int value) {
    setState(() => _quantity = value);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cart')),
      body: Column(
        children: [
          QuantitySelector(
            quantity: _quantity,
            onChanged: _updateQuantity,
          ),
          Text('Total: ${_quantity * 20} €'),
        ],
      ),
    );
  }
}
يملك الأب الحالة ويوزّع القيمة ودالة رد النداء.

يصبح الابن ويدجت مُتحكَّمًا به: فهو StatelessWidget، لا يخزّن شيئًا، ويعرض القيمة التي يتلقّاها، ويُشير إلى كل نيّة عبر onChanged. إن ValueChanged<int> مجرد اسم بديل لـ void Function(int). لاحظ زر الطرح المُعطَّل عند الكمية 1: يكفي تمرير null إلى onPressed، وهذا المنطق ينبع مباشرةً من القيمة المُتلقّاة.

DART
import 'package:flutter/material.dart';

class QuantitySelector extends StatelessWidget {
  const QuantitySelector({
    super.key,
    required this.quantity,
    required this.onChanged,
  });

  final int quantity;
  final ValueChanged<int> onChanged;

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        IconButton(
          icon: const Icon(Icons.remove),
          onPressed: quantity > 1
              ? () => onChanged(quantity - 1)
              : null,
        ),
        Text('$quantity'),
        IconButton(
          icon: const Icon(Icons.add),
          onPressed: () => onChanged(quantity + 1),
        ),
      ],
    );
  }
}
يعرض الابن القيمة التي يتلقّاها ويُبلِّغ عن النيّات عائدةً إلى الأعلى.

أنت تستخدم هذا العقد طوال الوقت دون أن تدرك: فإن Checkbox وSlider وSwitch كلها ويدجات مُتحكَّم بها (value + onChanged). وعندما تتعمّق الشجرة ويصبح تمرير دوال رد النداء عبر خمسة مستويات مُرهِقًا، سيحين وقت اكتشاف حلول إدارة الحالة - وهذا موضوع الوحدة التالية.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. أين يجب أن تضع الحالة التي يتشاركها ويدجتان شقيقان؟
    • في كل من الويدجتين
    • في أقرب سلف مشترك
    • في متغير عام
    • في MaterialApp
  2. كيف يطلب ويدجت ابن تغييرًا في الحالة؟
    • يستدعي setState مباشرةً على أبيه
    • يعدّل الخاصية التي تلقّاها كوسيط
    • يستدعي دالة رد النداء التي قدّمها الأب
  3. ما هو الويدجت المُتحكَّم به؟
    • ويدجت يتلقّى قيمته ويُبلِّغ عن التغييرات عبر دالة رد نداء، دون حالة خاصة به
    • ويدجت يجب أن يستخدم setState
    • ويدجت تغطّيه اختبارات الوحدة
    • ويدجت مُصرَّح به باستخدام const