Tapahtumat ja sovelluksen tila
Learning objectives
- Tutustut hieman tarkemmin tapahtumiin ja niiden käsittelyyn.
- Tutustut käsitteeseen sovelluksen tila.
- Opit luomaan tilallisia komponentteja, joissa olevien muuttujien arvot päivittyvät automaattisesti käyttöliittymään.
Ohjelmissamme on tähän mennessä käsitelty tapahtumia kuten esimerkiksi napin painalluksia tai ruudun pyyhkäisyjä. Tarkastellaan seuraavaksi käsitettä tapahtuma hieman tarkemmin. Tämän jälkeen tutustumme sovelluksen tilaan sekä tilan muutosten myötä päivittyvien käyttöliittymäkomponenttien toteutukseen.
Tapahtumat ja niiden käsittely
Flutter-sovelluksessa näkyvät komponentit luodaan pääsääntöisesti joko sovelluksen käynnistyessä tai jonkinlaisen tapahtuman yhteydessä. Komponentin luomiseen johtava tapahtuma voi olla vaikkapa siirtyminen näkymästä toiseen -- tapahtuman yhteydessä suoritetaan jonkinlainen ohjelmakoodi.
Tarkastellaan tapahtumia ja niiden käsittelyä nappien ja nappeihin liittyvän onPressed
-attribuutin avulla. Attribuutille onPressed
annetaan funktio, jota kutsutaan kun nappia painetaan.
Alla olevassa esimerkissä luodaan kaksi nappia. Toisen napin onPressed
-attribuutille annetaan arvo null
, joka tarkoittaa ettei napin painamiseen liity kutsuttavaa funktiota eikä nappia voi painaa. Toisen napin onPressed
-attribuutille annetaan kutsuttavan funktion nimi. Tässä kutsuttava funktio on tulosta
, joka tulostaa print
-funktiota käyttäen tekstin Hei!
konsoliin.
Kun käynnistät ohjelman, huomaat että toista nappia voi painaa ja toista ei.
Tapahtumien käsittely on niinkutsutun tapahtumankäsittelijän vastuulla. Käytännössä Flutterissa (ja vastaavissa sovelluksen suorittamisesta vastaavissa ympäristöissä) tapahtumien käsittely on toteutettuna osaksi ympäristöä. Tapahtuma, kuten vaikkapa napin painaminen, lähettää viestin tapahtumankäsittelijälle, joka viestin saatuaan suorittaa tapahtumaan liitetyn ohjelmakoodin.
Tapahtumaan liitetty ohjelmakoodi määritellään tyypillisesti tapahtuman aiheuttavan komponentin yhteydessä. Yllä olevassa esimerkissä tapahtuman aiheuttava komponentti on nappi, jolle annetaan tapahtumaan liittyvä ohjelmakoodi onPressed
-attribuutin avulla.
Yllä olevassa esimerkissä tapahtumaan liittyvä ohjelmakoodi on funktio tulosta
, joka on määritelty erillisenä funktiona, ja jota voi kutsua muualta ohjelmasta. Toiminnallisuuden voi määritellä myös anonyyminä funktiona eli funktiona, jolle ei anneta nimeä. Tällöin funktioon liittyvä ohjelmakoodi määritellään onPressed
-attribuutin arvoksi.
Anonyymin funktion voi kirjoittaa muodossa () { koodi }
, jolloin aaltosulkuihin voi määritellä useamman rivin (lauseen), tai nuolisyntaksin avulla muodossa () => koodi
, jolloin anonyymiin funktioon liittyy yksi rivi (lause). Alla olevassa esimerkissä käytetään kumpaakin näistä vaihtoehdoista.
Kun yllä olevassa ohjelmassa painaa nappia, jossa on teksti Nappi 1
, konsoliin tulostuu viesti Hei 1!
. Vastaavasti, kun ohjelmassa painetaan nappia, jossa on teksti Nappi 2
, konsoliin tulostuu viesti Hei 2!
.
Ohjelmaan voidaan määritellä myös muuttujia. Alla olevassa ohjelmassa ohjelman alussa määritellään muuttuja luku
, joka sisältää arvon 0
. Napin painaminen tulostaa muuttujan luku
arvon konsoliin.
Ohjelman voi toteuttaa myös siten, että muuttujan arvo muuttuu napin painalluksen yhteydessä. Alla olevassa esimerkissä muuttujan luku
arvoa kasvatetaan nappia painettaessa.
Mutta, entäpä jos muuttujan arvoa käytetään osana käyttöliittymäkomponenttia? Alla olevassa esimerkissä napin tekstissä on muuttujan luku
arvo, ja napin painaminen kasvattaa muuttujan luku
arvoa.
Kun kokeilet ohjelmaa, huomaat, ettei käyttöliittymässä näkyvä arvo muutu nappia painettaessa.
Sovelluksen tila
Sovelluksen tilalla viitataan siihen, että sovelluksessa voi olla muuttujia, joista jokaiseen muuttujaan liittyvä arvo voi vaikuttaa siihen mitä ohjelmassa kullakin hetkellä näytetään tai tehdään. Osa sovelluksen tilaan liittyvistä muuttujista on piilossa sekä käyttäjältä että ohjelmoijalta, osa ohjelmoijan käsiteltävissä, ja toisaalta osa käyttäjänkin käsiteltävissä.
Esimerkiksi näkymästä toiseen siirtyessä sovellus näyttää käyttäjälle animaation, missä ruudun päälle tulee esimerkiksi toinen ruutu -- tämän animaation askeleita ja toisaalta kussakin askeleessa näytettävää näkymää hallinnoidaan muuttujien avulla, jotka eivät lähtökohtaisesti ole ohjelman toteuttajan tai käyttäjien nähtävissä. Toisaalta sitten, esimerkiksi lomakkeen näyttämiseen ja täyttämiseen tarkoitetussa sovelluksessa käyttäjä syöttää tietoa, jota ohjelma myöhemmin mahdollisesti käsittelee.
Aiemmin käyttämämme StatelessWidget
-luokka viittaa tilattomiin komponentteihin, eli komponentteihin, joiden sisäistä tilaa ei voida muuttaa niiden luomisen jälkeen. Alla olevassa esimerkissä luodaan StatelessWidget
-olio, jonka sisällä on muuttuja. Aivan kuten aiemmassa esimerkissä, vaikka muuttujan arvoa voidaan muuttaa nappia painamalla, ei napissa oleva teksti muutu vaikka muuttujan laskuri
arvo muuttuu.
Kun sovellusta tarkastelee ohjelmointiympäristössä, ilmoittaa ohjelmointiympäristö seuraavaa: This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final: Laskuri.laskuri - line 8. Käytännössä ohjelmointiympäristö kertoo, että luokan Laskuri pitäisi olla tilaton (tai, tarkemmin ottaen, luokasta tehtyjen olioiden pitäisi olla muuttumattomia), joten oliomuuttujat pitäisi määritellä avainsanalla final
.
Tilan ja näkymän muuttaminen
Tutustutaan seuraavaksi sovelluksen tilan ja näkymän ylläpitoon. Sovelluksen tila (state) sisältää tietoa, joka luetaan sovelluksen käynnistyksen yhteydessä, ja joka voi muuttua sovelluksen käynnissäoloaikana. Muuttuvien käyttöliittymäkomponenttien toteuttaminen Flutter-sovelluskehyksellä tapahtuu StatefulWidget ja State -luokkien avulla.
Tilan ja näkymän ylläpitoon löytyy myös muita menetelmiä -- tutustumme myöhemmin lyhyesti provider-kirjastolla toteutettavaan tilan ja näkymän ylläpitoon.
Luokat StatefulWidget
ja State
toimivat vuorovaikutuksessa keskenään. Luokasta State
perityllä luokalla määritellään käyttöliittymäkomponentti, joka voi sisältää muuttujia (tilan), ja joka sisältää toiminnallisuuden tilan muuttamiseen. Luokasta StatefulWidget
perityn luokan kautta taas konkreettisesti luodaan State
-luokan avulla määritelty käyttöliittymäkomponentti.
Tätä vuorovaikutusta on kuvattu alla olevassa esimerkissä. Alla olevassa esimerkissä luodaan luokat Laskuri
ja LaskuriState
. Luokka Laskuri
perii luokan StatefulWidget
ja korvaa luokasta StatefulWidget
perityn createState
, jota käytetään State
-luokan perivän luokan (tässä LaskuriState
) luomiseen. Luokka LaskuriState
perii luokan State
ja määrittelee metodin build
, jonka sisällä luodaan konkreettinen käyttöliittymäkomponentti.
Esimerkissä ei ole vielä vuorovaikutustoiminnallisuutta -- käyttöliittymään määriteltyä nappia ei voi painaa.
Lisätään luokalle LaskuriState
metodi kasvata
, joka kasvattaa muuttujan laskuri
arvoa yhdellä. Muokataan sovellusta myös siten, että napin painaminen kutsuu metodia kasvata
.
Kun kokeilet ylläolevaa ohjelmaa, huomaat, ettei napin painaminen vaikuta sovelluksessa näytettyyn tietoon.
Luokkien State
ja StatefulWidget
vuorovaikutus tapahtuu luokalta State
perittävän metodin setState
avulla. Metodia setState
kutsuttaessa luokan State
perinyt luokka ilmoittaa, että sen sisäinen tila on muuttunut. Tällöin sen metodia build
kutsutaan uudestaan ja näytettävä käyttöliittymäkomponentti päivittyy.
Metodille setState
annetaan parametrina funktio. Tämän funktion sisällä tehdään konkreettinen State
-luokan perivän luokan muuttujien arvojen muutos. Esimerkiksi, mikäli haluamme muuttaa laskuri
-muuttujan arvoa yhdellä, kirjoitetaan setState
-kutsu seuraavassa muodossa.
setState(() {
laskuri++;
});
Konkreettisesti funktiota setState
kutsutaan tapahtuman yhteydessä. Edellisessä esimerkissä hyvä paikka setState
kutsulle on metodi kasvata
. Alla olevassa esimerkissä metodia kasvata
on muokattu siten, että se kutsuu setState
-funktiota. Funktiolle annetaan parametrina funktio, jonka sisällä kasvatetaan muuttujan laskuri
arvoa.
Kun kokeilet alla olevaa sovellusta, huomaat että napissa oleva arvo kasvaa nappia painettaessa.
State ja tyyppiparametri
Luokan State
perinnän yhteydessä luokalle voidaan määritellä myös tyyppiparametri, joka perinnän yhteydessä kertoo mihin luokkaan State
-luokasta perittävä luokka liittyy. Yllä olevassa laskuriesimerkissä tämä näyttäisi seuraavalta.
// ...
class Laskuri extends StatefulWidget {
// ..
}
class LaskuriState extends State<Laskuri> {
// ..
}
Olemme tässä materiaalissa jättäneet tyyppiparametrin metkitsemättä.
Sovelluksessa voi luonnollisesti olla useampia tapahtuman lähteitä. Alla olevassa esimerkissä on luku sekä kaksi nappia. Ensimmäisen napin painaminen kasvattaa luvun arvoa yhdellä, ja toisen napin painaminen nollaa luvun.
Sovelluksen tilaan liittyvä logiikka
Sovelluksiin voi luoda logiikkaa, jossa sovelluksen tila huomioidaan näytettävää sisältöä päätettäessä. Sovelluslogiikkaa voi sisällyttää sekä setState
-kutsun yhteyteen että build
-kutsun yhteyteen. Tarkastellaan näitä kelloa kuvaavan esimerkin kautta.
Alla olevassa esimerkissä esitellään kelloa kuvaava sovellus, joka sisältää tiedon sekunneista ja minuuteista. Kun sovelluksessa olevaa nappia painetaan, sekuntien määrä kasvaa yhdellä.
Kelloissa sekuntien ja minuuttien suurin mahdollinen arvo on tyypillisesti 59, jonka jälkeen muuttujan arvo asetetaan nollaksi. Mikäli sekuntien arvo kasvaa yli viidenkymmenen yhdeksän, sekuntien nollaamisen yhteydessä minuuttien arvoa kasvatetaan yhdellä.
Tämä toiminnallisuus toteutetaan osaksi setState
-kutsua. Toiminnallisuus näyttää seuraavalta.
setState(() {
sekunnit++;
if (sekunnit >= 60) {
sekunnit = 0;
minuutit++;
}
if (minuutit >= 60) {
minuutit = 0;
}
});
Edeltävä muutos on lisätty alla olevaan sovellukseen. Nyt, kun käynnistät sovelluksen ja painat nappia kuusikymmentä kertaa, huomaat että sekuntien määrä nollaantuu ja minuuttien määrä kasvaa yhdellä.
Kun kokeilet ylläolevaa sovellusta, huomaat että se ei vastaa täysin ajatustamme kellon näyttämästä arvosta. Esimerkiksi minuutti viisi sekuntia esitetään yllä olevassa sovelluksessa muodossa 1:5
. Olemme kuitenkin tottuneet muotoon 01:05
-- mikäli minuuttien tai sekuntien arvo on alle 10, näytettyihin arvoihin lisätään etunolla.
Sovelluslogiikkaa voi sisällyttää myös build
-metodiin, jossa päätetään mitä sovelluksessa näytetään. Tällä hetkellä build
-metodin toteutus on seuraava.
Widget build(BuildContext context) {
return ElevatedButton(child: Text('$minuutit:$sekunnit'), onPressed: kasvata);
}
Muokataan tätä siten, että build
-metodissa esitellään sekuntien ja minuuttien näyttämiseen tarkoitetut merkkijonomuuttujat. Mikäli sekuntien tai minuuttien arvo on pienempi kuin 10, merkkijonomuuttujille asetetaan nolla alkuun. Sekunneille tämän voi tehdä seuraavalla tavalla.
var sTeksti = '$sekunnit';
if (sekunnit < 10) {
sTeksti = '0$sekunnit';
}
Nyt, sen sijaan, että käytämme muuttujia minuutit
ja sekunnit
kellon arvon näyttämiseen, käytämme kellon arvon näyttämiseen erikseen luotuja merkkijonomuuttujia. Alla olevassa ohjelmassa esitellään tämä toiminnallisuus.
Erilaista logiikkaa eri paikoissa
Edellä käsittelimme logiikan sisällyttämistä setState
- ja build
-kutsujen yhteyteen. Logiikan jakaminen näihin kahteen kutsuun on mielekästä, sillä se mahdollistaa myös tilan muuttamiseen liittyvän logiikan ja tilan näyttämiseen liittyvän logiikan eriyttämisen toisistaan.
Hyvä nyrkkisääntö logiikan sijainnin päättämiseen on seuraava:
Tilan muuttamiseen liittyvä logiikka asetetaan setState
-kutsun yhteyteen ja tilan näyttämiseen liittyvä logiikka asetetaan build
-kutsun yhteyteen.
Sovellusta voi halutessaan pilkkoa pienempiin osiin. Esimerkiksi etunollan lisäämiseen liittyvä toiminnallisuus voidaan eriyttää omaksi metodikseen. Alla olevassa esimerkissä on luotuna erillinen metodi lukuEtunollalla
, joka palauttaa luvun etunollalla. Metodin määrittelyssä lukuEtunollaksi(int luku)
parametrina annettava muuttuja luku
on eksplisiittisesti määritelty kokonaisluvuksi (int
).
Metodia lukuEtunollalla
käytettäessä build
-metodi on hieman selkeämpi.
Edellisissä esimerkeissä olemme huomanneet, että tilallisissa komponenteissa käyttöliittymässä näytettävät arvot voivat muuttua kun sovelluksen tila muuttuu. Emme ole vielä kuitenkaan tarkastelleet miksi näin tapahtuu. Tutustutaan tähän seuraavaksi.
Komponentti ja sen lapset luodaan tilan muuttuessa
Kun komponentin tila muuttuu, se luodaan uudestaan. Tämä tarkoittaa käytännössä sitä, että setState
-kutsun yhteydessä State
-luokan perineen luokan build
-metodia kutsutaan uudelleen, ja tuloksena saatu komponentti vaihdetaan edellisen komponentin paikalle. Mikäli muuttuva komponentti sisältää muita komponentteja, myös ne luodaan uudestaan.
Alla olevassa esimerkissä demonstroidaan tätä toiminnallisuutta. Ohjelmassa näytetään sanoja yksi kerrallaan. Sanaa painamalla siirrytään seuraavaan sanaan. Ohjelma on toteutettu neljällä luokalla: Laulu
perii luokan StatefulWidget
ja luo LauluState
-olion. LauluState
-luokka perii luokan State
ja vastaa näytettävästä sanasta. Luokka Teksti
perii luokan StatelessWidget
ja sitä käytetään tyyylitellyn merkkijonon näyttämiseen (tässä fonttikokoa on hieman kasvatettu).
Kun metodeja createState
ja build
kutsutaan, ohjelma tulostaa konsoliin viestin. Tämän lisäksi ohjelma tulostaa viestin aina metodia vaihda
-kutsuttaessa, jota kutsutaan nappia painettaessa. Kun tekstit on käyty loppuun asti, niiden näyttäminen aloitetaan alusta.
Kun kokeilet ylläolevaa ohjelmaa, huomaat että tekstiä painettaessa tulostetaan viestit Rakennetaan sisältöä (LauluState)
ja Rakennetaan sisältöä (Teksti)
. Tulostus tapahtuu uudelleen jokaisen napin painalluksen yhteydessä -- tämä tarkoittaa sitä, että LauluState ja Teksti luodaan uudestaan aina kun nappia painetaan.
Käytännössä tämä tarkoittaa sitä, että kun ohjelman tila muuttuu, tilaan liitetyt komponentit rakennetaan uudestaan.
Tilan päivitys lapsikomponentista
Edellisissä esimerkeissä setState
-funktiota kutsutaan State
-luokan perivässä luokassa. Toisin sanoen, tilaan tehdyistä muutoksista on tähän mennessä nähdyissä esimerkeissä vastannut komponentti, jossa tila on määritelty. Entäpä jos haluaisimme rakentaa käyttöliittymän useammasta komponentista, joista useampi voi vaikuttaa sovelluksen tilaan?
Tämä on mahdollista, sillä voimme antaa funktion konstruktorin (tai metodin tai funktion) parametrina. Antamalla funktio parametrina luokan konstruktorille, voimme luoda komponentin, jonka sisältä voidaan kutsua toista funktiota. Tämä annettava funktio voi sisältää setState
-kutsun.
Tarkastellaan seuraavan esimerkin kautta. Alla oleva ohjelma sisältää tykkäysten lukumäärän sekä tykkäämiseen käytetyn kuvakkeen. Tykkäämiseen käytetty kuvake on toteutettu erillisenä komponenttina TykkaaKuvake
, joka perii StatelessWidget
-luokan. Luokasta TykkaaKuvake
tehdyt oliot ovat siis tilattomia. Kun sovelluksessa painetaan tykkäämistä kuvaavaa kuvaketta, konsoliin tulostuu viesti "Nappia painettu". Tykkäysten lukumäärä ei kuitenkaan kasva.
Havainto siitä, että yllä olevassa esimerkissä tykkäysten lukumäärä ei kasva kuvaketta painettaessa on odotettu. Kuvakkeeseen ei ole liitetty toiminnallisuutta, jonka pitäisi kasvattaa tykkäysten määrää.
Lisätään ohjelmaan toiminnallisuus, jonka avulla kuvakkeen painaminen johtaa tykkäysten lukumäärän kasvuun. Tämä tehdään antamalla määrittelemällä luokalle TykkaaKuvake
funktiota kuvaava muuttuja Function, joka annetaan konstruktorin parametrina.
class TykkaaKuvake extends StatelessWidget {
final Function tykkaaFunktio; TykkaaKuvake(this.tykkaaFunktio);
painettu() {
print('Nappia painettu');
}
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.thumb_up),
onPressed: painettu
);
}
}
Kun luokalle on määritelty konstruktorin parametrina annettava funktio, lisätään luokkaan vielä toiminnallisuus funktion kutsumiseksi. Muokataan metodia painettu
siten, että siellä kutsutaan konstruktorin parametrina saatua funktiota.
class TykkaaKuvake extends StatelessWidget {
final Function tykkaaFunktio;
TykkaaKuvake(this.tykkaaFunktio);
painettu() {
tykkaaFunktio(); }
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.thumb_up),
onPressed: painettu
);
}
}
Yllä olevan esimerkin voi kirjoittaa hieman lyhyemmin siten, että luokassa ei määritellä erillistä painettu
-metodia. Tällöin onPressed
-muuttujan arvoksi määritellään anonyymi funktio, jossa kutsutaan konstruktorin parametrina saatua tykkaaFunktio
-funktiota.
class TykkaaKuvake extends StatelessWidget {
final Function tykkaaFunktio;
TykkaaKuvake(this.tykkaaFunktio);
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.thumb_up),
onPressed: () => tykkaaFunktio() );
}
}
Kun TykkaaKuvake
-luokalle on määritelty konstuktorin parametriksi funktio, tulee funktio antaa konstruktorikutsun yhteydessä. Luokasta TykkaaKuvake
tehdään olio luokassa TykkayksetState
. Muokataan luokkaa TykkayksetState
siten, että TykkaaKuvake
saa konstruktorin parametrina funktion tykkaa
.
class TykkayksetState extends State {
var tykkaykset = 0;
tykkaa() {
setState(() => tykkaykset++);
}
Widget build(BuildContext context) {
return Column(children: [
Text('Tykkäyksiä: $tykkaykset'),
TykkaaKuvake(tykkaa) ]);
}
}
Nyt TykkaaKuvake
saa konstruktorin parametrina tykkaa
-funktion, jota kutsuttaessa ohjelman tila päivittyy. Funktiota kutsutaan TykkaaKuvake
-luokasta luodun olion kuvaketta painettaessa. Jokainen painallus kasvattaa tykkäysten lukumäärää yhdellä. Voit kokeilla ohjelmaa kokonaisuudessaan alla.
Kuten aiemmin olemme huomanneet, sovelluksessa voi olla useita tilan vaihtamiseen tarkoitettuja funktioita. Alla olevassa esimerkissä on kuvattuna sovellus, jossa näytetään iso kirjain sekä neljä nappia, joissa on pienet kirjaimet. Mikäli sovelluksessa painaa isoa kirjainta vastaavaa pientä kirjainta, pisteiden määrä kasvaa yhdellä; muulloin pisteiden määrä pienenee yhdellä.
Esimerkki kuvastaa miten saman funktion voi antaa parametrina useammalle lapsikomponentille.
Lapsikomponenteille annettavat funktiot voidaan määritellä myös siten, että funktiokutsu saa parametrin. Tämä tehdään tyypillisesti niin, että lapsikomponentille annetaan anonyymi funktio, jonka kutsussa parametri määritellään. Tätä demonstroidaan alla olevassa esimerkissä. Esimerkki näyttää miten sovellukselle voidaan luoda alalaitaan valikko, joka mahdollistaa näytettävän näkymän vaihtamisen.
Alalaidan valikon toteuttaminen
Edellisessä esimerkissä tarkasteltiin alalaidan valikon toteuttamista. Tutustuimme tilan muuttamisen harjoitteluun ja parametrillisen funktion kutsumiseen. Flutter tarjoaa myös valmiin luokan BottomNavigationBar, jota suosittelemme alalaidan valikon toteuttamiseen mikäli sellaisen sovellukseesi tarvitset.
Valmiita komponentteja
Flutter tarjoaa joukon valmiita komponentteja, joihin liittyy tila, ja jotka tarjoavat mahdollisuuden tilan muokkaamiseen. Näitä ovat muunmuassa tekstikentän luomiseen käytetty TextField sekä arvon valintaan käytetty Slider. Tarkastellaan näitä tässä lyhyesti.
Flutterin valmiissa komponenteissa tapahtuvat tilan muutokset käsitellään siten, että komponentti kutsuu sille konstruktorin parametrina annettua funktiota tai metodia. Tämä toiminnallisuus käytännössä vastaa edellä tarkasteltua funktion antamista lapsikomponentille. Siinä missä aiemmissa esimerkeissämme tilan muutoksen käsittelevä funktio ei saanut parametreja, Flutterin valmiissa komponenteissa tilan muutokseen liittyy yleensä uuden tilan arvo -- tämä arvo annetaan parametrina tilan muutosta käsittelevälle funktiolle.
Esimerkiksi tekstikentän TextField
onChanged
attribuutille annettava funktio saa parametrina merkkijonon. Aina kun tekstikenttä muuttuu, TextField
-olio kutsuu parametrina annettua funktiota. Kutsun yhteydessä parametrin arvoksi asetetaan tekstikentässä oleva merkkijono.
Alla olevassa esimerkissä kuvataan tätä toiminnallisuutta. Alla oleva ohjelma pitää yllä tietoa tekstikentässä olevien merkkien määrästä. Aina kun tekstikentässä olevaa merkkijonoa muutetaan, sovelluksessa näytettävää merkkien määrää päivitetään siten, että se vastaa tekstikentässä olevaa merkkien määrää.
Vastaavasti Slider
-luokkaa luodessa onChanged
-attribuutille annetaan parametrina funktio, joka saa parametrinaan desimaaliluvun (liukuluku, double). Aina kun Slider
-olion arvoa muutetaan, Slider
-olio kutsuu sille onChanged
-attribuutin parametrina annettua funktiota. Funktiolle annetaan kutsun yhteydessä parametrina viimeisin arvo.
Alla olevassa esimerkissä Slider-oliota käytetään prosentin valintaan. Valittu prosentti näytetään tekstissä (prosentti on pyöristetty double-tyyppiseen muuttujaan liittyvällä metodilla round
).
Edellä olevassa esimerkissä tekstikentän muutoksia käsitellään erillisen funktion avulla. Tekstikentän muutoksia voi tarkastella myös TextEditingController-luokan avulla.
Lisäämällä TextEditingController
-olio tekstikentälle, tekstikentässä tehdyt muutokset asetetaan TextController
-olion muuttujaan text
. Tähän muuttujaan tehdyt muutokset päivittyvät myös tekstikenttään -- esimerkiksi asettamalla text
-muuttujan arvoksi tyhjä merkkijono, tyhjenee tekstikentässä näkyvä teksti.
Tarkastellaan tätä lyhyesti seuraavassa tehtävässä.
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?