Flutter ile Güzel Kullanıcı Arayüzü Oluşturmak

Mustafa TÜRKMEN
Flutter Türkiye
Published in
21 min readDec 18, 2019

GİRİŞ

Google’ın geliştiriciler için açtığı ve Codelabs ismini verdiği platformda yayınlanan Codelabs eğitimlerinlerinden Building Beautiful UIs with Flutter’ın türkçe çevirisini sizlere sunmaktan büyük kıvanç duyuyorum.

1- Genel Bakış

Flutter, iOS ve Android için yüksek performanslı, yüksek kaliteli mobil uygulamalar oluşturmak için açık kaynaklı bir SDK’dir. Flutter framework, uygulamanızın görünümünü senkronize etmek ve güncellemek için gereken kod miktarını azaltırken, uygulamanızda sorunsuz tepki veren kullanıcı arayüzleri oluşturmanızı kolaylaştırır.

Flutter, zengin Materyal Tasarımı ve Cupertino (iOS) widget’ları ve davranışlarıyla güzel uygulamalar geliştirmenizi kolaylaştırır . Kullanıcılar uygulamanın doğal görünümüne ve kullanımına bayılacaklar çünkü Flutter, platforma özel scrolling, navigational kalıplar, yazı tipleri ve daha çeşitli özellikleri kullanmanıza yardımcı olur. Flutter’ın işlevsel Framework’ü içerisinde bulunan “Hot Reload” özelliği ile uygulamanızı üzerinde gerçekleştirdiğiniz değişiklikleri eş zamanlı gözlemleyerek kendinizi daha güçlü ve efektif hissedeceksiniz.

Flutter uygulamanızı Dart dili ile yazacaksınız. Dart dilinin söz dizimi (syntax’ı) eğer daha önce ilgilendiyseniz java, javascript, C#, Swift gibi dillerden size tanıdık gelecektir. Dart dili, kullanılarak geliştirilen uygulamalar mobil platformlar için gereken standart Android ve IOS toolchain’leri kullanarak derlenir, aşina olduğumuz söz dizimi (syntax), birinci sınıf fonksiyonlar, async/await, zengin kütüphaneler ve daha bir çok avantajdan dahil olmak üzere Dart dilinin özelliklerinden faydalanabilirsiniz.

Bu codelab yazısı “İlk Flutter Uygulamanızı yazın part1 ve part2”, yazılarından daha yoğun bir eğitim içermektedir. Eğer daha yumuşak bir başlangıç noktasına ihtiyacın varsa bu yazılarımızdan başlamanı tavsiye ederiz.

Neler Öğreneceğiz

+ Hem IOS hem de Android platformlarında nasıl doğal bir Flutter uygulaması yazılır?

+ Flutter uygulaması nasıl debug edilir?

+ Flutter uygulaması Emülatör/Cihazda nasıl çalıştırılır?

2- Flutter Ortamının Kurulması

Bu Lab’ı tamamlamak için 2 yazılıma ihtiyacımız var: Flutter SDK ve bir compiler. Biz bu yazımızda Android Studio’yu kullanacağız, ama siz farklı bir derleyici kullanabilirsiniz.

Bu yazımızdaki uygulamayı çalıştırabilmek için aşağıda belirtilen herhangi bir cihazı kullanabilirsiniz.

  • Geliştirici modu aktif edilmiş ve bilgisayara bağlanmış fiziksel bir cihaz (Android/IOS).
  • IOS Simülatör’ü (XCode aracı (toolkit) kurulması gerekli).
  • Android Emülatör( Android Studio’nun kurulması gerekli).

3- Yeni Bir Flutter Projesine Başlarken

“İlk Flutter Projenize başlarken” yazısındaki adımları takip ederek yeni basit bir Flutter Şablonu oluşturun. Projenizi friendlychat olarak adlandırın(myapp yerine). Projenin başından sonuna kadar düzenleyebilirsin.

İpucu: IDE’nizde seçenek olarak “Yeni Flutter Projesi”ni göremiyorsanız, Flutter ve Dart için eklentilerin kurulu olduğundan emin olun .

Kodlarda çoğunlukla Dart kodunun derlenmeye başladığı lib/main.dart dosyasını düzenleyeceğiz.

4-Ana Kullanıcı Arayüzünü Oluşturun

Bu bölümde, varsayılan uygulamayı bir sohbet uygulamasıyla değiştirmeye başlayacağız. Amaç, bu özelliklere sahip basit, genişletilebilir bir sohbet uygulaması olan FriendlyChat’i oluşturmak için Flutter’ı kullanmak.

+ Uygulama gerçek zamanlı olarak metin mesajlarını görüntüleyebilir.

+Kullanıcılar metin dizesi girebilir, geri gidebilir, ve gönder butonuna basarak metini gönderebilir.

+Arayüz hem Android hem de IOS cihazlarda çalışabilir.

IOS
IOS
Android
Android

Ana uygulamanın Scaffold’unu oluşturun

Ekleyeceğimiz ilk öge, uygulama içi statik bir başlık gösteren basit AppBar . Bu kodun sonraki bölümlerine ilerlerken uygulamaya daha fazla yanıt veren ve durum bilgisi olan Arayüz ögeleri ekleyeceğiz.

lib klasöründe bulunan main.dart dosyasının içindeki main() fonksiyonu uygulamanın başlatılmasını sağlayan fonksiyondur.

main() ve runApp() fonksiyon tanımlamaları varsayılan uygulama için aynıdır. runApp() fonsiyonu Flutter Framework’ünün çalışma süresince uygulamanın ekranında görüntülenen birwidget’ı argüman olarak alır. Uygulama kullanıcı arayüzünde Material Design ögelerini kullandığından, runApp() fonksiyonu içerisinde MaterialApp nesnesi oluşturun. Bu widget, widget ağacımızın kökü olacak.

// bu kodları main.dart dosyasının içindekilerle değiştirin.

import 'package:flutter/material.dart';

void main() {
runApp(
new MaterialApp(
title: "Friendlychat",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Friendlychat"),
),
),
),
);
}

Kullanıcıların uygulamanızda göreceği varsayılan ekranı belirlemek için MaterialApp tanımında ki home argümanını ayarlayın. home argümanı, bu uygulama için ana kullanıcı arayüzü tanımlayan bir widget’ı referans alır. Bu widget Scaffold widget’ı kullanılarak oluşturulur ve basit bir AppBar child widget’ına sahiptir.

Eğer uygulamayı çalıştırırsanız, şu şekilde görülen tekil bir ekran ile karşılaşacaksınız.

IOS
Android

Sohbet Ekranını Yapalım

Etkileşimli bileşenlerin temelini atmak için, basit uygulamayı iki farklı widget alt sınıfına ayıracağız: FriendlychatApp hiç değişmeyen kök düzeyinde bir widget ve ChatScreen iletiler gönderildiğinde ve dahili durum değiştiğinde yeniden oluşturabilecek bir alt widget. Şimdilik bu iki sınıfı StatelessWidget kullanarak genişletebiliriz. Sonra, ChatScreen sınıfını düzenleyerek genişletmek ve yönetmek için StatefullWidget’ını kullanacağız.

// bu kodları main.dart dosyasının içindekilerle değiştirin.

import 'package:flutter/material.dart';

void main() {
runApp(new FriendlychatApp());
}

class FriendlychatApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Friendlychat",
home: new ChatScreen(),
);
}
}

class ChatScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Friendlychat")),
);
}
}

Bu adımda Flutter Framework’ünün birkaç anahtar kavramını tanıyacağız:

  • Bir widget tarafından temsil edilen kullanıcı arabiriminin parçasını kendi metodunda tarif edersiniz. Framework FriendlychatApp ve ChatScreen sınıfları içerisindeki widget’ları, widget hiyerarşisine eklediğinde ve bağımlılıkları değiştiğinde build() metodunu çağırır.
  • @override etiketi yönetimin bir üst sınıfın yöntemini geçersiz kıldığını belirten ek bir dart açıklamasıdır.
  • Scaffold ve AppBar gibi bazı widget’lar, Materyal Tasarım uygulamalarına özgüdür. Text gibi diğer widget’lar ise geneldir ve herhangi bir uygulamada kullanılabilir. Flutter Framework’ündeki farklı kütüphanelerde bulunan widget’lar birbirleriyle uyumludur ve tek bir uygulamada kullanılabilir.

Değişiklikleri hemen gözlemlemek için Hot Reload düğmesine tıklayın. Kullanıcı ara birimlerini ayırdıktan sonra ve Root Widget’ı düzenledikten sonra herhangi bir değişiklik görmemeniz gerekiyor.

5. Mesaj Oluşturmak için bir Kullanıcı Arayüzü Ekleyin

Bu bölümde, kullanıcının metin mesajları girmesi ve göndermesini sağlayan bir kullanıcı denetimi oluşturmayı öğreneceksiniz.

Bir aygıtta kullanıcı metin alanına tıkladığında ekrana klavye gelir. Kullanıcı boş olmayan bir metin girip klavye üzerinde ki enter tuşuna basarak sohbet mesajları gönderebilir. Alternatif olarak kullanıcılar metin alanının yanında bulunan gönder butonuna basarak da mesajları gönderebilirler.

Şimdilik mesaj oluşturma arayüzü ekranın en üstünde, ancak sonraki adımda mesajları görüntülemek için gerekli arayüzü eklediğimiz de sohbet ekranının en altına gidecek.

Etkileşimli bir metin giriş alanı ekleyin

Flutter Framework’ü, TextField isminde bir Material Design widget’ı sunar. Girdi alanının davranışlarını özelleştirmek için gerekli özelliklere sahip, durum bilgisi olan (Değişken duruma sahip) bir widget. State, widget oluşturulduğunda senkronize okunabilen ve widget’in kullanım ömrü boyunca değişebilecek bilgilerdir. İlk state bilgisine sahip olan widget’ı FriendlyChat’e ekleyebilmek için bir kaç değişiklik yapmamız gerekli.

Flutter’da state bilgisini görsel olarak bir widget’ta göstermek istiyorsanız, bu verileri bir State nesnesine eklemelisiniz. State nenenizi daha sonra Statefull Widget’ı genişleten bir widget ile ilişkilendirmelisiniz.

Aşağıdaki Code parçacığı etkileşimli metin giriş alanını eklemek için main.dart dosyanızda bir sınıfı tanımlamaya nasıl başlayacağınızı gösteriyor. Öncelikle ChatScreen sınıfına ait StatelessWidget’ını StatfullWidget ile değiştireceğiz. TextField widget’ın içeriğini işlerken, durum bu seviyede bu widget hiyerarşisine aittir, çünkü ChatScreen bir metin denetleyici nesnesine sahip olacaktır. Ayrıca State nesnesini uygulayan yeni bir ChatScreenState sınıfı da tanımlayacaksınız. ChatScreenState sınıfını eklemek için createState () yöntemini geçersiz kılın. Durum bilgisi olan TextField widget’ını oluşturmak için yeni sınıfı kullanacaksınız. ChatScreenState sınıfını eklemek için createState () yöntemini geçersiz kılın.

Durum bilgisi olan TextField widget’ını oluşturmak için yeni sınıfı kullanacaksınız:

// Modify the ChatScreen class definition to extend StatefulWidget.

class ChatScreen extends StatefulWidget { //modified
@override //new
State createState() => new ChatScreenState(); //new
}

// Add the ChatScreenState class definition in main.dart.

class ChatScreenState extends State<ChatScreen> { //new
@override //new
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Friendlychat")),

);
}
}

Şimdi ChatScreenState için build () yöntemi önceden widget ağacının ChatScreen kısmındaki tüm widget’ları içermelidir. Framework, kullanıcı arayüzünü yenilemek için build () yöntemini çağırdığında, ChatScreenState öğesini, ağaçların çocuk widget’ları ile yeniden oluşturabilir.

İpucu: Sahnelerin arkasında neler olup bittiğini daha iyi anlamak için Flutter’un çerçeve API’lerinin kaynak kod tanımını görüntülemek genellikle yararlıdır. Bunu bir sınıf veya yöntem adı seçip ardından sağ tıklayıp Beyana Git seçeneğini seçerek IntelliJ’in editör panelinden kolayca yapabilirsiniz. İşletim sistemine bağlı olarak, klavyedeki Komut veya Kontrol düğmesine basarken de tıklayabilirsiniz. Daha fazla seçenek ve klavye kısayollarına bakın.

Artık uygulamanızın durumu yönetme özelliği olduğundan, ChatScreenState sınıfını bir giriş alanı ve gönder düğmesiyle oluşturabilirsiniz.

Metin alanıyla etkileşimleri yönetmek için, bir TextEditingController nesnesini kullanmak yardımcı olacaktır. Giriş alanının içeriğini okumak ve kısa mesaj gönderildikten sonra alanı silmek için kullanacaksınız. Bu nesneyi oluşturmak için ChatScreenState sınıf tanımına bir satır ekleyin.

// Add the following code in the ChatScreenState class definition.

class ChatScreenState extends State<ChatScreen> {
final TextEditingController _textController = new TextEditingController(); //new

Aşağıdaki kod parçacığı, yapılandırılmış bir TextField widget’ına sahip bir Container widget’ını döndüren, _buildTextComposer () adlı özel bir yöntemi nasıl tanımlayabileceğinizi gösterir.

// Add the following code in the ChatScreenState class definition.

Widget _buildTextComposer() {
return new Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
),
);
}

Ekranın kenarı ile giriş alanının her bir tarafı arasına yatay kenar boşluğu ekleyen bir Container widget’ı ile başlayın. Buradaki birimler, cihazın piksel oranına bağlı olarak belirli sayıda fiziksel piksele çevrilen mantıksal piksellerdir. İOS (puan) veya Android için (yoğunluktan bağımsız pikseller) eşdeğer terimine aşina olabilirsiniz.

Bir TextField widget’ı ekleyin ve kullanıcı etkileşimlerini yönetmek için aşağıdaki gibi yapılandırın:

— Metin alanının içeriği üzerinde kontrol sahibi olmak için, TextField yapıcısına bir TextEditingController vereceğiz. Bu denetleyici, alanı temizlemek veya değerini okumak için de kullanılabilir.
— Kullanıcı bir mesaj gönderdiğinde uyarılmak için, özel bir geri arama yöntemi _handleSubmitted () sağlamak için onSubmitted değişkenini kullanın. Şimdilik, bu yöntem sadece alanı temizleyecektir ve daha sonra mesajı göndermek için koda daha fazlasını ekleyeceğiz. Bu yöntemi aşağıdaki gibi tanımlayın:

// Add the following code in the ChatScreenState class definition.

void _handleSubmitted(String text) {
_textController.clear();
}

İpucu: Bir tanımlayıcının _ (alt çizgi) ile ön eklenmesi, kendi sınıfına özel olmasını sağlar. Dart derleyicisi gizliliği zorlar.Kütüphaneler ve dartın daha fazla görünürlüğü için dartlang.org sitesine bakın

Metin Yapıcı Widget’ı Ekleyin

Şimdi, uygulamaya metin girişi kullanıcı kontrolünü nasıl göstereceğini söyleyin. ChatScreenState sınıfınızın build () yönteminde, body özelliğine _buildTextComposer adlı özel bir yöntem ekleyin. _BuildTextComposer yöntemi, metin giriş alanını içine alan bir widget döndürecektir.

// Modify the code in the ChatScreenState class definition as follows.

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Friendlychat")),

body: _buildTextComposer(), //new
);
},

Hot reload özelliği ile uygulamayı yeniden yükleyin. Şu şekilde tekil bir ekran görmelisiniz:

IOS
Android

Responsive Gönder Butonu Ekleyin

Ardından, metin alanının sağına ‘Gönder’ düğmesini ekleyeceğiz. Düğmeyi giriş alanına bitişik görüntülemek istediğimizden, üst satır olarak bir Row widget’ı kullanacağız.

Ardından, TextField widget’ını Esnek bir widget’a sarın. Bu, Satır’a, düğme tarafından kullanılmayan kalan alanı kullanmak için metin alanını otomatik olarak boyutlandırmasını söyler.

// Modify the _buildTextComposer method with the code below to arrange the 
// text input field and send button.

Widget _buildTextComposer() {
return new Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row( //new
children: <Widget>[ //new
new Flexible( //new
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
),
), //new
], //new
), //new
);
}

Şimdi Gönder simgesini görüntüleyen bir IconButton widget’ı oluşturabilirsiniz. İcon özelliğinde, yeni bir Icon örneği oluşturmak için Icons.send sabitini kullanın. Bu sabit, widget’ınızın malzeme simgeleri kitaplığı tarafından sağlanan aşağıdaki “Gönder” simgesini kullandığını gösterir.

İpucu: Standart Malzeme Tasarımı simgelerinin bir listesi için, Malzeme Simgeleri sitesine ve Simgeler sınıfındaki sabitlere bakın.

IconButton widget’ınızı başka bir Container ebeveyn widget’ine yerleştirin; Bu, düğmenin kenar boşluğunu kişiselleştirerek giriş alanınızın yanına görsel olarak daha iyi oturmasını sağlar. OnPressed özelliği için, _handleSubmitted () yöntemini de çağırmak için adsız bir işlev kullanın ve iletinin içeriğini iletmek için _textController kullanın.

// Modify the _buildTextComposer method with the code below to define the 
// send button.

Widget _buildTextComposer() {
return new Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row(
children: <Widget>[
new Flexible(
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
),
),
new Container( //new
margin: new EdgeInsets.symmetric(horizontal: 4.0), //new
child: new IconButton( //new
icon: new Icon(Icons.send), //new
onPressed: () => _handleSubmitted(_textController.text)), //new
), //new
],
),
);
}

İpucu: Dart sözdiziminde fat arrow işlevi bildirimi => ifadesi {return expression; }.Anonim ve yuvalanmış işlevler dahil olmak üzere Dart işlevi desteğine genel bakış için Dart Dili Turu’na bakın.

Varsayılan Malzeme Tasarımı temasından düğmenin rengi siyahtır. Uygulamanızdaki simgeleri vurgu rengi olarak vermek için, renk argümanını IconButton öğesine iletin. Alternatif olarak, farklı bir tema uygulayabilirsiniz.

Simgeler renklerini, opaklıklarını ve boyutlarını, bu özellikleri tanımlamak için bir IconThemeData nesnesi kullanan bir IconTheme widget’ından devralır. Tüm widget’ları bir IconTheme widget’ındaki _buildTextComposer () yöntemine sarın ve geçerli temanın ThemeData nesnesini belirtmek için data özelliğini kullanın. Bu, düğmeye (ve widget ağacının bu bölümündeki diğer simgeler) geçerli temanın vurgu rengini verir.

// Modify the _buildTextComposer method with the code below to give the 
// send button the current theme's accent color.

Widget _buildTextComposer() {
return new IconTheme( //new
data: new IconThemeData(color: Theme.of(context).accentColor), //new
child: new Container( //modified
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row(
children: <Widget>[
new Flexible(
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: new IconButton(
icon: new Icon(Icons.send),
onPressed: () => _handleSubmitted(_textController.text)),
),
],
),
), //new
);
}

Bir BuildContext nesnesi, uygulamanızın widget ağacındaki bir widget’ın konumunu gösteren bir tanıtıcıdır. Her pencere aracının StatelessWidget.build veya State.build işlevi tarafından döndürülen pencere aracının ebeveyni olan kendi BuildContext’i vardır. Bu, _buildTextComposer () yönteminin, BuildContext nesnesine kapsüllenen State nesnesinden erişebileceği anlamına gelir; bağlamı açıkça metoda iletmeniz gerekmez.

Sıcak uygulamayı yeniden yükleyin. Buna benzeyen bir ekran görmelisiniz:

IOS
ANDROID

IntelliJ ile uygulamanızda hata ayıklayın

IntelliJ IDE, bir simülatörde / emülatörde veya bir cihazda çalışan Flutter uygulamalarında hata ayıklamanızı sağlar. IntelliJ editörü ile şunları yapabilirsiniz:

— Uygulamanızda hata ayıklamak için bir cihaz veya simülatör seçin.
— Konsol mesajlarını görüntüleyin.
— Kesme noktalarını kodunuzda ayarlayın.
— Değişkenleri inceleyin ve ifadeleri çalışma zamanında değerlendirin.

İpucu: IntelliJ kullanmaya ek olarak, Flutter uygulamanızda hata ayıklamak için kullanabileceğiniz birkaç başka araç, komut ve teknik vardır. Daha fazla bilgi için, bkz. Flutter Uygulamalarında Hata Ayıklama.

IntelliJ editörü, uygulamanız çalışırken sistem günlüğünü gösterir ve kesme noktaları ile çalışmak ve yürütme akışını kontrol etmek için bir Hata Ayıklayıcı Kullanıcı Arabirimi sağlar.

Break point ile çalışmak

Flutter uygulamasında kesme noktalarını kullanarak hata ayıklamak için:

1-Kesme noktası ayarlamak istediğiniz kaynak dosyayı açın.
2-Bir kesme noktası ayarlamak istediğiniz çizgiyi bulun, tıklayın ve ardından menüden Çalıştır> Satır Kesme Noktasını Aç’ı seçin. Alternatif olarak, bir kesme noktasını değiştirmek için oluktaki (satır numarasının sağındaki) tıklayabilirsiniz.
3-Önceden hata ayıklama modunda çalışmıyorsanız, uygulamayı durdurun.
4-Menüde Çalıştır> Hata Ayıkla’yı kullanarak uygulamayı yeniden başlatın.

IntelliJ editörü, Hata Ayıklayıcı Kullanıcı Arayüzünü başlatır ve uygulamanız kesme noktasına ulaştığında çalışmasını durdurur. Ardından hatanın nedenini belirlemek için Hata Ayıklayıcı Kullanıcı Arabirimindeki kontrolleri kullanabilirsiniz.

Friendlychat uygulamanızdaki build () yöntemlerine kesme noktaları ayarlayarak hata ayıklayıcıyı kullanmayı deneyin, ardından uygulamayı çalıştırın ve hata ayıklayın. Uygulamanız tarafından yapılan yöntem çağrısı geçmişini görmek için yığın çerçevelerini inceleyebilirsiniz.

6. Mesajları Görüntülemek için Kullanıcı Arayüzü Ekleyin

Temel uygulama Scaffold’u ve ekranı hazır olduğunda sohbet mesajlarının görüntüleneceği alanı tanımlamaya hazırsınız.

Mesaj Listesini Ekleme

Bu bölümde, kullanıcıların sohbet mesajlarını görüntüleyen bir widget oluşturacaksınız. Bunu kompozisyonu kullanarak, birden çok küçük widget oluşturarak ve birleştirerek yapacaksınız. Tek bir sohbet mesajını temsil eden bir widget ile başlayın, bu widget’i kaydırılabilir bir listeye yerleştirin ve kaydırılabilir listeyi temel uygulama Scaffold’una yerleştirin.

Öncelikle, tek bir sohbet mesajını temsil eden bir widget’a ihtiyacımız var. Aşağıdaki gibi ChatMessage adlı bir StatelessWidget tanımlayın. Build () yöntemi, mesajı gönderen kullanıcıyı, gönderenin adını içeren Row widget’ını ve mesajın metnini ve basit grafik avatarı gösteren bir Column widget’i döndürür.

// Add the following class definition to main.dart.

class ChatMessage extends StatelessWidget {
ChatMessage({this.text});
final String text;
@override
Widget build(BuildContext context) {
return new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
margin: const EdgeInsets.only(right: 16.0),
child: new CircleAvatar(child: new Text(_name[0])),
),
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(_name, style: Theme.of(context).textTheme.subhead),
new Container(
margin: const EdgeInsets.only(top: 5.0),
child: new Text(text),
),
],
),
],
),
);
}
}

Adınızı kendi adınızla değiştirerek, _name değişkenini gösterildiği gibi tanımlayın. Her sohbet mesajını gönderenin adıyla etiketlemek için bu değişkeni kullanacağız. Bu kod dosyasında basitlik için değeri zorlarsınız ancak çoğu uygulama, Flutter için Firebase Codelabi’nde gösterildiği gibi, gönderenin adını kimlik doğrulama yoluyla alır.

// Add the following code to main.dart.

const String _name = "Your Name";

CircleAvatar widget’ını kişiselleştirmek için, _name değişkeninin değerinin ilk karakterini bir child Text widget’ına geçirerek kullanıcının ilk karakteriyle etiketleyin. Avatar ve mesajları üst widget’larına göre konumlandırmak için Row yapıcısının crossAxisAlignment argümanı olarak CrossAxisAlignment.start öğesini kullanacağız.

Avatar için ebeveyn, ana ekseni yatay olan bir Row widget’tır, bu nedenle CrossAxisAlignment.start, ona dikey eksen boyunca en yüksek pozisyonu verir. Mesajlar için ebeveyn, ana ekseni dikey olan bir Sütun gerecidir, bu nedenle CrossAxisAlignment.start, metni yatay eksen boyunca en uzak soldaki konuma hizalar.

Avatarın yanında, gönderenin adını ve aşağıdaki iletinin metnini görüntülemek için iki Metin parçasını dikey olarak hizalayın. Gönderenin adını stillendirmek ve mesaj metninden daha büyük yapmak ve uygun bir ThemeData nesnesi elde etmek için Theme.of (context) kullanmanız gerekir. TextTheme özelliği, alt başlık gibi metinler için Materyal Tasarımın mantıksal stillerine erişmenizi sağlar; böylece kodlama fontlarının boyutlarını ve diğer metin niteliklerini önleyebilirsiniz.

Bu uygulama için bir tema belirlemedik, bu yüzden Theme.of (context) varsayılan Flutter temasını kullanıyor. Sonraki adımlarda, uygulamanızı Android ve iOS platformlarında farklı bir şekilde stillendirmek için bu varsayılan temayı geçersiz kılabilirsiniz.

Bir Sohbet Mesajı Listesi Ekleyin

Bir sonraki ayrıntı, sohbet mesajlarının listesini almak ve UI’da göstermek. Kullanıcıların sohbet geçmişini görüntüleyebilmeleri için bu listenin kaydırılabilir olmasını istiyoruz. Listeye mesajlar kronolojik sırayla gönderilmeli ki en son gönderilen mesaj en altta olsun.

ChatScreenState widget tanımınızda, her sohbet mesajını temsil etmek için _messages adlı bir Liste üyesi ekleyin. Her liste öğesi bir ChatMessage örneğidir. Mesaj listesini boş bir Listeye başlatmanız gerekir.

// Add the following code to the ChatScreenState class definition.

class ChatScreenState extends State<ChatScreen> {
final List<ChatMessage> _messages = <ChatMessage>[]; // new
final TextEditingController _textController = new TextEditingController();

Geçerli kullanıcı, metin alanından bir mesaj gönderdiğinde, uygulamanız yeni mesajı mesaj listesine eklemelidir. Bu davranışı uygulamak için _handleSubmitted () yönteminizi aşağıdaki gibi değiştirin.

// Modify the code in the _handleSubmitted method definition.

void _handleSubmitted(String text) {
_textController.clear();
ChatMessage message = new ChatMessage( //new
text: text, //new
); //new
setState(() { //new
_messages.insert(0, message); //new
}); //new
}

_Messages öğesini değiştirmek ve Framework’e, widget ağacının bu bölümünün değiştiğini ve Framework’ün kullanıcı arayüzünü yeniden oluşturması gerektiğini bildirmek için setState () öğesini çağırırsınız. SetState () işlevinde yalnızca eşzamanlı işlemler gerçekleştirilmelidir, aksi takdirde framework işlemi tamamlanmadan önce pencere öğelerini oluşturulabilir.

Genel olarak, bu yöntem çağrısının dışında bazı özel veriler değiştikten sonra boş bir kapatma ile setState () yöntemini çağırmak mümkündür. Ancak, setState () ‘in kapanışı içindeki verilerin güncellenmesi tercih edilir, dolayısıyla daha sonra çağırmayı unutmayın.

Mesaj Listesini Yerleştirin.

Artık sohbet mesajlarının listesini görüntülemeye hazırsınız. ChatMessage widget’larını _messages listesinden alacağız ve kaydırılabilir bir liste için ListView widget’ına koyacağız.

ChatScreenState sınıfınızın build () yönteminde, mesaj listesi için bir ListView widget’ı ekleyin. ListView.builder yapıcısını seçiyoruz, çünkü varsayılan yapıcı kendi alt değişkenlerinin mutasyonlarını otomatik olarak algılamıyor.

// Modify the code in the ChatScreenState class definition as follows.

Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Friendlychat")),
body: new Column( //modified
children: <Widget>[ //new
new Flexible( //new
child: new ListView.builder( //new
padding: new EdgeInsets.all(8.0), //new
reverse: true, //new
itemBuilder: (_, int index) => _messages[index], //new
itemCount: _messages.length, //new
), //new
), //new
new Divider(height: 1.0), //new
new Container( //new
decoration: new BoxDecoration(
color: Theme.of(context).cardColor), //new
child: _buildTextComposer(), //modified
), //new
], //new
), //new
);
}

Scaffold widget’ının body özelliği şimdi gelen iletilerin yanı sıra giriş alanı ve gönder düğmesini içerir. Aşağıdaki katman widget’larını kullanıyoruz:

— Divider, doğrudan çocuklarını dikey olarak düzenler. Sütun bir kaydırma listesi ve bir giriş alanı için bir satır olacak olan birden fazla alt öge alabilir.
— Flexible, ListView’ın ebeveyni. Bu, Framework’e, alınan mesajların listesinin, Sütun yüksekliğini doldurmak için genişlemesine izin verirken, TextField sabit bir boyutta kalmasını söyler.
Divider, mesajları görüntülemek için UI ile mesajları oluşturmak için metin girişi alanı arasında yatay bir kural çizer.
— Container, arka plan görüntülerini, dolguyu, kenar boşluklarını ve diğer genel katman ayrıntılarını tanımlamak için yararlı olan Text yapıcının ebeveyni olarak kullanılır. Arka plan rengini tanımlayan yeni bir BoxDecoration nesnesi oluşturmak için dekorasyonu kullanın. Bu durumda, varsayılan temanın ThemeData nesnesi tarafından tanımlanan cardColor kullanıyoruz. Bu, mesaj oluşturmak için kullanıcı arayüzüne mesaj listesinden farklı bir arka plan verir.

Liste içeriğini ve görünümünü özelleştirmek için argümanları ListView.builder yapıcısına iletin:

Padding, mesaj metninin etrafındaki beyaz boşluk için
Reverse, ListView’i ekranın alt kısmından başlatmak için
ItemCount, Listedeki mesaj sayısını belirlemek için
itemBuilder, [index] ‘de her widget’ı oluşturan bir işlev için. Mevcut derleme içeriğine ihtiyacımız olmadığından, IndexedWidgetBuilder’ın ilk argümanını görmezden gelebiliriz. Argümanın adlandırılması _ (alt çizgi), kullanılmayacağını gösteren bir kuraldır.

Hot reload özelliğini kullandığınızda; Aşağıdaki gibi görünen tek bir ekran görmelisiniz:

IOS
ANDROID

Şimdi, Görüntüleme ve oluşturma için yaptığımız kullanıcı arayüzünü kullanarak birkaç mesaj göndermeyi deneyin.

IOS
ANDROID

7. Uygulamanızı Canlandırın

Uygulamanızın kullanıcı deneyimini daha akıcı ve sezgisel hale getirmek için widget’larınıza animasyon efektleri ekleyebilirsiniz. Bu bölümde, sohbet mesajı listenize nasıl basit bir animasyon efekti ekleyeceğinizi gözden geçireceğiz.

Kullanıcı yeni bir mesaj gönderdiğinde, onu yalnızca mesaj listesinde görüntülemek yerine, listenin en altından dikey olarak kolaylaştıracak mesajı canlandıracağız.

Flutter’daki animasyonlar, yazılı bir değer ve durum içeren (ileri, geri, tamamlandı ve reddedildi gibi) Animasyon nesneleri olarak kapsüllenir. Bir widget’a bir animasyon nesnesi ekleyebilir veya animasyon nesnesindeki değişiklikleri dinleyebilirsiniz. Animasyon nesnesinin özelliklerinde yapılan değişikliklere bağlı olarak, Framework widget’ın görünme şeklini değiştirebilir ve widget ağacını yeniden oluşturabilir.

Bir animasyon denetleyicisi belirtin

Animasyonun nasıl çalışması gerektiğini belirlemek için AnimationController sınıfını kullanın. AnimationController sınıfı, animasyonun süresi ve oynatma yönü (ileri veya geri) gibi önemli özelliklerini tanımlamanıza izin verir.

Bir AnimationController oluştururken, bir vsync argümanını iletmelisiniz. Vsync, ekran dışı animasyonların gereksiz kaynak tüketmesini önler. ChatScreenState’inizi vsync olarak kullanmak için, ChatScreenState sınıf tanımına bir TickerProviderStateMixin karışımı ekleyin.

İpucu: Dart’da bir karışım, bir sınıf gövdesinin birden fazla sınıf hiyerarşisinde yeniden kullanılmasına izin verir. Daha fazla bilgi için, bkz. Sınıflar, Dart Dil Turu.

// Modify the code in the ChatScreenState class definition as follows.

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin { // modified
final List<ChatMessage> _messages = <ChatMessage>[];
final TextEditingController _textController = new TextEditingController();

ChatMessage sınıf tanımında, animasyon denetleyicisini depolamak için bir üye değişkeni ekleyin.

// Modify the ChatMessage class definition as follows.

class ChatMessage extends StatelessWidget {
ChatMessage({this.text, this.animationController}); //modified
final String text;
final AnimationController animationController; //new

ChatScreenState sınıfınızdaki _handleSubmitted () yöntemini aşağıdaki gibi değiştirin. Bu yöntemde, bir AnimationController nesnesini somutlaştırın ve animasyonun çalışma süresini 700 milisaniye olarak belirtin. (Animasyon efektini yavaşlatmak için bu daha uzun süreyi seçtik, böylece geçişin daha kademeli olarak gerçekleştiğini görebilirsiniz; pratikte uygulamanızı çalıştırırken muhtemelen daha kısa bir süre seçip yavaş modunu devre dışı bırakmak isteyeceksiniz.)

Animasyon denetleyicisini yeni bir ChatMessage örneğine ekleyin ve sohbet listesine her yeni mesaj eklendiğinde animasyonun oynatılması gerektiğini belirtin.

// Modify the _handleSubmittted method definition as follows.

void _handleSubmitted(String text) {
_textController.clear();
ChatMessage message = new ChatMessage(
text: text,
animationController: new AnimationController( //new
duration: new Duration(milliseconds: 700), //new
vsync: this, //new
), //new
); //new
setState(() {
_messages.insert(0, message);
});
message.animationController.forward(); //new
}

SizeTransition widget’ı ekleme

ChatMessage nesnesinin build () yöntemini, daha önce tanımladığımız Container alt pencere aracını saran bir SizeTransition pencere aracını döndürmek için değiştirin. SizeTransition sınıfı, çocuğunun genişliğinin veya yüksekliğinin belirli bir boyut faktörü değeri ile çarpıldığı bir animasyon efekti sağlar.

CurvedAnimation nesnesi, SizeTransition sınıfıyla birlikte, kolay bir animasyon efekti oluşturur. Kolaylaştırma efekti, mesajın animasyonun başında hızlı bir şekilde kaymasına ve durma noktasına gelene kadar yavaşlamasına neden olur.

// Modify the build() method for the ChatMessage class as follows.

class ChatMessage extends StatelessWidget {
ChatMessage({this.text, this.animationController});
final String text;
final AnimationController animationController;
@override
Widget build(BuildContext context) {
return new SizeTransition( //new
sizeFactor: new CurvedAnimation( //new
parent: animationController, curve: Curves.easeOut), //new
axisAlignment: 0.0, //new
child: new Container( //modified
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
margin: const EdgeInsets.only(right: 16.0),
child: new CircleAvatar(child: new Text(_name[0])),
),
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(_name, style: Theme.of(context).textTheme.subhead),
new Container(
margin: const EdgeInsets.only(top: 5.0),
child: new Text(text),
),
],
),
],
),
) //new
);
}
}

Animasyonu elden çıkar

Artık gerekli olmadığında kaynaklarınızı boşaltmak için animasyon denetleyicilerinizi elden çıkarmak iyi bir çözümdür. Aşağıdaki kod parçacığı, ChatScreenState öğesinde dispose () yöntemini geçersiz kılarak bu işlemi nasıl uygulayabileceğinizi gösterir. Geçerli uygulamada, uygulama yalnızca tek bir ekrana sahip olduğundan, Framework dispose () yöntemini çağırmaz. Birden çok ekranlı daha karmaşık bir uygulamada, ChatScreenState nesnesi artık kullanılmadığında framework yöntemi çağırır.

// Add the following code to the ChatScreenState class definition.

@override
void dispose() { //new
for (ChatMessage message in _messages) //new
message.animationController.dispose(); //new
super.dispose(); //new
}

Animasyon efektini görmek için uygulamanızı yeniden başlatın ve birkaç mesaj girin. Hot reload yerine yeniden başlatmanın kullanılması, bir animasyon denetleyicisi olmayan mevcut mesajları siler.

Animasyonlarla daha fazla deneme yapmak istiyorsanız, denemek için birkaç fikir:

— _HandleSubmitted () yönteminde belirtilen süre değerini değiştirerek animasyon efektini hızlandırın veya yavaşlatın.
— Curves sınıfında tanımlanan sabitleri kullanarak farklı animasyon eğrileri belirtin.
— Container’i SizeTransition yerine FadeTransition widget’ına sararak bir solma animasyonu efekti oluşturun.

8. Son dokunuşları uygulayın

Bu isteğe bağlı adımda, uygulamanıza yalnızca Gönderilecek metni yalnızca metin varken etkinleştirmek, daha uzun mesajlar sarmak ve iOS ve Android için yerel görünümlü özelleştirmeler eklemek gibi birkaç karmaşık ayrıntı sunacaksınız.

Gönder düğmesini Context’e duyarlı hale getir

Şu anda, giriş alanı metin olmasa bile Gönder düğmesi etkin görünüyor. Alanın gönderilecek metni içerip içermediğine bağlı olarak düğmenin görünümünün değişmesini isteyebilirsiniz.

Kullanıcı giriş alanına yazarken özel bir üye değişkeni olan _isComposing öğesini tanımlayın.

// Add the following code in the ChatScreenState class definition.

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
final List<ChatMessage> _messages = <ChatMessage>[];
final TextEditingController _textController = new TextEditingController();
bool _isComposing = false;

Kullanıcı alanla etkileşime girdiğinde metindeki değişikliklerden haberdar olmak için, onChanged geri aramasını TextField constructor’ına iletin. TextField, değeri alanın geçerli değeriyle değiştiğinde bu yöntemi çağırır. OnChanged geri çağrınızda, alan bir metin içerdiğinde _isComposing değerini true olarak değiştirmek için setState () öğesini çağırın.

Ardından, _isComposing yanlış olduğunda onPressed argümanını null olacak şekilde değiştirin.

// Modify the _buildTextComposer method with the code below
// to add the onChanged() and onPressed() callbacks.

Widget _buildTextComposer() {
return new IconTheme(
data: new IconThemeData(color: Theme.of(context).accentColor),
child: new Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row(
children: <Widget>[
new Flexible(
child: new TextField(
controller: _textController,
onChanged: (String text) { //new
setState(() { //new
_isComposing = text.length > 0; //new
}); //new
}, //new
onSubmitted: _handleSubmitted,
decoration:
new InputDecoration.collapsed(hintText: "Send a message"),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: new IconButton(
icon: new Icon(Icons.send),
onPressed: _isComposing
? () => _handleSubmitted(_textController.text) //modified
: null, //modified
),
),
],
),
),
);
}

Metin alanı silindiğinde _isComposing değerini false olarak güncellemek için _handleSubmitted’yi değiştirin.

// Modify the _handleSubmittted method definition as follows.

void _handleSubmitted(String text) {
_textController.clear();
setState(() { //new
_isComposing = false; //new
}); //new
ChatMessage message = new ChatMessage(
text: text,
animationController: new AnimationController(
duration: new Duration(milliseconds: 700),
vsync: this,
),
);
setState(() {
_messages.insert(0, message);
});
message.animationController.forward();
}

_isComposing değişkeni şimdi davranışı ve Gönder düğmesinin görsel görünümünü kontrol eder.

— Kullanıcı metin alanına bir dize yazarsa, _isComposing true olur ve düğmenin rengi Theme.of (context) .accentColor olarak ayarlanır. Kullanıcı düğmeye bastığında, sistem _handleSubmitted () öğesini çağırır.
— Kullanıcı metin alanına hiçbir şey yazmazsa, _isComposing yanlıştır ve widget’ın onPressed özelliği null değerine ayarlanır ve gönder düğmesini devre dışı bırakır. Framework, düğmenin rengini otomatik olarak Theme.of (context) .disabledColor olarak değiştirir.

Daha uzun satırları kaydır

Bir kullanıcı, mesajları görüntülemek için kullanıcı arayüzünün genişliğini aşan bir metin gönderdiğinde, satırlar, mesajın tamamı görüntülenecek şekilde sarılmalıdır. Şu anda, taşan çizgiler kesiliyor ve bir hata mesajı görüntüleniyor. Metnin doğru bir şekilde sarıldığından emin olmanın basit bir yolu, bir Expanded Widget eklemektir.

Bu adımda, Mesajların Expanded widget’ta görüntülendiği Column widget’ini saracaksınız. Expanded , Column gibi bir Widget aracının, bir alt pencere aracında katman kısıtlamaları (bu durumda, Sütun genişliği) dayamasına izin verir. Burada normal olarak içeriği tarafından belirlenen Text widget aracının genişliğini sınırlar.

Widget hiyerarşisinin bu kısmı, ChatMessage sınıfının build () yöntemiyle tanımlanır. Bir ana pencere öğesi eklemek için birkaç kullanışlı IntelliJ kısayolu deneyeceğiz:

  1. İmleci yeni Column ifadesine yerleştirin.
  2. Sol kenardaki ampul simgesini tıklayın ve açılan menüden Wrap with new widget’ı seçin. IntelliJ, özelleştirmeniz için doğru biçimlendirilmiş genel yeni bir widget ifadesi ekler. Expression ve nesting widget’larını eklemenin klavye kısayolu, option+ return (macOS) veya alt + enter (Linux, Windows) kullandığınızda daha da hızlıdır.
  3. Vurgulanan widget anahtar kelimesinin üzerinde imleç varken, akıllı kod tamamlama için tuş bileşimine basın. Bakmanız gerekirse, IntelliJ IDEA referansına bakın.
  4. Column’un parent’ı olabilecek olası nesneler listesinden Expand seçeneğini belirleyin. Listedeki ilk öğe olmalı.

Aşağıdaki kod parçacığı ChatMessage sınıfının değiştirildikten sonra nasıl görüneciğini gösteriyor.

//Modify the ChatMessage class definition in main.dart.

...

new Expanded( //new
child: new Column( //modified
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(_name, style: Theme.of(context).textTheme.subhead),
new Container(
margin: const EdgeInsets.only(top: 5.0),
child: new Text(text),
),
],
),
), //new

...

Android ve IOS için özelleştirin

Uygulamanızın kullanıcı arayüzüne doğal bir görünüm ve his vermek için, FriendlychatApp sınıfı için build () yöntemine bir tema ve basit bir mantık ekleyebilirsiniz. Bu adımda, farklı birincil ve vurgulu renkler uygulayan bir platform teması tanımlarsınız. Ayrıca, iOS’ta bir CupertinoButton ve Android’de Material Design IconButton kullanmak için Gönder düğmesini özelleştirebilirsiniz.

IOS
Android

Öncelikle, iOS için renklerle kIOSTheme adlı yeni bir ThemeData nesnesi (turuncu aksanla açık gri) ve Android için renklerle (turuncu aksan ile mor) başka bir ThemeData nesnesi kDefaultTheme tanımlayın.

// Add the following code to main.dart.

final ThemeData kIOSTheme = new ThemeData(
primarySwatch: Colors.orange,
primaryColor: Colors.grey[100],
primaryColorBrightness: Brightness.light,
);

final ThemeData kDefaultTheme = new ThemeData(
primarySwatch: Colors.purple,
accentColor: Colors.orangeAccent[400],
);

Uygulamanızın MaterialApp widget’ının theme özelliğini kullanarak temayı değiştirmek için FriendlychatApp sınıfını değiştirin. Bir tema seçmek üzere bir ifade oluşturmak için üst düzey defaultTargetPlatform özelliğini ve koşullu işleçleri kullanın.

// Add the following code to main.dart.

import 'package:flutter/foundation.dart'; //new

// Modify the FriendlychatApp class definition in main.dart.

class FriendlychatApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Friendlychat",
theme: defaultTargetPlatform == TargetPlatform.iOS //new
? kIOSTheme //new
: kDefaultTheme, //new
home: new ChatScreen(),
);
}
}

Seçilen temayı AppBar widget’ına uygulayabiliriz (uygulamanızın kullanıcı arayüzünün üstündeki başlık). Yükseklik özelliği, AppBar’ın z koordinatlarını tanımlar. 0.0 değerinde bir z koordinat değeri gölge içermez (IOS) ve 4.0 değerinde tanımlanmış bir gölge (Android) bulunur.

// Modify the build() method of the ChatScreenState class.

Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Friendlychat"), //modified
elevation:
Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0, //new
),

_BuildTextComposer yönteminde, Konteyner üst gerecini değiştirerek Gönder simgesini özelleştirin. Bir düğme seçmek üzere bir ifade oluşturmak için child özelliğini ve koşullu operatörleri kullanın.

// Add the following code to main.dart.

import 'package:flutter/cupertino.dart'; //new

// Modify the _buildTextComposer method.

new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: Theme.of(context).platform == TargetPlatform.iOS ? //modified
new CupertinoButton( //new
child: new Text("Send"), //new
onPressed: _isComposing //new
? () => _handleSubmitted(_textController.text) //new
: null,) : //new
new IconButton( //modified
icon: new Icon(Icons.send),
onPressed: _isComposing ?
() => _handleSubmitted(_textController.text) : null,
)
),

Üst düzey column’un, üst kenarına açık gri bir kenarlık vermek için bir Container widget’a sarın. Bu sınır, IOS’ta uygulama çubuğunu görsel olarak uygulamanın gövdesinden ayırt etmeye yardımcı olacaktır. Sınırı Android’de gizlemek için önceki snippet’te uygulama çubuğu için kullanılan aynı mantığı uygulayın.

// Modify the following lines in main.dart.

Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Friendlychat"),
elevation:
Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0),
body: new Container( //modified
child: new Column( //modified
children: <Widget>[
new Flexible(
child: new ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
),
),
new Divider(height: 1.0),
new Container(
decoration: new BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTextComposer(),
),
],
),
decoration: Theme.of(context).platform == TargetPlatform.iOS //new
? new BoxDecoration( //new
border: new Border( //new
top: new BorderSide(color: Colors.grey[200]), //new
), //new
) //new
: null), //modified
);
}

Uygulamayı hot reload ile yeniden yükleyin, uygulamanın IOS ve Android için farklı renk, gölge ve ikon butonlarına sahip olduğunu göreceksiniz.

Tebrikler

Artık Flutter Framework ile platformlar arası mobil uygulamalar oluşturmanın temellerini biliyorsunuz.

Uygulamanın koduna erişmek için:

git clone https://github.com/flutter/friendlychat-steps.git

--

--

Mustafa TÜRKMEN
Flutter Türkiye

Bir Mühendis adayı. Bazen kendini dağlara vurmak isteyen bir deli. Hayata muhalif bir genç