1. Programació d'aplicacions client amb Flet
1. Aplicacions multiplataforma
Són aquelles aplicacions que són desenvolupades en un únic llenguatge o framework i que faciliten la seua exportació i execució a diversos dispositiu, de manera independent al sistema operatiu.
1.1. Aproximacions
Per tal de minimitzar el desenvolupament específic par a cada plataforma, i el cost que això suposa, apareixen diverses tecnologíes, centrades sobretot en tecnologíes web. Dins d’aquestes, disposem d’un gran ventall de possibilitats:
-
Aplicacions web responsives: Es tracta d’aplicacions basades en tecnologia web: HTML, CSS i JavaScript, que adapten la seua interfície a qualsevol dispositiu (disseny responsive). Aquestes aplicacions no requereixen de cap desenvolupament natiu, ja que s’executen sobre el propi navegador web del sistema. Així doncs, disposem d’ún codi únic, però que no ofereixen una experiència d’usuari tan fluïda com les aplicacions natives, ni permeten l’accés a tots els components del sistema.
-
Aplicacions híbrides: Es tracta d’aplicacions web responsives que es carreguen dins un component de tipus WebView del sistema, que no és més que un navegador sense la barra de navegació, pel que presenta l’aparença d’una aplicació nativa. Aquestes aplicacions, a més, també permeten l’accés a través del WebView a algunes característiques del dispositiu, com la ubicació o l’acceleròmetre. El framework per al desenvolupament d’aplicacions híbrides més popular és Ionic, que permet el desenvolupament amb altres frameworks web com React, Angular o Vue.
-
Aplicacions web progressives (PWAs): Les PWAs segueixen sent aplicacions web, però que gràcies a determinats components, com els Service Workers i altres tecnologies estan més a prop de les aplicacions natives, de manera que permeten traure major potencial d’aquestes, accedint al maquinari, treballar amb poca connexió o sense ella, o oferir notificaions del sistema. Existeixen diversos frameworks per al desenvolupament de PWAs, entre els quals es troben React PWA Library, Angular PWA Framework, Vue PWA Framework, Ionic PWA Framework, Svelte, PWA Builder o Polymer.
Com hem comentat, aquests tres tipus d’aplicacions es basen en l’ús de tecnologíes web i algun component, bé siga el propi navegador o un WebView. Tot i que hi ha apliccions molt bones basades en aquestes tecnologíes, no aporten totes les funcionalitats ni la fluïdesa d’una aplicació nativa.
Un pas més enllà en el desenvolupament multiplataforma es troben els frameworks que, partint d’un mateix codi base, generen aplicacions compilades de forma nativa per als diferents sistemes operatius. Algunes de les tecnologies més utilitzades en aquest tipus d’aplicacions són:
-
React Native i Native Script: Que utilitzen com a base el llenguatge de programació JavaScript, però en lloc de construir les interfícies mitjançant HTML, utilitzen components propis del framework que són compilats a codi natiu, fent ja innecessari utilitzar un WebView com a intermediari.
-
Flutter: Aquest framework, creat i mantingut per Google permet el desenvolupament d’aplicacions multiplataforma mitjançant el llenguatge Dart. Aquestes aplicacions són compilades a codi natiu dels diferents sistemes operatius (Android, iOS, Linux, Windows) i fins i tot web. Flutter, és a més la tecnología nativa del sistema operatiu Google Fuchsia, basat en el seu propi microkernel Zircon, i que a mitjà o llarg termini podria ser el reemplaç d’Android.
2. Introducció
En aquest primer apartat de la unitat introduirem les tecnologies que utilitzarem per als nostres desenvolupaments, Python, Flutter i Flet.
2.1. Python

™/®Python Software Foundation, GPL http://www.gnu.org/licenses/gpl.html, via Wikimedia Commons
Python és un llenguatge de programació multiparadigma, interpretat, multiplataforma i lliure. Va nàixer de la mà de Guido Van Rossum, un programador holandés, i la seua primera versió va ser publicada el 1991.
Característiques:
- D'alt nivell: proper a llenguatge de l'ésser humà i no al llenguatge màquina binari.
- Interpretat: s'executa en qualsevol màquina que tinga un intèrpret de Python. Això suposa un gran avantatge a l'hora de fer petits canvis de forma ràpida, ja que elimina la necessitat de recompilar el codi.
- Multiparadigma: podem fer servir la programació modular, estructurada o l'orientació a objectes segons les nostres necessitats.
- Multiplataforma: permet que el codi siga executat en diferents sistemes operatius.
- Lliure: és propietat de la Python Software Foundation i està publicat sota llicència PSF-License que és compatible amb GPL (General Public License), la qual cosa significa que és de lliure ús i distribució, fins i tot per a ús comercial.
- Net i llegible: posa l'accent en la seua llegibilitat, cosa que ho fa fàcilment comprensible i fàcil d'aprendre. Si ja heu treballat amb qualsevol altre llenguatge de programació, us resultarà fàcil l'ús de Python.
- Tipat fort i dinàmic: encara que les variables són d'un tipus concret, no tenim la necessitat de declarar-los, sinó que l'assignació de tipus s'anirà en temps d'execució.
- Àmplia comunitat: gràcies a la seua popularitat compta amb un ampli suport i es pot trobar fàcilment molta documentació, esdeveniments, conferències, etc.
Actualment ocupa el primer lloc al rànquing TIOBE, que és un prestigiós indicador de la popularitat dels llenguatges de programació que s'actualitza un cop al mes. No només això, sinó que a més mostra una tendència creixent davant de llenguatges com C o Java, que mostren la tendència contrària. Això és degut en gran manera al seu ús majoritari en camps com la Intel·ligència Artificial, el Big Data, el Machine Learning o la Ciberseguretat, àrees predominants en un futur proper.
TIOBE
És important assenyalar que l'índex TIOBE no tracta d'escollir el millor llenguatge de programació o el llenguatge en què s'han escrit la majoria de les línies de codi.
Tot això ens ha portat a escollir aquest llenguatge de programació per al present curs.
Vegem la comparació entre “Hola Món!” de Java i de Python
Versions de Python
Hi ha dues versions de python no compatibles entre elles, la versió 2 i la versió 3. Nosaltres utilitzarem la versió 3 de python.
2.2. Flutter
Flutter és un framework de codi obert desenvolupat per Google que es fa servir per a la creació d'aplicacions mòbils per a diferents plataformes, com Android i iOS, així com per a aplicacions per a la web i escriptori. Les seves principals característiques són les següents:
-
Desenvolupament multiplataforma: Flutter permet als desenvolupadors crear una sola base de codi que pot ser utilitzada per a desplegar aplicacions en diverses plataformes. Això significa que pots desenvolupar una aplicació i fer-la funcionar en dispositius Android, iOS, la web i fins i tot en altres plataformes com Windows o macOS.
-
Llenguatge de programació Dart: Flutter fa servir el llenguatge de programació Dart com a base. Dart és un llenguatge modern i eficient que es compila a codi nativo, la qual cosa millora l'eficiència de l'aplicació i el seu rendiment.
-
Widgets personalitzats: Flutter ofereix una àmplia gamma de widgets personalitzats que es poden utilitzar per a crear una interfície d'usuari atractiva i dinàmica. Aquests widgets són altament personalitzables i permeten als desenvolupadors dissenyar aplicacions amb un aspecte i comportament únic.
-
Ràpides actualitzacions d'UI: Flutter utilitza un sistema de composició de l'interfície d'usuari (UI) basat en widgets que permet actualitzacions ràpides i eficients de la UI. Això significa que les animacions i les interaccions són suaus i responsives.
-
Hot Reload: Aquesta característica permet als desenvolupadors veure els canvis immediatament a mesura que editen el codi font, sense necessitat de reiniciar l'aplicació. Això accelera el procés de desenvolupament i la depuració.
-
Suport de tercers: Flutter té un ecosistema actiu i en creixement de paquets i extensions que faciliten l'accés a funcionalitats i integracions diverses. Això fa que sigui fàcil d'integrar amb altres serveis i biblioteques.
-
Altes prestacions i rendiment: Gràcies a la compilació a codi natiu, les aplicacions Flutter solen tenir un bon rendiment i una càrrega ràpida.
-
Comunitat activa: Flutter té una comunitat àmplia i actiu, amb molts recursos en línia, tutorials i ajuda disponible per a desenvolupadors.
En resum, Flutter és una opció atractiva per a desenvolupadors que volen crear aplicacions mòbils i multiplataforma amb un aspecte i comportament personalitzats, així com un alt rendiment i un cicle de desenvolupament eficient. La seva versatilitat i la seva creixent popularitat han fet que sigui una eina rellevant en el món del desenvolupament d'aplicacions.
2.3. Flet
Flet és un framework que permet crear aplicacions GUI tant web, com d'escriptori com per a mòbils. Tot i que el projecte vol incorporar la possibilitat d'utilitzar diversos llenguatges de programació com C++ o Go, actualment sols permet la creació d'aplicacions amb Python.
Els controls de Flet es basen en els controls Flutter de Google, combinant controls més menuts, ocultant complexitats i aplicant valors predeterminats raonables per garantir que les nostres aplicacions tinguen un aspecte professional sense molts esforços.
En definitiva, farem ús dels controls de Flutter des de Python.
3. Components de programari
En el desenvolupament de circuits electrònics, el grau de reutilització de components és molt alt, de manera que la construcció d'un circuit electrònic es limita a la integració i acoblament de diferents components comercials. Per exemple, una placa base conté components com resistències, condensadors, relés, xips…, que no han estat fabricats pel fabricant de la placa. Així que, hi ha fabricants especialitzats en components i altres en producte final. Això permet reduir costos quant a temps i diners es refereix.
En el desenvolupament de programari, la reutilització de codi continua sent relativament escassa, però amb la utilització de components aquest problema es redueix, reduint alhora el temps de desenvolupament, el cost econòmic i els errors de programació.
El desenvolupament de la interfície d'una aplicació es basa en la construcció d'una aplicació a partir de components programari ja existents, anomenats normalment controls, limitant al mínim necessari el desenvolupament de codi nou. Podem imaginar-nos els components com les peces d'un Lego que podem connectar entre ells en una jerarquia d'arbre. Qualsevol aplicació pot estar composta per múltiples components, i els components principals tenen components secundaris niats dins seu.
Vam veure una llista dels principals controls d'usuari a la unitat anterior. En aquesta unitat posarem en pràctica l'ús dels controls o components esmentats amb Flet.

4. Instal·lació de Flet
Flet necessita com a mínim la versió 3.7 de Python, així que el que recomanem és crear un entorn virtual conda amb l'última versió de Python. Una vegada creat i activat, instal·larem flet.
| Bash | |
|---|---|
Ús de Flet amb WSL
Si executeu Flet amb WSL i obteniu l'error error while loading shared libraries: libgstapp-1.0.so.0: cannot open shared object file: No such file or directory, necessitareu instal·lar les llibreries GStreamer.
5. Estructura bàsica d'un programa Flet
L'estructura mínima d'un programa Flet és la següent:
| Python | |
|---|---|
Un programa típic de Flet acaba amb una crida a flet.app() on comença l'aplicació esperant noves sessions d'usuari. La funció main() és el punt d'entrada de l'aplicació. Per a cada sessió d'usuari es crearà un fil d'execució, que se li passarà la instància de Page.
Quan s'executa l'aplicació Flet al navegador, s'inicia una nova sessió d'usuari per a cada pestanya o pàgina oberta. Quan s'executa com a aplicació d'escriptori, només es crea una sessió.
Page és com un llenç específic per a cada usuari, un estat visual d'una sessió d'usuari. Per crear una GUI, afegim i suprimim controls a una pàgina.
Per defecte, l'aplicació Flet s'inicia en una finestra del sistema operatiu natiu, però, podem obrir-la en una finestra del navegador modificant la crida a flet.app() i indicant el port desitjat (aleatori en cas de no especificar-lo):
| Python | |
|---|---|
5.1. Hello world!!
Els controls són els elements bàsics i principals per crear interfícies d'usuari a Flutter. Podeu mostrar informació, rebre informació de l'usuari i organitzar altres controls de forma agrupada.
Exemple
Si executem el codi anterior obtindrem una aplicació com la següent:

Dins la finestra estarà continguda tota la interfície de l'usuari. En aquest cas, només conté un component Text centrar tant verticalment com horitzontalment, sense interacció possible. Cada aplicació gràfica tindrà almenys una finestra, però en podrà tenir més. Això ho veurem en seccions posteriors de la unitat. Normalment una aplicació acabarà en tancar la darrera de les finestres.
6. Controls
La interfície d'usuari està feta de controls (també coneguts com widgets). Per fer visibles els controls per a un usuari, s'han d'afegir a un Page o dins d'altres controls que ja siguen visibles. La pàgina és el control superior, i la resta de controls pengen d'aquesta arrel formant una estructura d'arbre.
Els controls són classes de Python, simplement els instanciarem mitjançant els seus constructors amb els paràmetres que ens convinga, per exemple:
| Python | |
|---|---|
Per mostrar el control anterior, l'afegim a la llista de controls de Page i fem una crida a page.update() per enviar els canvis de pàgina a un navegador o client d'escriptori:
| Python | |
|---|---|
Podem modificar les propietats del control i la interfície d'usuari s'actualitzarà en la següent crida a page.update():
| Python | |
|---|---|
Actualització de controls
El mètode Page.update() només actualitzarà els canvis fets des de l'última crida al mateix mètode. Podeu afegir, eliminar o modificar tants controls com vulgau abans de la seua crida, de forma que l'actualització siga en lot.
Alguns controls són contenidor d'altres controls (com Page). Per exemple, el control Row permet organitzar els controls en fila:
| Python | |
|---|---|

6.1. Esdeveniments
Cada interacció de l'usuari amb la interfície, per exemple un clic de ratolí, un doble clic, l'ús d'una tecla, etc. generarà un esdeveniment. Aquest esdeveniment serà afegit a la cua d'esdeveniments (event queue) per ser gestionat.
El bucle d'esdeveniments (event loop), que és un bucle infinit, comprovarà a cada iteració si hi ha esdeveniments pendents de ser gestionats. En cas de ser així, l'esdeveniment serà gestionat pel gestor d'esdeveniments (event handler) que executarà el vostre controlador. Quan aquest acaba, el control torna al bucle d'esdeveniments per esperar més esdeveniments. Açò ja ho vam utilitzar al joc que desenvoluparem amb PyGame.
Tot aquest procés complex, s'oculta al programados, que simplement haurà de programar quina funcionalitat s'executa en produir-se cert esdeveniment.
Flet vs Flutter
Flet implementa un model d'interfície d'usuari imperatiu on creeu "manualment" la interfície d'usuari de l'aplicació amb controls d'estat i després la modifiqueu actualitzant les propietats de control. En canvi, Flutter implementa un model declaratiu on la interfície d'usuari es reconstrueix automàticament amb els canvis de dades de l'aplicació. La gestió de l'estat de l'aplicació a les aplicacions frontend modernes és una tasca inherentment complexa i l'enfocament de la vella escola de Flet és més atractiu i simple per als programadors sense experiència en el frontend.
Propietat visible
Cada control té la propietat visible que és True per defecte. La configuració visible a False impedeix que el control (i tots els seus fills, si hi ha) es mostren. Els controls ocults no es poden enfocar, ni seleccionar amb un teclat o ratolí i no emeten cap esdeveniment.
Propietat disabled
Cada control té la propietat disabled que és False per defecte, el control i tots els seus fills estan habilitats. S'utilitza principalment amb controls d'entrada de dades com els botons, TextField, Dropdown, Checkbox,... Tanmateix, disabled es podria establir en un control pare i el seu valor es propagarà a tots els fills de forma recursiva.
6.2. Referències a Controls
Els controls Flet són objectes i per accedir a les seves propietats hem de mantenir les referències (variables) a aquests objectes. Quan s'afegeixen molts controls i controladors d'esdeveniments, es fa difícil mantindre totes les definicions de control en un sol lloc, de manera que es dispersen pel cos del main(). Mirant els paràmetres de page.add(), és difícil imaginar (sense mirar constantment a les definicions de variables) com seria la forma final de la interfície (ahí pot ajudar molt el nom de la variable).
| Python | |
|---|---|
Flet proporciona una classe Ref que permet definir una referència al control, utilitzar aquesta referència als controladors d'esdeveniments i establir la referència a un control.
Per definir una nova referència a un control:
| Python | |
|---|---|
Per accedir al control referenciat (desreferència del control), utilitzem la propietat Ref.current:
| Python | |
|---|---|
Per utilitzar la referència al control:
Compareu els següents fragments de codi:
Variables de control vs Referències a controls
Sense Refs
Amb Refs
7. Principals controls en formularis
A la primera unitat vam veure un llistat de controls habituals juntament amb el seu ús habitual. Farem un llistat amb els controls més habituals de Flet usats en formularis, juntament amb algun dels seus esdeveniments. Practicarem amb exemples i exercicis en altres apartats de la unitat. Per obtenir una llista completa i més informació de cadascun, cal consultar la documentació de Flet.
Controls
| Python | |
|---|---|
8. Layouts
Els controls tipus layout són controls especials que servixen per a contindre i organitzar la resta de controls a la interfície. Veurem els principals controls tipus layout a esta secció.
8.1. Container
El contenidor permet decorar un control amb color de fons i vora i posicionar-lo amb farciment, marge i alineació. També permet decorar-lo amb gradients radials o linials, animar-lo,...
Exemple de Container

8.2. Row
Un control que mostra els seus fills en una matriu horitzontal.
Amb les seues propietats es poden modificar la forma en que es col·loquen, l'espai entre els components,...
Espai entre controls

Amplària de la fila

Aliniació horitzontal

expand igual a True
| Python | |
|---|---|

Expanció proporcional

8.3. Column
Un control que mostra els seus fills en una matriu vertical. El funcionament és molt similar a Row, on el width de Row serà el height de Column.
Scroll infinit
8.4. Stack
Un control que situa els seus fills uns sobre els altres, apilats.
Aquest control és útil si volem superposar diversos fills d'una manera senzilla, per exemple, tenint text i una imatge.
Stack també és útil si voleu implementar animacions implícites que requereixen conèixer la posició absoluta d'un valor objectiu.
Layout apilat
8.5. ListView
ListView és el control de desplaçament més utilitzat. Mostra els seus fills un darrere l'altre en la direcció de desplaçament.
Exemple de ListView
| Python | |
|---|---|
8.6. ListTile
Una única fila d'alturaa fixa que normalment conté text, així com una icona inicial o final.
ListTile
8.7. GridView
Es tracta d'un layout en forma de graella.
Informació
GridView és molt eficient per a llistes grans (milers d'elements). Pot substituir el niuament de layouts tipus Row i Column.
Exemple de GridView
8.8. Responsive Row
ResponsiveRow pren la idea del disseny en quadrícula de Bootstrap. Permet alinear els controls en columnes virtuals. Per defecte, una quadrícula virtual té 12 columnes, però que es poden personalitzar amb la propietat ResponsiveRow.columns.
De manera similar a la propietat expand, cada control ara té la propietat col que permet especificar quantes columnes ha d'abastar un control. Per exemple, per fer un disseny format per dues columnes que abasten 6 columnes virtuals cadascuna:
| Python | |
|---|---|
ResponsiveRow és responsiu perquè pot adaptar la mida dels seus fills a una mida de pantalla (pàgina, finestra) dinàmica o canviant. La propietat col de l'exemple anterior és un nombre constant, la qual cosa significa que el control ocuparà 6 columnes per a qualsevol mida de pantalla.
Si als descendents en l'arbre de controls no tenen especificada la propietat col, ocuparan el nombre màxim de columnes.
col es pot configurar per tenir un valor diferent per a "punts d'interrupció" específics. Els punts d'interrupció s'anomenen intervals de dimensions:
Exemple amb ResponsiveRow
8.9. DataTable
Una taula de dades, és un control per a mostrar informació de forma ordenada.
Exemple de taula

8.10. Tabs
El control Tab s'utilitza per navegar per categories de continguts diferents d'accés freqüent. Les pestanyes permeten la navegació entre dues o més visualitzacions de contingut i es basen en les capçaleres de text per articular les diferents seccions de contingut.
Exemple amb Tabs

8.11. Card
El control de targeta és un panell amb vore lleugerament arrodonides i una ombra d'elevació.
Exemple de card

8.12. Divisor (horitzontal o vertical)
Una línia horitzontal fina, amb encoixinat a banda i banda.
Poden ser:
- Horitzontals: Divider
- Verticals: VerticalDivider
Exemple de Divisor

9. Creació de nous controls per codi.
Els controls són utilitzats, entre altres coses, per amagar la complexitat del programari i transformar-la en parts més manejables. Cada control amaga la seva complexitat darrere d'una interfície formada per les seves propietats i mètodes a què es té accés. Poden introduir-se i eliminar-se, fins i tot ser intercanviats com a part dun tot.
Això redueix la complexitat del desenvolupament programari i millora el seu manteniment, permetent que el mateix codi puga ser reutilitzat a diferents llocs. El resultat és un bloc de codi encapsulat en una classe independent que passa a formar part del banc de peces disponibles per formar part de desenvolupaments més complexos.
10. Controls que hereden d'altres controls
Per començar a desenvolupar un control propi, el més fàcil és buscar un control des del qual partir, de manera que se n'hereten les propietats i mètodes i així poder utilitzar-los en el control que volem crear.
Per exemple, si voleu un Container amb un identificador centrat, podem heretar de flet.Container i afegir una propietat id:
Exemple
11. Controls que hereden d'UserControl
En cas que el nostre control no guarde relació estreta amb altres controls o siga una composició de diversos controls, el més adequat serà heretar de ft.UserControl directament, que és la classe base des de la qual hereta la resta de Controls d'usuari. Això ens permetrà construir components reutilitzables combinant els controls Flet existents.
Exemple mínim de control d'usuari
UserControl ha d'implementar el mètode build() que es crida per crear la interfície d'usuari del control i ha de retornar una únic objecte Control o una Llista de controls. UserControls hereda d'Stack, de manera que hi haurà diversos fills els uns sobre els altres. Si necessiteu organitzar la interfície d'usuari del control de manera diferent, podeu utilitzar Layouts com Row o Column.
Exemple
Actualització d'UserControl a la interfície
Quan es crida al mètode update() del control principal, els canvis dins del UserControl no s'apliquen, i per tant, la seua vista no es refresca. UserControl hauria de cridar self.update() per enviar els seus canvis a una pàgina de Flet.
Fixeu-se que a les línies 13 i 18, el control s'actualitza a ell mateix. Proveu a canviar per e.page.update() i comproveu el funcionament.