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

متتبع العادات: الأسبوع

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

تأتي أسفل كل اسم أيام الأسبوع: سبع دوائر صغيرة، من الاثنين إلى الأحد، تُملأ في الأيام التي أنجزت فيها العادة. كل دائرة عنصر واجهة صغير مستقل يُسمى DayCircle، وله تسمية وحالة توضّح ما إذا كان قد أُنجز.

الدائرة هي Container بحجم ثابت وBoxDecoration يكون shape فيه هو BoxShape.circle. يأتي لونها من السمة، لذا يتناسق مع اللون الأخضر للتطبيق، ثم يتغير تلقائيًا في الوضع الداكن:

final colors = Theme.of(context).colorScheme;
...
color: done ? colors.primary : colors.surfaceContainerHighest,

يكون لون النص على خلفية ملوّنة متناسقًا مع ألوان السمة: استخدم onPrimary مع primary، وonSurfaceVariant مع اللون الرمادي. نسّق كل زوج معًا، وسيظل النص مقروءًا دائمًا.

الدوائر السبع هي Row مع MainAxisAlignment.spaceBetween، فتتوزع على امتداد البطاقة. أنشئها باستخدام حلقة for تعدادية: dayNames[i] هي التسمية، وhabit.days[i] تحدد ما إذا كان اليوم قد أُنجز.

challenge icon

التحدي

متوسط

اعرض الأسبوع:

  • اكتب عنصر واجهة DayCircle مع required وlabel وdone: دائرة أبعادها 40 × 40، بلون primary عند إتمامها وبلون surfaceContainerHighest عند عدم إتمامها، مع توسيط التسمية بخط عريض حجمه 11، بلون onPrimary أو onSurfaceVariant
  • في HabitCard، أضف أسفل الاسم وبعد مسافة مقدارها 12 بكسل صفًا Row موزعًا باستخدام spaceBetween، ويضم سبعة عناصر DayCircle

جرّب بنفسك

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),
          ],
        ),
      ),
    );
  }
}
quiz iconاختبر نفسك

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

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

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