Kodokon kodokon.com

التفاعلية: الأزرار وStatefulWidget وsetState

امنح شاشاتك الحياة بالاستجابة للنقر على الأزرار وتحديث العرض باستخدام setState.

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

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

شاشاتنا جميلة لكنها جامدة: حان وقت الاستجابة لإيماءات المستخدم. والزر القياسي في Material 3 هو ElevatedButton. وهو يأخذ وسيطين أساسيين: child، أي محتواه المعروض (غالبًا Text)، وonPressed، أي الدالة التي تُنفَّذ عندما تنقر عليه. وتُسمى الدالة المُمرَّرة بهذه الطريقة كوسيط دالة رد النداء (callback): فأنت لا تستدعيها بنفسك، بل تستدعيها Flutter نيابةً عنك في اللحظة المناسبة. وتنشئ الصيغة () { ... } دالة مجهولة (anonymous function)، بلا اسم، تُكتب مباشرة في مكانها.

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

Widget buildHelloButton() {
  return ElevatedButton(
    onPressed: () {
      debugPrint("Button pressed!");
    },
    child: const Text("Tap here"),
  );
}
زر يكتب في وحدة التحكم عند كل نقرة

تكتب debugPrint في وحدة التحكم... لكن الشاشة نفسها لا تتغير. لماذا؟ لأن StatelessWidget (وهو widget عديم الحالة) جامد: فحالما يُبنى، لا يمكن أن يتغير بعد ذلك. ولعرض قيمة تتطور، تحتاج إلى StatefulWidget (وهو widget ذو حالة). إن الحالة (state) هي مجموعة البيانات التي يمكن أن تتغير خلال حياة الـ widget: عدّاد، أو صندوق مُحدَّد، أو نص مكتوب... ويسير StatefulWidget دائمًا جنبًا إلى جنب مع صنف State، الذي يحفظ هذه البيانات ويحتوي على الدالة build. وإليك الكلاسيكية العظيمة للبدايات: العدّاد.

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

void main() {
  runApp(const CounterApp());
}

class CounterApp extends StatelessWidget {
  const CounterApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const CounterPage(),
    );
  }
}

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

  @override
  State<CounterPage> createState() =>
      _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Counter: $count",
              style: const TextStyle(fontSize: 32),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  count = count + 1;
                });
              },
              child: const Text("+1"),
            ),
          ],
        ),
      ),
    );
  }
}
العدّاد: StatefulWidget كامل

تأتي الآلية في ثلاث خطوات. 1) يوجد المتغير count في _CounterPageState: وهذا هو الحالة. 2) عند كل نقرة، تستدعي دالة رد النداء setState: تعدّل هذه الدالة الحالة وتُخبر Flutter بأن شيئًا ما قد تغير. 3) عندئذٍ تُعيد Flutter تنفيذ build، التي تعيد رسم الشاشة بالقيمة الجديدة لـ count. تذكّر الفلسفة: أنت لا تغيّر الشاشة مباشرة أبدًا، بل تغيّر البيانات، وتتكفّل Flutter بالعرض.

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

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

  1. ما فائدة الوسيط onPressed في ElevatedButton؟
    • لتحديد النص المعروض على الزر
    • لتوفير الدالة التي تُنفَّذ عند النقر على الزر
    • لاختيار لون الزر
  2. متى ينبغي أن تستخدم StatefulWidget بدلًا من StatelessWidget؟
    • عندما يعتمد العرض على بيانات تتغير بمرور الوقت
    • عندما يحتوي الـ widget على أكثر من ثلاثة أبناء
    • عندما تريد أن يبدأ التطبيق بسرعة أكبر
  3. ماذا يحدث إذا غيّرت count دون استدعاء setState؟
    • يتعطّل التطبيق فورًا
    • يتغير المتغير في الذاكرة لكن الشاشة لا تُحدَّث
    • تستدعي Flutter setState تلقائيًا نيابةً عنك