DEV Community

Cover image for Flutter Layout ve UI Widget'lar─▒ ­čĺź ­čîî ÔťĘ
G├╝lsen Keskin
G├╝lsen Keskin

Posted on • Updated on

Flutter Layout ve UI Widget'lar─▒ ­čĺź ­čîî ÔťĘ

Stack widget

Stack widget widget'lar─▒ ├╝st ├╝ste yerle┼čtirmek (veya y─▒─čmak) i├žin kullan─▒l─▒r.

Image description

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.

Image description

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.

Image description

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>[]
);

Enter fullscreen mode Exit fullscreen mode

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.

Image description

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>[...],
);
Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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(...));
}
Enter fullscreen mode Exit fullscreen mode

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(),
      ]
    ),
  ]
)
Enter fullscreen mode Exit fullscreen mode

TableCell, Text, Iconve 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),
        ),
      ),
    ],
  );
}),
// ...


Enter fullscreen mode Exit fullscreen mode

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.

Image description

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).

Image description

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;
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

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);
}

Enter fullscreen mode Exit fullscreen mode

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,
  );
}
Enter fullscreen mode Exit fullscreen mode

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),
          ),
        );
      },
    ),
);

Enter fullscreen mode Exit fullscreen mode

ÔÇó 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

Discussion (0)