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

قائمة المهام: السحب للحذف

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

على الهاتف، يمكنك مسح مهمة مكتملة بسحبها بعيدًا. ضع كل صف داخل Dismissible، وسيتولى Flutter تحريكه نيابةً عنك:

Dismissible(
  key: ObjectKey(task),
  direction: DismissDirection.endToStart,
  background: Container(color: Colors.red, ...),
  onDismissed: (direction) => _deleteTask(task),
  child: ListTile(...),
)

تُخبر key Flutter بالصف المقصود، لكي يحرّك الصف الصحيح إلى الخارج. تربط ObjectKey(task) المفتاح بالمهمة نفسها. تعني endToStart السحب إلى اليسار، ويمثّل background ما يظهر خلف الصف أثناء تحريكه: خلفية حمراء مع أيقونة حذف على اليمين.

تُنفَّذ onDismissed عندما يختفي الصف من الشاشة. مهمتك عندها هي إزالة المهمة من القائمة، داخل setState. أزل المهمة نفسها، لا موضعها، لكي تُزال المهمة الصحيحة حتى لو تغيّرت القائمة لاحقًا.

challenge icon

التحدي

متوسط

أزِل المهام بالسحب:

  • أضِف دالة _deleteTask(Task task) تزيل المهمة داخل setState
  • أحِط كل ListTile بعنصر Dismissible مع key: ObjectKey(task) يُسحب إلى اليسار
  • تكون قيمة background عبارة عن Container أحمر يحتوي على Icons.delete أبيض على اليمين، على بُعد 24 بكسل من الحافة

جرّب بنفسك

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.teal),
      ),
      home: const TodoScreen(),
    );
  }
}

class Task {
  Task(this.title);

  final String title;
  bool done = false;
}

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

  @override
  State<TodoScreen> createState() => _TodoScreenState();
}

class _TodoScreenState extends State<TodoScreen> {
  final List<Task> _tasks = [Task('Buy milk'), Task('Walk the dog'), Task('Read a book')];
  final _controller = TextEditingController();

  int get _left => _tasks.where((task) => !task.done).length;

  void _addTask() {
    final title = _controller.text.trim();
    if (title.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Type a task first')),
      );
      return;
    }
    setState(() {
      _tasks.add(Task(title));
    });
    _controller.clear();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My Tasks')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      labelText: 'New task',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                IconButton.filled(
                  onPressed: _addTask,
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Row(
              children: [
                Text(
                  _left == 1 ? '1 task left' : '$_left tasks left',
                  style: Theme.of(context).textTheme.titleSmall,
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _tasks.length,
              itemBuilder: (context, index) {
                final task = _tasks[index];
                return ListTile(
                  leading: Checkbox(
                    value: task.done,
                    onChanged: (value) {
                      setState(() {
                        task.done = value!;
                      });
                    },
                  ),
                  title: Text(
                    task.title,
                    style: task.done
                        ? const TextStyle(
                            decoration: TextDecoration.lineThrough,
                            color: Colors.grey,
                          )
                        : null,
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
quiz iconاختبر نفسك

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

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

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