Flutter İle “Toggle Selection” Örneği (Local State ve Callback Kullanımları) – Widget Communication

Birçok uygulamada bir liste içerisinden elemanları seçmemizi ve seçilenleri göstermemizi gerektiren bölümler vardır. Hatta bir uygulama içerisinde birden daha fazla kullanılması gereken durumlar olmuştur.  Bu sebeple bu yazımızın konusu ToggleSelection oldu.

Uygulamamız bittiğinde aşağıdaki gibi görünecektir.

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

  1. Ana ekranımızın tasarımını yapacağız.
  2. Müzik türlerinin listesi için model oluşturacağız.
  3. Listview” kullanarak oluşturduğumuz listeleri ekranda göstereceğiz.
  4. “ToggleSelectionItem” class’ı oluşturalım ve callback yardımıyla parent class ile haberleşmesini sağlayacağız.
  5. “Butonun tıklandı” bilgisini callback yardımıyla parent class’a aktarmasını sağlayacağız.
  6. Seçilen müzik türü sayısını göstereceğiz.
  7. Butona tıklandıkça yazının ve butonun rengini değiştireceğiz.
  8. “Continue” buton renginin seçilen item sayısına göre rengini değiştireceğiz.

İlk olarak yeni bir flutter uygulaması açalım. “Home.dart” isimli bir dosya açıp içerisine bir StatelessWidget oluşturalım:

import 'package:flutter/material.dart';
class Home extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Container();
 }
}

“Main.dart” dosyasında MaterialApp class’ının “home” property’sini “Home”  classına atayalım:

import 'package:favorite_music_selection2/home.dart';
import 'package:flutter/material.dart';
void main() {
 runApp(MyApp());
}
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
   debugShowCheckedModeBanner: false,
     home: Home(),
);}}

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

1. Ana Ekran Tasarımı

“Main.dart” dosyamızdan yönlendirmiş olduğumuz “home.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 Home extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Padding(
       padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
       child: Column(
         children: [
           Container(
             padding: EdgeInsets.only(top: 60, bottom: 10),
             child: Text(
               "Match Your Favorites Musics",
               style: TextStyle(fontSize: 20),
             ),
           ),
           Container(
               decoration: BoxDecoration(
                   color: Colors.white,
                   border: Border.all(color: Colors.lime),
                   borderRadius: BorderRadius.circular(10)),
               height: 500,
               width: 500,
               margin: EdgeInsets.all(10),
               padding:
                   EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 10),
               child: Text("Music türleri listesi")),
           Container(
             padding: EdgeInsets.all(20),
             child: Text(" Seçilen müzik türü sayısı"),
           ),
           Container(
             padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15),
             child: FlatButton(
               disabledColor: Colors.grey[200],
               shape: RoundedRectangleBorder(
                   side: BorderSide.none,
                   borderRadius: BorderRadius.circular(50)),
               color: Colors.lime,
               onPressed: () {},
               child: Text(
                 "Continue",
                 style: TextStyle(),
               ),
             ),
           ),
         ],
       ),
     ),
   );
 }
}

2. Model Oluşturma

“Lib” klasörünün içerisine “toggle_selection.dart” isimli bir dosya oluşturalım. Dosya içerisine “ToggleSelectionModel” isimli bir class oluşturalım. Modelimiz içerisine “id” ve “name” isimli iki adet String oluşturalım. Kod olarak düzenlenmiş kısmı aşağıdaki gibidir.

class ToggleSelectionModel {
 final String id;
 final String name;
 
 ToggleSelectionModel(this.id, this.name);
}

3. “Listview” İle Liste Oluşturma

Eleman sayısının değişme ihtimaline karşı örneğimizde “ListView” yerine “ListView.builder” kullanalım. 

Şimdi “toggle_selection.dart” dosyamızda “ToggleSelection” isimli StatelessWidget oluşturalım. Ve içerisine “ListView.builder” yerleştirelim. Liste verilerini; uygulama içerisinde birden çok yerde kullanma ihitmalimize karşı dışarıdan alalım ve “list” isimli bir callback oluşturalım. itemCount property’si için dışarıdan callback yardımıyla gelecek olan listemizin eleman sayısını yerleştirelim. itemBuilder property’si için ise kod kalabalıklığını önlemek adına “ToggleSelectionItem” isimli bir statelesswidget oluşturalım ve property karşılığına yazalım.

class ToggleSelection extends StatelessWidget {
 final List<ToggleSelectionModel> list;
 
 
 const ToggleSelection({Key key, this.list}) : super(key: key);
 @override
 Widget build(BuildContext context) {
   return Container(
     child: ListView.builder(
         itemCount: list.length,
         itemBuilder: (context, index) {
           return ToggleSelectionItem();
         }),
   );
 }
}
 
class ToggleSelectionItem extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Container();
 }
}

Artık bu class’ımız dışarıdan aldığı herhangi bir listeyi düzenleyebilir. Bu sebeple uygulama içerisinde farklı yerlerde kullanılabilir.

4. “ToggleSelectionItem” Class’ı Oluşturma ve Callback Kullanımı

Listemizin her bir elemanı için geçerli olacak “ToggleSelectionItem” classımızı düzenleyelim. Butonlara tıklandıkça renk değişikliği yapmak istediğimiz her işlemde ekranımızın tekrardan çizilmesi gerekmektedir. Bu sebeple bu class artık statefullwidget olmalı. Herbir liste elemanının tıklanamabilmesi için de “FlatButton” yerleştirelim.

 Artık parent(ToggleSelection) ile child(ToggleSelectionItem) widget arasında callback kullanımına geçelim. child widget içerisine “ToggleSelectionModel” tipinde bir “item” oluşturalım ve butonumuzun içerisinde yerleştirelim:

class ToggleSelectionItem extends StatefulWidget {
 final ToggleSelectionModel item;
 
 const ToggleSelectionItem({Key key, this.item}) : super(key: key);
 @override
 _ToggleSelectionItemState createState() => _ToggleSelectionItemState();
}
 
class _ToggleSelectionItemState extends State<ToggleSelectionItem> {
 @override
 Widget build(BuildContext context) {
   return Container(
     child: ButtonTheme(
       minWidth: 100,
       height: 30,
       child: FlatButton(
           color: Colors.grey[200],
           shape: RoundedRectangleBorder(
               side: BorderSide.none, borderRadius: BorderRadius.circular(50)),
           onPressed: null,
           child: Text(
             widget.item.name,
             textAlign: TextAlign.center,
             style: TextStyle(color: Colors.black),
           )),
     ),
   );
 }
}

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

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

Yapmış olduğumuz callback’lerin yapısını şema halinde inceleyecek olursak:

Burada parent class’tan child class’a bir property gönderiliyor. child class’tanda callback ile parent class’a data akışı, tıklanma bilgisi gibi olaylar geri gönderiliyor.

Bu zamana kadar yapmış oluşduklarımızı görüntülemek için home.dart dosyamızda “Home” parent classımız içerisinde “ToggleSelection” child class’tan göndermiş olduğumuz “list” isimli callback’ten faydalanalım.  “ToggleSeelctionModel” modelinde elemanlardan oluşan bir liste oluşturalım.

ToggleSelection(
               list: [
                 ToggleSelectionModel("1", "Blues"),
                 ToggleSelectionModel("2", "Hip Hop"),
                 ToggleSelectionModel("3", "Jazz"),
                 ToggleSelectionModel("4", "Alternative Metal"),
                 ToggleSelectionModel("5", "Pop"),
                 ToggleSelectionModel("6", "Dance"),
                 ToggleSelectionModel("7", "Funck"),
                 ToggleSelectionModel("8", "Rock"),
                 ToggleSelectionModel("9", "Progressive Rock"),
                 ToggleSelectionModel("10", "Alternative Rock"),
                 ToggleSelectionModel("11", "Stoner Rock"),
                 ToggleSelectionModel("12", "Classical"),
                 ToggleSelectionModel("13", "Instrumental"),
                 ToggleSelectionModel("14", "Techno"),
                 ToggleSelectionModel("15", "Progressive Trance"),
               ],
             ),

Oluşan ekran aşağıdaki gibidir:

5. Buton Tıklanma Bilgisinin Aktarımı

“ToggleSelectionItem” class’ı içerisinde “onChange” isimli bir callback oluşturalım. Ardından “ToggleSelectionItem” class’ın state’i içerisinde başlangıç değeri “false” olan bir boolen tanımlayalım:

bool _isSelected = false;

Listemizin her bir elemanı tıklandığında çalışmasını istediğimiz, FlatButton’un “onPressed” propertsine atayacak olduğumuz bir method oluşturalım:

_onItemSelect() {
   setState(() {
     _isSelected = !_isSelected;
     widget.onSelect(widget.item);
   });
 }

Burada buton tıklandıkça butonun select durumu false ise true, true ise false olarak güncellenecek. Ve onSelect fonksiyonu yardımıyla da güncel durum bilgisi parent widget’a iletilecek.

child class’ta callback ile oluşturduğumuz değeri(onSelect) parent class’ta çağırmamız gerekiyor. “onSelect” anında bir durum güncellemesi yapacağımızdan dolayı class’ımızı statefulWidget’a çevirelim.  Ve state içerisinde seçilen liste elemanlarını tutmak için bir liste oluşturalım.

 List<ToggleSelectionModel> _selectedItems = [];

“onSelect” propertisine atanacak olan bir method yazalım:

_onSelectedItem(ToggleSelectionModel item) {
   int foundItemIndex =
       _selectedItems.indexWhere((element) => element.id == item.id);
   if (foundItemIndex > -1) {
     _selectedItems.removeAt(foundItemIndex);
   } else {
     _selectedItems.add(item);
   }
 
   widget.onChange(_selectedItems);
 }

Burada bu method içerisinde ele alınan item başka bir değişle tıklanılan item, itemın id’si üzerinden “_selectedItems” listesinin içerisinde olup olmadığına bakılıyor. Eğer varsa listeden çıkarılıyor. Eğer listede yoksa listeye eklenmesi sağlanıyor. Ve “onChange” fonksiyonu yardımıyla da güncel durum bilgisi parent class’a iletilecek.

return ToggleSelectionItem(
             item: widget.list[index],
             onSelect: _onSelectedItem,
           );

Şimdi parent class’ta “onChange” metodunu çağırma işlemini gerçekleştirelim. Ilk olarak durum güncellemeleriyle sayfamızı tekrardan çizdirmemiz gerektiğinden class’ımızı statefullwidiget olarak değiştirelim. State içerisine tıklandı bilgisini, tıklanan türlerin listesini tutmak adına list oluşturalım.

class _HomeState extends State<Home> {
 List<ToggleSelectionModel> _selectedList = [];

ToggleSelection class’ından gönderilen callback’i(onChange)  parent class’ımızda çağıralım:

onChange: (List<ToggleSelectionModel> selectionList) {
                 setState(() {
                   _selectedList = selectionList;
                   print(_selectedList.length);
                 });
               },

Child class’tan bir aldığımız listeye “selectionList”  adını verelim. Ve butonlara tıklandıkça oluşturmuş olduğumuz “_selectedList” listemize ekleyelim.

6. Seçilen Müzik Türü Sayısının Gösterimi

Oluşturduğumuz“_selectedList” listemizin eleman sayısı eğer sıfırsa en az 1 adet item seçilmesi gerektiğini ekrana yazdıralım, 1 ve 1’den fazla item seçildiğinde ise seçilen item sayısını ekrana gösterelim:

Container(
             padding: EdgeInsets.all(20),
             child: _selectedList.length == 0
                 ? Text("Please select at least 3 items.")
                 : Text("Selected: ${_selectedList.length} items"),
           ),

Bu işlem aşağıdaki gibi görünmektedir.

7. Butona Tıklanmasıyla Yazının ve Butonun Renginin Değiştirilmesi

“ToggleSelectionItem” classımızda “FlatButton” içerisinde bu değişiklikleri yapalım:

FlatButton(
           color: _isSelected ? Colors.lime : Colors.grey[200],
                      child: Text(
             widget.item.name,
             style:
                 TextStyle(color: _isSelected ? Colors.white : Colors.black),
           )),

8. “Continue” Botununun Tıklanabilir Olması ve Renginin Değiştirilmesi

Seçilen item sayımız 3’den daha az ise rengini gri ve tıklanabilme özelliği kapalı, 3 ve 3’den daha fazla ise rengi yeşil ve tıklanabilir olmasını sağlayalım. Bunun için öncelikle “home” classımızda bool tipinde başlangıcı false olan bir değer atayalım:

class _HomeState extends State<Home> {
 bool _hasSelection = false;
 List<ToggleSelectionModel> _selectedList = [];

“onChange” içerisinde setState anında oluşturduğumuz “_selectedList” listemizin eleman sayısına göre “_hasSelection” durumunu güncelleyelim:

onChange: (List<ToggleSelectionModel> selectionList) {
                 setState(() {
                   _selectedList = selectionList;
                   print(_selectedList.length);
 
                   if (_selectedList.length > 2) {
                     _hasSelection = true;
                   } else {
                     _hasSelection = false;
                   }
                 });
               },

Eğer “_selectedList” listesinin eleman sayısı 2’den fazla ise _hasSelection true, değilse false olarak düzenlendi.

Şimdi “Continue” isimli butonumuzdaki değişiklikleri yapalım:

FlatButton(
               disabledColor: Colors.grey[200],
               shape: RoundedRectangleBorder(
                   side: BorderSide.none,
                   borderRadius: BorderRadius.circular(50)),
               color: _hasSelection ? Colors.lime : Colors.grey[200],
               onPressed: !_hasSelection ? null : () {},
               child: Text(
                 "Continue",
                 style: TextStyle(
                     color: _hasSelection ? Colors.white : Colors.black),
               ),
             ),

Yapmış olduğumuz uygulamamızın tüm kodlarına https://github.com/emralyilmaz/favorite_music_selection 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.