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

متتبع العادات: علّم يومًا

جزء من قسم المشاريع في رحلة Flutter على Coddy. الدرس 30 من 36.

انقر على دائرة لوضع علامة على اليوم، وانقر عليها مرة أخرى لإزالة العلامة. بيانات العادات موجودة في حالة الشاشة، لكن الدائرة مرسومة في عمق عنصر HabitCard. يجب أن تنتقل النقرة إلى الأعلى، إلى الموضع الذي توجد فيه البيانات.

هذا هو رفع الحالة إلى الأعلى باستخدام دوال الاستدعاء. الشاشة هي التي تتولى التغيير:

void _toggle(Habit habit, int day) {
  setState(() {
    habit.days[day] = !habit.days[day];
  });
}

وتمرر إلى كل بطاقة دالةً، onToggle: (day) => _toggle(habit, day). نوعها في البطاقة هو void Function(int day)، أي دالة تأخذ رقم اليوم. وتحصل كل دائرة على VoidCallback عادية، onTap: () => onToggle(i)، وتُغلّف عنصر Container الخاص بها داخل GestureDetector يستدعيها.

يبدّل !habit.days[day] قيمة bool: تتحول true إلى false والعكس، لذا تؤدي دالة واحدة مهمتي وضع العلامة وإزالتها.

challenge icon

التحدي

صعب

حدّد الأيام:

  • أضف الطريقة _toggle إلى حالة الشاشة
  • أضف إلى HabitCard الخاصية onToggle الإلزامية من النوع void Function(int day)، ومرّر (day) => _toggle(habit, day)
  • أضف إلى DayCircle الخاصية VoidCallback onTap الإلزامية، ولفّ Container الخاص بها داخل GestureDetector، ومرّر () => onToggle(i)

جرّب بنفسك

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
      ),
      home: const HabitScreen(),
    );
  }
}

const List<String> dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

class Habit {
  Habit(this.name, this.days);

  final String name;
  final List<bool> days;
}

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

  @override
  State<HabitScreen> createState() => _HabitScreenState();
}

class _HabitScreenState extends State<HabitScreen> {
  final List<Habit> _habits = [
    Habit('Drink water', [true, true, true, false, true, false, false]),
    Habit('Read 10 pages', [true, false, true, true, false, false, false]),
    Habit('Stretch', List.filled(7, false)),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Habits')),
      body: ListView(
        padding: const EdgeInsets.fromLTRB(16, 8, 16, 88),
        children: [
          for (Habit habit in _habits) HabitCard(habit: habit),
        ],
      ),
    );
  }
}

class HabitCard extends StatelessWidget {
  const HabitCard({super.key, required this.habit});

  final Habit habit;

  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;
    return Card(
      margin: const EdgeInsets.only(top: 12),
      clipBehavior: Clip.antiAlias,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(habit.name, style: textTheme.titleMedium),
            const SizedBox(height: 12),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                for (int i = 0; i < 7; i++) DayCircle(label: dayNames[i], done: habit.days[i]),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class DayCircle extends StatelessWidget {
  const DayCircle({super.key, required this.label, required this.done});

  final String label;
  final bool done;

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).colorScheme;
    return Container(
      width: 40,
      height: 40,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: done ? colors.primary : colors.surfaceContainerHighest,
      ),
      child: Text(
        label,
        style: TextStyle(
          fontSize: 11,
          fontWeight: FontWeight.bold,
          color: done ? colors.onPrimary : colors.onSurfaceVariant,
        ),
      ),
    );
  }
}
quiz iconاختبر نفسك

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

جميع دروس المشاريع

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