EditText'i Klavyeden Kurtaracak Yeni Bir Widget Yazmak

Güzel bir Çarşamba gününden merhabalar. Bugün; oldukça kullanışlı ve genellikle form içeren her uygulamanın ortak derdi olan, hem klavyeden hem de farklı ekranlardan giriş yapılabilecek bir TextField görünümlü widget geliştireceğiz.



Bildiğiniz üzere Flutter'da hem Material widget'larını hem de Cupertino widget'larını rahatlıkla aynı anda kullanabiliyoruz, bu sayede iki tasarım anlayışının güçlerini birleştirdiğimiz süper esnek yeni widget'lar geliştirebiliyoruz. Bu yazıda CupertinoPicker'den giriş alma yeteneği olan ama gerektiğinde daha fazlasına da izin verebilecek bir widget yazacağız.



Demo uygulamasını buradan deneyebilirsiniz:




Entegrasyon: Kolları Sıvayalım


Bu yeni widget'ımızın adı PickableTextField olacak. Bir StatefulWidget açalım ve ilk aşamada bizim işimizi görecek standart TextField alanlarını hızlıca tanımlayalım:


Bu aşamayı detaylıca anlatmama gerek yok sanırım, açtığımız StatefulWidget'a bir controller#TextEditingController ve decoration#InputDecoration final'ları verip State objemizin sadece TextField widget'ını kurduk.


Yazıya başlarken bu widget'ın "süper esnek" olmasını ve CupertinoPicker'ı bir BottomSheet içinde açmasını bekliyoruz demiştik. Şimdi bu fonksiyonelliği bize katacak can alıcı tanımlamalarımızı yapalım.



Süper, Esnek ve Güvenli


Süper ve esnek bir widget tanımlıyorsak, bu widget'ı sadece CupertinoPicker oluşturacak ya da BottomSheet üzerinde olacak şekilde değil, ileride geliştirilebilecek farklı metodlara da kucak açabilecek şekilde geliştirmekte fayda var. Mümkün olduğunca imperative yapıdan sıyrılıp declarative bir yapı kurmaya çalışacağız.


Bu ihtiyacı karşılamak için ileride if ya da switch içinde kullanabileceğimiz bir enum tanımlaması açalım:



Örnek bir method tipi olması açısından dropdownButton'u da listeye ekledim ancak bu yazıda destek vermeyeceğimiz için o yöntemin desteklenmediğini belirttim. Güvenli geliştirmeyi bozmamak için aşağıda bu tipin seçimini engelleyecek bir doğrulama da kuracağız.


CupertinoPicker kullanıyorsak kendisinin bazı değişkenlerini de atayabileceğimiz ve sadece PickerInputMethod.cupertinoPicker seçildiğinde required olacak yeni bir modifier sınıfına ihtiyacımız var:



Bu yazıda sadece belli başlı kabiliyetleri kullanacağımız için diameterRatio#CupertinoPicker ve itemExtend#CupertinoPicker değişkenlerini dahil ettik ama dilerseniz daha fazlasını sağlayabiliriz.


Elbette bir liste kuruyorsak, bu listenin elemanlarını sunduğumuz, nasıl kurulacaklarını belirlediğimiz bir listeye ve builder Function'una ihtiyacımız var. Yukarıda hazırladığımız yeni bağımlıkları da ekleyerek widget'ın son haline bir göz atalım:


Yukarıda tanımladığımız PickerInputMethod ve CupertinoPickerOptions sınıflarımızı bağlayıp, kurulacak itemlerın listesini ve itemlerı kuracak callback fonksiyonumuzu tanımladık. Widget'ımız güvenli kullanımı ve runtime hatalarını minimize etmek için assert doğrulaması ile gerekli kontrollerimizi ekledik.


Burada dikkatinizi çekmek istediğim ek bir Function daha ekledik, itemToString. Bu callback sayesinde widget'ımız, seçilen item'ın metnini (text) bilip controller sınıfımızı gönül rahatlığıyla güncelleyebilecek. Bu nedenle eğer PickerInputMethod.cupertinoPicker methodu kullanılırsa itemToString parametresini de tanımlamamız gerekecektir.



CupertinoPicker


Tamam tamam, definitive bir widget kuruyor olduğumuz için tanımlayıcıları ve widget constructor'unu yazmayla epey vakit geçirdik. Şimdi vakit, CupertinoPicker'ımızı kurmaya geldi.


Öncelikle CupertinoPicker'ımızı görüntüleyebilmek için material showModalBottomSheet

kullanacağız, aslında iOS'ye daha yakın bir görünüm elde etmek istiyorsanız showCupertinoModalPopup da kullanılabilir ancak hem yazıyı kısa tutmak hem de daha hızlı sonuç almak için showModalBottomSheet 'ı tercih ettim. Widget'ımızın esnek kalması için build(BuildContext context) ile BottomSheet'imizi çıkaracak yapımızı ayırmakta fayda var:


Ne yaptığımıza hızlıca bakarsak:

  • Daha önceden seçilmiş item'ın TextField'e tıklandığında tekrar seçili gelmesi için controller'e işlenmiş olan metin (text) ile elimizdeki listeyi karşılaştırıp bir index bulup +1 ekliyoruz. +1'in nedeni birazdan da açıklayacak olduğum items'ı kurarken en başa boşluk atıyor oluşumuz.

  • Bir Container vasıtası ile 200 birim yükseklikte BottomSheet modal oluşturuyoruz, burada bilinmesi gereken temel nokta CupertinoPicker sınıfının bir BoxConstraits'ının olmadığıdır. Bu nedenle kendisi bağlı bulunduğu üst (parent) için bir anlam ifade etmiyor, Container yerine SizedBox.shrink kullansaydık 0 yüksekliğinde bir bottom sheet elde ederdik.

  • Padding ile CupertinoPicker'ımızın altına ve üstüne 20 birimlik boşluk atıyoruz, Container.padding de kullanılabilir.

  • CupertinoPicker'ı seçili item'ı alabileceği kendi controller'ı ve widget'a verilmiş olan CupertinoPickerOptions parametreleriyle kuruyoruz.

  • CupertinoPicker.onSelectedItem tanımlamasında, eğer seçili item değişirse TextField'in controller'ını güncelleyerek seçili elemanın hızlıca TextField'a yansımasını sağlatıyoruz.

  • CupertinoPicker.children'ı tanımlarken, kullanıcının boş değer seçebilmesini sağlamak için en tepeye bir adet boş Container atıyoruz ve items listesini eşleştirip (map) boş Container'ı ile birleştirirken tip dönüşüm hatasının çıkmaması için her bir elemanı Container ile sarıyoruz.



Can Alıcı Vuruş: TextField, CupertinoPicker'i Nasıl Çalıştıracak?


Aslında çok basit bir children sarma yöntemiyle widget'ımızın build fonksiyonunda GestureDetector kullanacağız ve TextField'ımızın enabled alanını false yapacağız:



Eğer pickerMethod'umuz PickerInputMethod.cupertinoPicker ise child'ımızı tıklamaları için dispatchCupertinoPicker()'e yönlendirdik ve EditText'in bu tıklamayı algılamaması için disabled yaptık.


Buraya kadar her şey güllük gülistanlıktı, şimdi gelelim en tartışmalı noktaya. Açıkcası benim bile iştahımı kaçıran kısma geldik. Eğer bir TextField'i enabled yapıyorsanız, Flutter material TextField'in border'larına kolay üretilemeyen bir renk atıyor, nasıl diye merak ediyorsanız sizi buraya alayım, bu nedenle disabled yaptığımız TextField'ın rengini enabled'e ayarlamak biraz zor. Bu nedenle bu widget'da biraz kendi bildiğimizi okumak zorundayız. Ve şöyle bir fonksiyon tanımlayalım:



Burada kabaca şunları yaptık:

  • Öncelikle widget'a yeni bir border#InputBorder field'ı ekledik ki bu sayede geliştirici widget'ı tanımlarken border kullanabilsin.

  • Eğer widget'da verilen decoration#InputDecoration enabledBorder'a sahipse disabled ve enabled border'larımız verilen decoration olsun.

  • Eğer hiçbir şey verilmemişse yeni border'ımızın rengi Colors.black26 olsun dedik.

Bu fonksiyonu Widget state sınıfının içindeki _decoration değişkenini üretmek için kullanacağız.


Tabii bir de hint kullanıyorsak onun da rengi disabled olacaktır, bu durumu da child'ı yeni bir Theme ile sararak çözebiliriz:




Bitirelim


Flutter'ın bize sağladığı esnekliği verimli kullandığımız sürece bu tarz hemen hemen her türlü amaca hizmet edebilecek al götür tarzı yeni widgetlar geliştirebiliriz.


Bu yazdığımız widget; henüz daha öncü konumda, daha bir çok yetenek ve method eklenebilir. Ben birkaçını sıralayayım:

  • Eğer widget'ı disabled etmek istersek ne olacağını bildirmedik, bu durumda buildInputDecoration() fonksiyonunu biraz daha geliştirmek isteyebilirsiniz. Ya da build()'in son çıktısını bir opacity ile sarabilirsiniz

  • Bu widget bir form doğrulaması sağlamıyor, bunun için TextFormField kullanılabilir.

  • Focus eventlerini işlemedik, eğer uygulamanızda inputlar için sıralı focusnode'ları kullanıyorsanız build()'de biraz daha ter dökmeniz gerekecektir.

  • Sayamayacağım kadar farklı pickerMethod'u ekleyebilirsiniz. Eğer dropdown yeteneği olmasını istiyorsanız typeahead package kullanmanızı şiddetle öneririm.

Yazıyı bitirirken, bu widgete challange bir pickerMethod'u da önereyim: Tag seçme havuzu açıp, kullanıcının TextField'e tagler ekleyebileceği çok amaçlı kullanabileceğiniz bir bottom modal sheet geliştirebilirsiniz.


Github: https://github.com/ndasim/pickable_text_field


#mobile #cross #flutter #multiplatform #widget

Komünite

Platform

Mobiler.dev Anasayfa
  • Twitter
  • Instagram
  • development_düzenlendi_düzenlendi
  • Youtube
  • slack-icon-black_edited_edited_edited
  • Gri LinkedIn Simge
imageedit_2_9667998092.png
JetBrains Hakkında Detaylı Bilgi Alın

© 2020 by mobiler.dev

Kurumsal Yazar Hesapları

adesso.png
mobilerdevLogo.jpg
Yazarlık Başvurusu Hakkında Bilgi Alın, Başvuru Yapın.
Topluluk Yazarlarını Tanıyın