Stack widget
Stack widget
widget'ları üst üste yerleştirmek (veya yığmak) için kullanılır.
Yukarıdaki resimde Güneş, bulutlar ve içerik, birbirinin üzerine yığılmış farklı Widget'lardır. Bunu Flutter'da stack widget kullanarak yaparız.
Stack widget alt widget'larını sol üst köşelerine göre hizalar ve bunları birbiri ardına yan yana yerleştirir. Alignment
özelliği ile bir stack'e hangi yöne hizalanacağını söyleyebilirsiniz. Örneğin, hizalamayı horizontal
(yatay olarak) ayarlarsanız, stack bir row gibi davranacaktır. Stack widget default olarak column gibi çalışır ve çocukları dikey olarak yerleştirir.
Bir widget'ı konumlandırmak için onu Positioned
widget'ına sararsınız.
Positioned
widget şu özelliklere sahiptir: top
, left
, right
, bottom
, width
, ve height
. Bu özelliklerin hiçbirini ayarlamanız gerekmez, ancak en fazla iki yatay özellik(horizontal properties) (left
, right
, ve width
) ve iki dikey özellik (vertical properties) (top
, bottom
, and height
) ayarlayabilirsiniz. Bu özellikler, Flutter'a widget'ın nereye boyanacağını söyler. Çocuklar, RenderStack
algoritması tarafından boyanır:
RenderStack
algoritması
• Konumlandırılmamış tüm çocukları bir row veya column'un yapacağı şekilde düzenler. Bu, stack'e son boyutunu söyler. Konumlandırılmamış çocuk yoksa stack mümkün olduğunca büyük olmaya çalışır.
•top
, left
vb özelliklerini kullanarak konumlandırılmış tüm alt öğelerini (children) stack'in render box'ına göre düzenler. Konumlandırılmış özellikler(positioned properties), Flutter'a stack'in alt öğelerini paralel kenarına göre nereye yerleştireceğini söyler.
Örneğin, top: 10.0, positioned widget'ı stack box'ının üst kenarından 10.0 piksel içe yerleştirir.
Stack
Widget'ları birbirinin üzerine veya açık bir şekilde birbiriyle ilişkili olarak yerleştirmek istiyorsanız, kullanabileceğiniz bir widget'tır.
Table widget
Adından da anlaşılacağı üzere tablo oluşturmak için kullanılır.
Table
gördüğümüz diğer layout widget'larından daha katıdır, çünkü tabloların (teoride) tek bir amacı vardır: verileri okunabilir bir şekilde görüntülemek. Tablolar, widget öğelerini sütunlar ve satırlar halinde sıralar ve tablodaki her hücre, satırdaki diğer tüm hücrelerle aynı yüksekliğe ve sütunundaki her widget'la aynı genişliğe sahiptir. Flutter tablolarında sutunlara genişlik vermek gereklidir ve hiçbir tablo hücresi boş olamaz.
Table(
columnWidths: Map<int, TableColumnWidth>{},
border: Border(),
defaultColumnWidth: TableColumnWidth(),
defaultVerticalAlignment:
TableCellVerticalAlignment(),
children: List<TableRow>[]
);
Table widget ile çalışırken dikkat etmeniz gerekenler:
columnWidths
kullanmak zorunda değilsiniz ama defaultColumnWidth
parametresi null
olamaz.
defaultColumnWidth
default bir argumana sahiptir: FlexColumnWidth(1.0)
, yani hiçbir şey iletmeniz gerekmez ancak bu değer null
olamaz.
defaulColumnWidth: null
olması durumunda hata verir. Ancak defaultColumnWidth'in default bir argumanı olduğundan, her sütunun aynı boyutta olmasını ve tablonun mümkün olduğunca fazla genişlikte olmasını istiyorsanız bunu yok sayabilirsiniz.
Column width'leri, bir map'i columnWidths
'e ileterek tanımlanır. Map, key olarak column'un index değerini(0'dan başlayarak) ve column'a vermek istediğiniz width değerini alır.
Border
isteğe bağlıdır.
TableCellVerticalAlignment
, yalnızca row'larınızın children'ları TableCells
ise çalışır.
Aşağıdaki kod, bazı satırların boyutlarını tanımlar. 1. column'un genişliği için bir tanım olmadığına dikkat edin!
Table(
columnWidths: {
0: FixedColumnWidth(100.0),
2: FixedColumnWidth(20.0),
3: FixedColumnWidth(20.0),
},
defaultVerticalAlignment:
TableCellVerticalAlignment.middle,
children: <TableRow>[...],
);
TableRow
: Bir table row, normal bir row'dan daha basittir ve iki önemli yapılandırması vardır:
• Bir tablodaki her row
'un eşit sayıda çocuğu olmalıdır.
• Çocukların alt widget ağaçlarında (sub-widget tree) TableCell
'i kullanabilirsiniz, ancak kullanmak zorunda değilsiniz. TableCell
, widget ağacında onun üzerinde bir yerde, ata olarak(ancestor) bir TableRow
'a sahip olduğu sürece, TableRow
'un doğrudan bir çocuğu olmak zorunda değildir.
Dart'ın List.generate() constructor'ından widget'lar oluşturma
Tablonun child özelliğine bir liste iletmek yerine, widget'ları döndüren fonksiyonları, constructor'ları ve class'ları kullanabiliriz.
Table(
columnWidths: {
0: FixedColumnWidth(100.0),
2: FixedColumnWidth(20.0),
3: FixedColumnWidth(20.0),
},
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: List.generate(7, (int index) {
ForecastDay day = forecast.days[index];
Weather dailyWeather =
forecast.days[index].hourlyWeather[0];
final weatherIcon =
_getWeatherIcon(dailyWeather);
return TableRow(
children: [
// ....
],
); // TableRow
});
); // Table
Burada ki List.generate constructor
fonksiyonu, derleme zamanında yürütülür. List.generate
'i bir döngü olarak düşünebilirsiniz. İşlevsel olarak şöyle bir şey yazmakla aynıdır:
List<Widget> myList = [];
for (int i = 0; i < 7; i++) {
myList.add(TableRow(...));
}
Tıpkı for döngüsü gibi, örnek koddaki List.generate constructor'ı , verdiğiniz kodu yedi kez çalıştıracaktır. (Yine de her döngü yinelemesindeki index'in aslında 0-6 arasında olacağını unutmamak önemlidir.)
List.generate
bir Dart özelliğidir ve Flutter'a özgü değildir. Yine de bir row, column, table, veya list için birkaç widget oluşturmanız gerektiğinde Flutter'da oldukça kullanışlıdır.
List.generate'i kullanmasaydık, şuna benzeyen daha ayrıntılı bir kod yazmamız gerekirdi:
Table (
children: [
TableRow(
children: [
TableCell(),
TableCell(),
TableCell(),
TableCell(),
]
),
TableRow(
children: [
TableCell(),
TableCell(),
TableCell(),
TableCell(),
]
),
]
)
TableCell
, Text
, Icon
ve Padding
'in tümü kullanılır.
children: List.generate(7, (int index) {
ForecastDay day = forecast.days[index];
Weather dailyWeather = forecast.days[index].hourlyWeather[0];
final weatherIcon = _getWeatherIcon(dailyWeather);
return TableRow(
children: [
TableCell(
child: const Padding(
padding: const EdgeInsets.all(4.0),
child: ColorTransitionText(
text: DateUtils.weekdays[dailyWeather.dateTime.weekday],
style: textStyle,
animation: textColorTween.animate(controller),
),
),
),
TableCell(
child: ColorTransitionIcon(
icon: weatherIcon,
animation: textColorTween.animate(controller),
size: 16.0,
),
),
TableCell(
child: ColorTransitionText(
text: _temperature(day.max).toString(),
style: textStyle,
animation: textColorTween.animate(controller),
),
),
TableCell(
child: ColorTransitionText(
text: _temperature(day.min).toString(),
style: textStyle,
animation: textColorTween.animate(controller),
),
),
],
);
}),
// ...
VerticalDirection.up
Sütunun default akışını tersine çevirmek için kullanılır.
TabBar widget
Tablar (sekmeler), mobil uygulamalarda yaygın olarak kullanılan bir UI öğesidir. Flutter Material library, tab'larla çalışmayı oldukça kolaylaştıran yerleşik tab widget
'ları sağlar. Yerleşik TabBar
widget'ı, alt öğelerini(children) yatay(horizontal) olarak kaydırılabilir bir şekilde görüntüler ve onları "dokunulabilir" (tappable) hale getirir. Tablar en çok, gerçekte gezinmeden farklı sayfalar veya UI componentleri arasında geçiş yapmak için kullanılır. Bu nedenle, tab bar'ın (sekme çubuğunun) alt widget öğelerine iletilen geri arama (callback), en yaygın olarak sayfadaki widget öğelerini değiştirmek için kullanılır.
Aşağıdaki şekil, tabların arkasındaki temel fikri temsil eder. Tab bardaki bir öğeye tıkladığınızda, ilgili tab içeriği değişir.
TabBar widget'ının iki önemli parçası vardır: çocukların kendileri
(kullanıcının seçmek istediği widget'lar) ve TabController
- (işlevselliği yöneten).
TabController widget
Flutter'da, etkileşim içeren birçok widget, olayları yönetmek için ilgili controller'lara(denetleyicilere) sahiptir.
Örneğin, widget'larla birlikte kullanılan ve kullanıcıların girdi yazmasına izin veren bir TextEditingController
vardır.
Controller, yeni bir sekme seçildiğinde Flutter uygulamasına bildirimde bulunmaktan sorumludur, böylece uygulamanız istenen içeriği görüntülemek üzere sekmeyi güncelleyebilir. Controller, ağaçta tab bar'dan daha yüksekte oluşturulur ve ardından TabBar
widget'ına iletilir. Bu mimari, tab bar'ın parent'ı (üst öğesi) aynı zamanda tab widget'larının parent'ı olduğundan gereklidir.
// Full TimePickerRow widget
class TimePickerRow extends StatefulWidget {
final List<String> tabItems;
final ForecastController forecastController;
final Function onTabChange;
final int startIndex;
const TimePickerRow({
Key key,
this.forecastController,
this.tabItems,
this.onTabChange,
this.startIndex,
}) : super(key: key);
@override
_TimePickerRowState createState() => _TimePickerRowState();
}
class _TimePickerRowState extends State<TimePickerRow>
with SingleTickerProviderStateMixin {
TabController _tabController;
int activeTabIndex;
@override
void initState() {
_tabController = TabController(
length: utils.hours.length,
vsync: this,
initialIndex: widget.startIndex,
);
_tabController.addListener(handleTabChange);
super.initState();
}
void handleTabChange() {
if (_tabController.indexIsChanging) return;
widget.onTabChange(_tabController.index);
setState(() {
activeTabIndex = _tabController.index;
});
}
}
Listener'lar
Listener
'lar belirli bir nesne veya nesne türü değil, farklı zaman uyumsuz (asynchronous) fonksiyonlar için kullanılan bir adlandırma kuralıdır.
Listener
, genellikle bilinmeyen bir zamanda gerçekleşecek bir olaya yanıt olarak çağrılan bir fonksiyonu ifade eder. Fonksiyonu sadece oturup birinin "Tamam, şimdi yürütme zamanınız" demesini dinlemektir.
Tab controller'ın addListener
fonksiyonu, bir kullanıcı sekmeleri değiştirdiğinde çağrılır. Bu size, bir kullanıcı sekmeleri değiştirdiğinde bazı değerleri veya state'i güncelleme şansı verir.
Listener'ların yanı sıra TabController, sekmelerinizi ve ilgili içeriği yönetmenize yardımcı olan alıcılara (getters
) sahiptir.
_handleTabChange methodunun içinde, uygulamanızın hangisinin "aktif" sekme olduğunu (o anda ekranda görüntülenen) bildiğinden emin olmak için şöyle bir şey yapabilirsiniz:
int activeTab;
void _handleTabChange() {
setState(() =>
this.activeTab = _tabController.index);
}
Hava durumu uygulamasında, tab bar'da günün farklı bir saatine dokunduğunuzda, kullanıcı arayüzü günün o saatindeki hava koşullarıyla yeniden oluşturulur. Bu mümkündür çünkü setState, Flutter'a yeni seçilen tab'ı yeniden oluşturmasını ve bunu yaptığında görüntülemesini söyler. TabController.index
getter'ı, o anda aktif olan tab'a başvurur.
TabController
hakkında vermek istediğim son not, onu hiçbir zaman doğrudan değiştirmeniz gerekmediğidir. Sekmeler hakkında bilgi almak ve hangi sekmelerin aktif olduğunu güncellemek için kullanılan bir nesnedir. Ancak, yalnızca onunla etkileşime geçmeniz gerekir, onu özel bir sınıfa genişletmeniz(extend) değil.
@override
Widget build(BuildContext context) {
return TabBar(
labelColor: Colors.black,
unselectedLabelColor: Colors.black38,
unselectedLabelStyle:
Theme.of(context).textTheme.caption.copyWith(fontSize: 10.0),
labelStyle:
Theme.of(context).textTheme.caption.copyWith(fontSize: 12.0),
indicatorColor: Colors.transparent,
labelPadding: EdgeInsets.symmetric(horizontal: 48.0, vertical: 8.0),
controller: _tabController,
tabs: widget.tabItems.map((t) => Text(t)).toList(),
isScrollable: true,
);
}
Default olarak TabBar'daki sekmeler kaydırılmaz ancak isScrollable
özelliğini true olarak ayarladığınızda kaydırılabilir hale gelir.
• Tabları kullanmak, bir TabController
ve children
widget'ları gerektirir. Çocuklar, görüntülenen ve dokunulabilen widget'lardır.
• Tab bar'daki bir widget'a dokunulduğunda sekmeleri değiştirme işlevi, bir callback
yoluyla yapılır. Callback
, Flutter'a ne zaman yeni bir sekme oluşturacağını söylemek için TabController
tarafından gösterilen özellikleri kullanmalıdır.
ListView ve builder'lar
ListView widget'ı bir column veya row gibidir, çünkü alt widget'larını bir satırda görüntüler. Önemli olan nokta, kaydırılabilir olmasıdır. Çocuk sayısı bilinmediğinde yaygın olarak kullanılır. ListView
, doğrusal olarak düzenlenmiş kaydırılabilir bir widget listesidir.
ListView, liste içeriğine göre seçim yapmaya olanak sağlayan birkaç farklı constructor'a sahiptir.
Gösterilecek statik, az sayıda öğeniz varsa, default constructor
ile bir ListView oluşturabilirsiniz ve bu, bir satır veya sütuna çok benzer bir kodla oluşturulacaktır ve en performanslı seçenektir, ancak listeye koyacak onlarca veya yüzlerce öğeniz veya bilinmeyen sayıda öğeniz varsa ideal olmayabilir.
Builder pattern
Flutter'ın her yerinde bulunur ve esas olarak Flutter'a gerektiğinde
widget'lar oluşturmasını söyler. Default ListView constructor'ı FLutter'a çocukları bir kere de oluşturmasını söyler. ListView.builder
constructor'ı ise itemBuilder
özelliğinde bir callback alır ve bu callback(geri arama) bir widget döndürür. Bu oluşturucu, listenizde görüntülenecek çok fazla (veya sonsuz) sayıda liste öğeniz varsa, Flutter'ı öğeleri oluşturma konusunda daha akıllı hale getirir ve yalnızca ekranda görünen öğeler oluşturulur.
Temelde sonsuz bir tweet listesi olan Twitter gibi bir sosyal medya uygulaması hayal edin. Sonsuz sayıda tweet olduğu için, her state değiştiğinde o listedeki tüm tweet'leri oluşturmak mümkün olmazdı.
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: allAddedCities.length,
itemBuilder: (BuildContext context, int index) {
final City city = allAddedCities[index];
return Dismissible(
// ...
child: CheckboxListTile(
value: city.active,
title: Text(city.name),
onChanged: (bool b) =>
_handleCityActiveChange(b, city),
),
);
},
),
);
• ListView.separated
, ListView.builder
'a benzer, ancak iki builder methodu kullanır: biri liste öğelerini oluşturur , ikincisi ise liste öğeleri arasına yerleştirilmiş bir separator (ayırıcı) oluşturur.
• ListView.custom
: Bazı liste öğelerinin belirli bir widget ve diğer liste öğelerinin tamamen farklı bir widget olduğu durumlarda custom list view kullanılır.
Resource: Flutter in Action chapter 4
Top comments (0)