Menu
CoddyTech

Yapılacaklar Listesi: Silme İşlemini Geri Alma

Coddy'nin Flutter Journey'sinin Projeler bölümünün bir parçası. Ders 7 / 36.

Kaydırma hızlıdır; yanlışlıkla kaydırmak da öyle. İyi uygulamalar bunu geri almanı sağlar: altta, silinen şeyi belirten bir mesaj ve yanında bir Geri Al düğmesi görünür.

Bir SnackBar, bir action: alır; bu da bir label ve bir onPressed içeren SnackBarAction'dır. Görevi eski yerine koymak için, kaldırmadan önce konumunu hatırla:

final index = _tasks.indexOf(task);
// onu kaldır, ardından SnackBar'ı göster
SnackBarAction(
  label: 'Undo',
  onPressed: () {
    setState(() {
      _tasks.insert(index, task);
    });
  },
)

İki görevi hızla kaydırırsan ikinci mesaj, ilkinin zaman aşımına uğramasını bekler. En yeni mesajın hemen görünmesi için showSnackBar'dan önce ScaffoldMessenger.of(context).hideCurrentSnackBar(); çağır.

insert(index, task), görevi o konuma geri koyar ve ardından gelenleri aşağı kaydırır. add ise görevi sona ekler.

onPressed'e verdiğin işlev, daha sonra çalışsa bile, oluşturduğun andaki index ve task değerlerini hatırlar. Buna closure denir ve geri çağırmalar buna sürekli dayanır.

challenge icon

Görev

Orta

Silme işleminin geri alınabilmesini sağlayın. _deleteTask içinde:

  • görevi kaldırmadan önce, indexOf ile konumunu saklayın
  • görevi kaldırdıktan sonra, Deleted metnini ve görevin başlığını içeren bir SnackBar gösterin; örneğin Deleted Walk the dog
  • görevi eski konumuna geri koyan bir Undo eylemi ekleyin
  • yeni bir silme işleminin mevcut mesajın yerine geçmesi için önce gösterilmekte olan mesajı gizleyin

Kendin dene

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();
  }

  void _deleteTask(Task task) {
    setState(() {
      _tasks.remove(task);
    });
  }

  @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 Dismissible(
                  key: ObjectKey(task),
                  direction: DismissDirection.endToStart,
                  background: Container(
                    color: Colors.red,
                    alignment: Alignment.centerRight,
                    padding: const EdgeInsets.symmetric(horizontal: 24),
                    child: const Icon(Icons.delete, color: Colors.white),
                  ),
                  onDismissed: (direction) => _deleteTask(task),
                  child: 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 iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Projeler bölümündeki tüm dersler

Kendi başına pratik yap: Online Flutter derleyicisi