Flutter İle “ToDo List” Uygulaması Geliştirme (State ve Callback Kullanımları)


Yazı içerisinde yapacaklarımızı sırasıyla özetleyelim:

  1. Görevler için model oluşturacağız.
  2. Ana ekranımızın tasarımını yapacağız.
  3. Liste eleman sayımızı ekranda görünmesini sağlayacağız.
  4. Listview” kullanarak oluşturduğumuz listeleri ekranda göstereceğiz.
  5. Herbir liste elemanına Checkbox ve silme botunu ekleyip, callback yardımıyla state kullanarak liste üzerinde güncellemeler yapacağız.
  6. Yeni görev ekleme butonu oluşturup, callback yardımıyla yeni görev ekleyeceğiz.

İlk olarak yeni bir flutter uygulaması açalım. Simulator üzerinde çalıştırdığımızda aşağıdaki gibi bir ekranla karşılaşmamız gerekir. 


Haydi sırasıyla uygulamamızı yapmaya başlayalım.

1. Model Oluşturma

“lib” klasörü içerisine “model” isimli bir klasör açalım. Model klasörünün de içerisine “task.dart” isimli dosyada “Task” isimli bir class oluşturalım. Kod olarak düzenlenmiş kısmı aşağıdaki gibidir.

class Task {
 String taskName;
 bool isDone;
 
 Task({this.taskName, this.isDone});
}

2. Ana Ekran Tasarlama

“lib” klasörü içerisine “screen” isimli bir klasör açıp “todo_screen.dart” dosyası oluşturalım. Dosyanın içerisine de “ToDoScreen” isimli StatelessWidget bir class yaratalım. Artık “main.dart” isimli dosyamızın içeriğini silip, “myApp” class’ının içerisinde “ToDoScreen” class’ına yönlendirme yapabiliriz.

“main.dart” dosyasını inceleyecek olursak:

import 'package:flutter/material.dart';
import 'package:todo_list_app/screen.dart/todo_screen.dart';
 
void main() {
 runApp(MyApp());
}
 
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     theme: ThemeData(
       primarySwatch: Colors.lime,
     ),
     home: ToDoScreen(),
   );
 }
}

Şimdi yönlendirmiş olduğumuz “todo_screen.dart” içerisindeki tasarımımızı yapalım. Yazdığımız kod ve mobil uygulamasında oluşan görüntü şu şekildedir:

import 'package:flutter/material.dart';
 
class ToDoScreen extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text("To-Do List "),
     ),
     body: SafeArea(
         child: Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         Container(
           padding: EdgeInsets.only(top: 60, left: 30, right: 30, bottom: 30),
           child: Text("liste eleman sayısı"),
         ),
         Expanded(
           child: Container(
             decoration: BoxDecoration(
                 color: Colors.white,
                 border: Border.all(color: Colors.lime),
                 borderRadius: BorderRadius.circular(10)),
             margin: EdgeInsets.all(10),
             padding:
                 EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 70),
             height: double.minPositive,
             width: 500,
             child: Text("Yapılacaklar Listesi burada oluşturulacak."),
           ),
         )
       ],
     )),
   );
 }
}

3. Liste Eleman Sayısını Ekranda Gösterme

Ekranımız oluşturacak olduğumuz listenin eleman sayısına bağlı olarak güncellenecek olduğundan öncelikle statelessWidget olan “ToDoScreen” class’ımızı statefulWidget’a çevirelim. İlk olarak “Task” modelimizle bir liste oluşturalım adına “taskList” diyelim.

 List<Task> taskList = [
    Task(taskName: "Evi boya", isDone: false),
    Task(
      taskName: "Ödevlerini yap.",
      isDone: false,
    ),
    Task(taskName: "Sınava çalış", isDone: false)
  ];

Liste eleman sayımızı artık yazdırmamızı sağlayan kodu yazabiliriz:

           child: Text("${taskList.length} adet görev vardır.")

“taskList” içerisinde örnek olarak 3 adet eleman koyduğumuz için “3 adet görev vardır.” yazısı ile karşılaşıyoruz:

4. “Listview” İle Liste Gösterme

Bu örneğimizde “ListView” listemizin eleman sayısı dinamik bir yapıya sahip olduğundan yani eleman sayısı kullanıcının görev eklemesi ve silmesiyle değişebileceğinden “ListView.builder” ile kullanılmıştır:

 ListView.builder(
                 itemCount: taskList.length,
                 itemBuilder: (context, index) {
                   return ListTile(
                     title: Text(taskList[index].taskName),
                   );
                 }),

“ListTile” kısmını artık başka bir widget içerisine alarak sayfamızdaki kod kalabalıklarını biraz giderelim. Bu işlem için “ListTile” üzerine gelerek “Extract Widget” seçeneğine tıklarsak bizden oluşturulacak olan statelessWidget’ın ismini belirlememizi ister. “TaskListTile” olarak belirliyorum ve ardından işlem otomatik olarak gerçekleştirilmiş olur.

Şimdi gerekli düzeltmeleri yapmamız gerekiyor. İlk olarak “lib” klasörünün içinde “widget” adında bir klasör oluşturalım. Ardından “task_list_tile_widget.dart” adında bir dosya içerisine “TaskListTile” class’ını ekleyelim.

Artık parent(ToDoScreen) ile child(TaskListTile) widget arasında callback kullanımına geçelim. child widget içerisine Task tipinde bir “item” oluşturalım ve ListTile içerisinde kullanalım.

child class kodları:

import 'package:flutter/material.dart';
import 'package:todo_list_app/model/task.dart';
 
class TaskListTile extends StatelessWidget {
 final Task item;
 
 const TaskListTile({Key key, this.item}) : super(key: key);
 
 @override
 Widget build(BuildContext context) {
   return ListTile(
     title: Text(item.taskName),
   );
 }
}

child class’ta callback ile oluşturduğumuz değeri parent class’ta çağırmamız gerekiyor:

child: ListView.builder(
                 itemCount: taskList.length,
                 itemBuilder: (context, index) {
                   return TaskListTile(item: taskList[index]);
                 }),

5. “CheckBox” Kullanımı ve Silme Buton’u Oluşturma

“checkbox” oluşturalım ve görevin yapılıp yapılmadığını tıklandığında değiştirilmesini sağlayalım. Ardından sil butonu ekleyip, buton tıklandığında görevi silelim. Her iki işlem için de “TaskListTile” widget içerisinde callback function tanımlanmalı ve bu fonksiyon onChanged yada onPress’te işleme sokulmalı.

Ilk olarak child widget olan “task_list_tile_widget.dart” dosyası içerisine “onTaskStatusChange” ve “onDelete” isimli iki adet callback fonksiyon tanımlayalım.

class TaskListTile extends StatelessWidget {
 final Task item;
 final Function onTaskStatusChange;
 final Function onDelete;
 
 const TaskListTile({Key key, this.item, this.onTaskStatusChange, this.onDelete})
     : super(key: key);
  • “onTaskStatusChange”; Checkbox isimli widget iki adet property almaktadır (value, onChanged). Value property’si bir değer alması gerekir. Bu da listemizin index’nin “isDone” değeri olmalıdır. Bu değerde bool tipindedir. onChanged property’sine ise “onTaskStatusChange” fonksiyonu atanmıştır.
  • onDelete için bir IconButton oluşturulmuştur. onPressed property’si de “onDelete” fonksiyonu ile çalıştırılmaktadır.

child class kodları:

         children: [
           Checkbox(value: item.isDone, onChanged: onTaskStatusChange),
           IconButton(icon: Icon(Icons.delete), onPressed: onDelete)
         ],

Child widget içerisinde oluşturduğumuz callback fonksiyonlarımızı Parent widget olan “todo_screen.dart” dosyası içerisinde tanımlamalıyız. Bu fonksiyonlar tanımlandıktan sonra fonksiyonlarla bir ekran güncellemesi yani ekranın tekrar çizilmesi gerektiğinden “setSate” kullanılmalıdır. “setState” ile durum güncellemesi yapılarak build metodunun tekrar çalışması sağlanmaktadır. 

“onTaskStatusChange” fonksiyonunu ilk olarak ele alacak olursak bool tipinde bir değer almalıdır. Burada “val” olarak adlandıralım. “setState” metodu içerisinde de listemizin index’nin “isDone” değerini “val” değerine eşitleyelim. Bu durumda değerimiz true ise false, false ise true olarak değiştirilebilir.

“onDelete” fonksiyonu için “setState” metodu içerisinde seçili index’e göre olan silme işlemimiz gerçekleştirelim.

Bu durumda parent class kodları:

                     onTaskStatusChange: (bool val) {
                       setState(() {
                         taskList[index].isDone = val;
                       });
                     },
                     onDelete: () {
                       setState(() {
                         taskList.removeAt(index);
                       });
                     },

Aşağıdaki fotoğrafta “checkbox” ve silme özellikleri eklenmiş durumunda oluşan simulator ekranı mevcuttur.

6. Yeni Görev Ekleme

“todo_screen.dart” dosyamızda Scaffold içerisinde floatingActionButton property’sinden faydalanalım. “onPressed” ile “showModalBottomSheet” fonksiyonunu aktif edelim ve “screen” klasörümüze “add_task_screen” isimli bir dosya oluşturup o sayfaya yönlendirelim.

Oluşturduğumuz sayfada Container içerisinde başlık için bir adet Text, girilmek istenilen görev için bir adet TextField ve girilen görevi aktarmak için de bir adet FlatButton oluşturalım. FlatButton’da “Navigator.pop” ile kullanıcının tanımlamış olduğu görevi argüman olarak alıp, “todo_screen.dart” sayfasına yönlendirilmesini sağlayalım.

“AddTaskScreen” class’ımızın kodlarını inceleyelim:

import 'package:flutter/material.dart';
 
class AddTaskScreen extends StatefulWidget {
 @override
 _AddTaskScreenState createState() => _AddTaskScreenState();
}
 
class _AddTaskScreenState extends State<AddTaskScreen> {
 var textController = TextEditingController();
 @override
 Widget build(BuildContext context) {
   return Container(
     height: 200,
     padding: EdgeInsets.all(40),
     child: Column(
       children: [
         Text("Add your task"),
         TextField(
           controller: textController,
         ),
         FlatButton(
             child: Text("add"),
             onPressed: () {
               Navigator.pop(context, textController.text);
             })
       ],
     ),
   );
 }
}

“todo_screen.dart” dosyasında da oluşturulan yeni görevi “newTaskName” isminde tutalım. Burada “async- await” kullanmamızın sebebi, bu fonksiyonu çalıştırmak için kullanıcının görev eklemesini beklememizdir. “setState” yardımıyla da “taskList” listemize “add” ile eklediğimiz şekli şu şekildedir:

floatingActionButton: FloatingActionButton(
        onPressed: () async {
          String newTaskName = await showModalBottomSheet(
              context: context,
              builder: (context) {
                return AddTaskScreen();
              });
          setState(() {
            taskList.add(Task(taskName: newTaskName, isDone: false));
          });
        },
        child: Icon(Icons.add),
      ),

Yeni görev ekleme özelliğimizle birlikte uygulamamızın son hali aşağıdaki gibidir:

Yapmış olduğumuz uygulamamızın tüm kodlarına https://github.com/emralyilmaz/ninjakod-todo-list-app adresinden ulaşabilirsiniz.

Bir yazımızında sonuna gelmiş bulunuyoruz. Hoşçakalın… 🙂

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.