Yleisimpiä käyttöliittymäkomponentteja
Learning objectives
- Tutustut usein käytettyihin käyttöliittymäkomponentteihin.
- Osaat luoda tekstisisältöä, kuvakkeita, kuvia, nappeja, ja lomakkeita.
Tutustuimme edellisessä osassa käyttöliittymäkomponenttien asetteluun käytettyihin luokkiin. Loimme listoja (ListView), rivejä (Row), ja sarakkeita (Column). Määrittelimme alueita (Container) ja harjoittelimme alueiden koon rajaamista (Expanded ja SizedBox).
Tutustutaan seuraavaksi sisällön luomiseen tarkoitettuihin käyttöliittymäkomponentteihin. Tarkastelemme tässä tekstien luomista, kuvakkeiden ja kuvien käyttöä, painikkeita, tiedon syöttämiseen käytettäviä lomakkeita, sekä muutamia apuvälineitä.
Teksti
Komponentti Text tarjoaa välineet tekstin ja sen tyylin määrittelyyn. Näytettävä teksti annetaan Text-luokan konstruktorille merkkijonona ja tekstin tyyli määritellään style
-attribuutin kautta.
Attribuutin style
arvoksi asetetaan TextStyle-olio, jonka avulla määritellään muunmuassa fontti (fontFamily
), fontin tyyli (fontStyle
), fontin painotus (fontWeight
), ja fontin koko (fontSize
). Alla olevassa esimerkissä käytetään näitä kaikkia tekstin "Sovellukseni" näyttämiseen.
Tekstin tyylin määrittely on valinnaista. Mikäli tekstille määritellään fontti TextStyle
-olion attribuutilla fontFamily
, käytössä oleva fontti määräytyy attribuutin arvon ja käyttöjärjestelmässä olevien fonttien perusteella. Mikäli haluttua fonttia ei löydy, käytetään oletusfonttia -- tutustumme loppukurssista lyhyesti fonttien tuomiseen mobiilisovellukseen ohjelmointiympäristön käyttöönottoon liittyvässä osassa.
Fontin tyyli ja painotus määritellään attribuuteilla fontStyle
ja fontWeight
. Tyyli voidaan määritellä joko kursiiviksi (arvo FontStyle.italic
) tai normaaliksi (arvo FontStyle.normal
). Vastaavasti, painotuksen oleellisimmat arvot ovat korostettu (FontWeight.bold
) ja normaali (FontWeight.normal
). TextStyle-olion oletustyyli ja -paino ovat normaalit, joten mikäli tyylin tai painotuksen haluaa asettaa normaaliksi, esim FontWeight.normal
, voi attribuutin myös jättää kokonaan määrittelemättä.
Fontin koko määritellään fontSize
-attribuutilla. Koko annetaan numeroarvona, joka kuvaa fontin kokoa pikseleinä. Fontin koko on suhteellinen eli mikäli käyttäjä esimerkisi zoomaa ruudulle tai kasvattaa kaikkien fonttien kokoa, muuttuu myös niiden fonttien koko, joiden koko on määritelty fontSize
-attribuutilla.
Luokka Text tarjoaa myös nimetyn konstruktorin Text.rich, jonka avulla määritellään useammasta tyylistä koostuvia tekstejä. Tällöin tekstin osat luodaan TextSpan-luokan avulla, jonka avulla määritellään kukin tekstin osa ja sen tyyli. Konstruktori Text.rich
saa yhden TextSpan
-olion, joka sisältää sekä tekstin (attribuutti text
) että mahdollisesti muita TextSpan
-olioita TextSpan-olion children
-attribuuttiin asetetussa listassa.
Alla olevassa esimerkissä luodaan ohjelma, joka näyttää tekstin, jota on korostettu ja kursivoitu. Teksti "Sovellukseni on" näytetään normaalilla fontilla. Tätä seuraa teksti " hieno", joka on korostettu. Tämän jälkeen tulee teksti " ja", joka näytetään normaalilla fontilla. Lopuksi tuleva teksti " monimutkainen" näytetään kursivoituna.
Huom! Vaikka yllä demonstroidaan monimutkaisemman tekstin luomista, on hyvä huomata että luettavuuden kannalta yksinkertaisemmat tekstit ovat tyypillisesti parempia.
Kuvakkeet ja kuvat
Tekstien lisäksi sovellukseen voidaan lisätä kuvakkeita ja kuvia. Kuvakkeiden (engl. icon) avulla voidaan luoda kiintopiste katseelle tai tarjota esimerkiksi lisätietoa ohjelman toiminnallisuudesta. Kuvat taas ovat, noh, kuvia.
Kuvakkeiden käyttö tapahtuu luokan Icon avulla. Kuvake luodaan Icon
-luokan konstruktorilla, jolle annetaan konkreettinen kuvake, kuvakkeen koko (attribuutti size
), kuvakkeen väri (attribuutti color
), sekä kuvakkeeseen liittyvä teksti (attribuutti semanticLabel
), joka on esimerkiksi ruudunlukijaohjelmien käytössä. Konkreettinen kuvake valitaan luokan Icons tarjoamista kuvakkeista (kts. kohta "Constants") -- esimerkiksi Icons.favorite
kuvaa sydäntä ja Icons.local_taxi
kuvaa taksia.
Alla olevassa esimerkissä luodaan kaksi 48-pikselin kokoista kuvaketta. Ensimmäinen on punainen sydän ja toinen on keltainen taksi. Kuvakkeet näytetään vierekkäin rivillä. Alla kuvataan myös eräs tapa tilan määrittelyyn rivillä olevien komponenttien ympärille. Asettamalla rivin attribuutille mainAxisAlignment
arvoksi MainAxisAlignment.spaceAround
, rivi tasaa komponenttien ympärille jäävän tilan.
Kuvien luominen onnistuu Image-luokan avulla. Oleellisin luokan tarjoama nimetty konstruktori on Image.network
, joka hakee näytettävän kuvan annetusta verkko-osoitteesta. Oletuksena kuvalle annetaan kaikki käytettävissä oleva tila, mutta kuvan koon voi myös määritellä attribuuttien width
ja height
avulla. Kuten kuvakkeita käytettäessä, kuvaa kuvaava teksti määritellään attribuutilla semanticLabel
.
Alla olevassa esimerkissä on sovellus, joka näyttää kaksi kuvaa sarakkeessa. Ensimmäisen kuvan leveys ja korkeus on 64 pikseliä, ja toinen kuva täyttää sille annetun tilan. Kuvat haetaan Cat as a Service -palvelusta, joka tarjoaa kissan kuvia.
Napit
Flutter tarjoaa useita nappeja ja painikkeita. Luokan ElevatedButton avulla luodaan napilta näyttävä painike, luokan OutlinedButton avulla luodaan painike, jolla on reunat, ja luokan TextButton avulla luodaan painike, jota ei korosteta. Kaikille näistä luokista annetaan konstruktorin child
attribuutin arvona komponentti, joka sisältää napissa näytettävän asian -- tämä on tyypillisesti tekstikomponentti. Attribuutille onPressed
annetaan funktio, joka kutsutaan napin painalluksen yhteydessä. Tässä kohtaa esimerkeissä napin painaminen tulostaa tekstin konsoliin -- palaamme myöhemmin muunlaisiin tapahtumiin.
Alla olevassa esimerkissä luodaan ohjelma, joka sisältää kolme nappia. Napit ovat asetettuna riville siten, että niiden välillä on hieman tilaa. Ensimmäisessä napissa on teksti "Ensimmäinen", toisessa napissa on teksti "Toinen", ja kolmannessa napissa teksti "Kolmas". Jokaisen napin painaminen kutsuu funktiota tervehdi
, joka tulostaa konsoliin viestin "Hei maailma!".
Kuvakkeista voidaan luoda painikkeita asettamalla ne IconButton-luokan sisälle. IconButton saa konstruktorin attribuuttina icon
-muuttujan, johon asetetaan Icon
-luokasta tehty olio. Attribuuttina onPressed
annetaan funktio, jota kutsutaan kun nappia painetaan. Alla olevassa esimerkissä on esitettynä ohjelma, joka sisältää kuvakkeesta luodun napin. Nappia painettaessa konsoliin tulostuu teksti "Hei maailma!".
Huom! IconButton-olion tulee olla Scaffold-olion sisällä.
Nappeja, jotka sisältävät sekä kuvakkeen että tekstin voidaan luoda nappien nimetyn konstruktorin avulla. Luokalla ElevatedButton
on konstruktori ElevatedButton.icon
, luokalla OutlinedButton
on konstruktori OutlinedButton.icon
, ja luokalla TextButton
on konstruktori TextButton.icon
. Kukin näistä saa attribuuttina icon
kuvakkeen, attribuuttina label
tekstin, ja attribuuttina onPressed
painikkeen yhteydessä kutsuttavan funktion.
Alla olevassa esimerkissä luodaan kaksi painiketta, joista kummallakin on kuvakkeena sydän ja tekstinä "Tykkään!".
Lomakkeet
Lomakkeita käytetään tiedon syöttämiseen ja lähettämiseen. Lomakkeiden luominen tapahtuu Form-luokan avulla, jonka child
-attribuutiksi asetetaan lomakekenttä tai joukko lomakekenttiä esimerkiksi Column-olion sisälle lisättynä. Lomakekentän luominen tapahtuu luokan TextFormField avulla.
Alla olevassa esimerkissä luodaan lomake, johon lisätään yksi lomakekenttä. Kun ohjelman käynnistää, ohjelmassa on kohta, johon voi syöttää tekstiä.
Yllä olevassa esimerkissä ei kerrota mitä tekstikenttään pitäisi syöttää. Lomakekentän kuvaus luodaan InputDecoration-luokan avulla, jonka avulla voidaan määritellä lomakekentälle kuvake (attribuutti icon
), sisällön kuvaus (attribuutti labelText
) ja kentässä näkyvä sisältöä kuvaava vinkki (attribuutti hintText
), sekä tyylejä kuten reunukset (attribuutti border
). InputDecoration-luokasta luotu olio asetetaan TextFormField-luokan konstruktorin attribuutin decoration
arvoksi.
Alla olevassa esimerkissä luodaan lomake, jossa on tekstikenttä. Tekstikentällä on kuvaus, joka sisältää sydänkuvakkeen (Icons.favorite
), kysymyksen tai kuvauksen "Mistä pidät?", ja tekstikentässä olevan oletustekstin "Pidän...". Tämän lisäksi tekstikentällä on luokan OutlinedBorder avulla luodut reunukset.
Mikäli lomakkeeseen haluaa lisätä useamman kentän, asetetaan ne esimerkiksi sarakkeen (Column) sisälle. Alla olevassa esimerkissä luodaan lomake, jossa on kaksi tekstikenttää ja nappi. Nappi on asetettu Container-olion sisälle, jotta sille voidaan määrätä reuna (padding
). Esimerkissä näytetään myös miten Container-olion sisälle asetettu komponentti rivitetään oikealle laidalle (alignment: Alignment.topRight
).
Joskus lomakkeen haluaa toteuttaa niin, että nappi sijaitsee tekstikentän vieressä, tai niin, että tekstikentän vieressä on toinen tekstikenttä. Tämä toteutetaan Row-olion avulla siten, että riville asetetaan vierekkäin näytettävät komponentit. Tekstikentällä ei kuitenkaan ole oletuksena määritelty leveyttä ja Row-olio taas haluaa tietää tekstikentän leveyden, jotta sitä voisi käyttää. Alla olevan ohjelman suorittaminen päätyisi virhetilanteeseen.
import 'package:flutter/material.dart';
tervehdi() {
print('Hei maailma!');
}
main() {
final sahkoposti = TextFormField(decoration: InputDecoration(
icon: Icon(Icons.email),
hintText: 'email@address.net',
border: OutlineInputBorder(),
));
final nappi = Container(
padding: EdgeInsets.all(16),
child: ElevatedButton(
child: Text('Lähetä'),
onPressed: tervehdi
)
);
final sarake = Row(children: [sahkoposti, nappi]);
final lomake = Form(child: sarake);
final sovellusrunko = Scaffold(body: lomake);
final sovellus = MaterialApp(home: sovellusrunko);
runApp(sovellus);
}
Mikäli tekstikentän vieressä haluaa näyttää napin tai toisen tekstikentän, tulee tekstikenttä asettaa Expanded-komponentin sisälle (myös Flexible ajaa saman asian). Alla olevassa esimerkissä näytetään ohjelma, missä tekstikenttä ja nappi ovat vierekkäin.
Lomakkeemme eivät tällä hetkellä oikeastaan tee vielä mitään. Tutustumme tapahtumien käsittelyn yhteydessä lomakkeisiin syötettyjen tietojen käsittelyyn.
Tiedon ryhmittely
Tutustuimme edellisessä osassa muutamiin käyttöliittymän asetteluun käytettyihin komponentteihin ja olemme tässä osassa keskittyneet käyttöliittymässä näkyviin komponentteihin. Mikäli haluaisimme koostaa käyttöliittymäkomponenteista loogisia kokonaisuuksia -- esimerkiksi luoda listalla näytettäviä tietoja, joissa on vaikkapa kuvake, otsikko, ja leipäteksti, käyttäisimme asetteluun tarkoitettuja komponentteja yhdessä käyttöliittymäkomponenttien kanssa.
Flutter tarjoaa valmiin komponentin ListTile yksinkertaisten tietojen näyttämiseen ja ryhmittelyyn. ListTile-olioille voidaan määritellä laidassa näkyvä kuvake tai kuva (attribuutti leading
), otsikko (attributti title
) sekä aliotsikko (attribuutti subtitle
). Alla olevassa esimerkissä ludaan kaksi ListTile-elementtiä, joiden avulla kuvataan henkilöiden yhteystietoja. ListTile-elementit näytetään ListView-luokasta luodussa listassa.
ListTile-luokkaa käytetään usein Card-luokan kanssa, jonka avulla näytettävälle tiedolle luodaan rajattu alue. Card-luokalle annetaan näytettävä tieto child
attribuutin arvona. Alla edellistä esimerkkiä on jatkettu siten, että näytettävät tiedot näytetään Card-luokan avulla tyyliteltynä.
Attribuutti leading
asettaa ListTile-olion vasemmalle laidalle kuvakkeen tai kuvan. Mikäli kuvakkeen tai kuvan haluaa asettaa ListTile-olion oikeaan laitaan, asetetaan se trailing
-attribuutilla.
Alla olevassa esimerkissä kuvataan leading
ja trailing
attribuuttien käyttöä viestinvaihtoa kuvaavassa ohjelmassa. Ohjelma näyttää kolme viestiä, joista kaksi on henkilöltä "Matti" ja yksi henkilöltä "Maija". Esimerkissä on käytetty CircleAvatar-luokkaa henkilöä kuvaavan kuvakkeen luomiseen.
Apuvälineitä
Tarkastellaan vielä luokkia Placeholder ja SafeArea, jotka ovat sovellusten tekemisessä auttavia apuvälineitä. Luokka Placeholder tarjoaa välineen väliaikaisten alueiden määrittelyn sovelluksessa, joka mahdollistaa käyttöliittymien rakentamisen siten, että esimerkiksi grafiikat tai kuvat lisätään myöhemmin. Luokka Placeholder luo käytännössä käyttöliittymään yliviivatun laatikon, joka täyttää käytössä olevaa tilaa. Oletuksena Placeholder-olio täyttää koko käytössä olevan tilan, mutta olion käyttämän tilan voi määritellä myös fallbackWidth
ja fallbackHeight
-attribuuteilla.
Alla olevassa esimerkissä näytetään sovellus, jossa on varattu Placeholder-luokan avulla tilaa kuvalle ja kuvakkeelle.
Siinä missä Placeholder on hyödyllinen sovelluksen suunnittelussa ja rakentamisessa, SafeArea edesauttaa valmiin sovelluksen toimintaa. Mobiililaitteita ja mobiililaitteiden käyttöliittymiä on hyvin erilaisia, ja niissä on jokaisessa pieniä rajoitteita graafiselle käyttöliittymälle. Osassa mobiililaitteista esimerkiksi kamera saattaa olla osittain ruudun päällä; vastaavasti myös esimerkiksi mobiililaitteen käyttöjärjestelmä saattaa näyttää osia ruudulla. Luokka [SafeArea] luo mobiilisovellukselle alueen, joka on rakennettu siten, että mobiililaitteen tai käyttöjärjestelmän ei pitäisi olla sovelluksen osien päällä.
Luokan SafeArea käyttäminen on hyvin suoraviivaista. SafeArea-olioita luodessa sille annetaan child
-attribuutti, joka sisältää näytettävän sovelluksen sisällön. Alla olevassa esimerkissä luodaan sovellus, jossa sovelluksen sisältö on SafeArea-olion sisällä.
Hi! Please help us improve the course!
Please consider the following statements and questions regarding this part of the course. We use your answers for improving the course.
I can see how the assignments and materials fit in with what I am supposed to learn.
I find most of what I learned so far interesting.
I am certain that I can learn the taught skills and knowledge.
I find that I would benefit from having explicit deadlines.
I feel overwhelmed by the amount of work.
I try out the examples outlined in the materials.
I feel that the assignments are too difficult.
I feel that I've been systematic and organized in my studying.
How many hours (estimated with a precision of half an hour) did you spend reading the material and completing the assignments for this part? (use a dot as the decimal separator, e.g 8.5)
How would you improve the material or assignments?