{"id":5974,"date":"2022-04-15T14:51:15","date_gmt":"2022-04-15T14:51:15","guid":{"rendered":"https:\/\/cloudsurfers.it\/?p=5974"},"modified":"2022-04-15T14:51:15","modified_gmt":"2022-04-15T14:51:15","slug":"sviluppare-applicazione-flutter-parte-1","status":"publish","type":"post","link":"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/","title":{"rendered":"Sviluppare un&#8217;applicazione Flutter &#8211; Parte 1"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover.png\" alt=\"\" class=\"wp-image-5975\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover.png 800w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover-300x150.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover-768x384.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure><\/div>\n\n\n\n<p>Questo sar\u00e0 il primo di una serie di articoli dove andremo a realizzare passo passo un&#8217;applicazione mobile multipiattaforma, Android ed iOS, con le seguenti funzionalit\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Supporto multilingua;<\/li><li>Autenticazione tramite uno strato API web con relativa gestione del token di autenticazione per eseguire chiamate API autenticate;<\/li><li>Base dati SQLite locale con salvataggio ed interrogazione di dati.<\/li><\/ul>\n\n\n\n<p>Vedremo inoltre alcune finezze per l&#8217;abbellimento finale ed il miglioramento della nostra applicazione, come ad esempio il supporto alla modalit\u00e0 scura.<\/p>\n\n\n\n<p>Il tutto verr\u00e0 realizzato tramite il framework open source <a href=\"https:\/\/flutter.dev\/\" data-type=\"URL\" data-id=\"https:\/\/flutter.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter <\/a>ed il linguaggio di programmazione <a href=\"https:\/\/dart.dev\/\" data-type=\"URL\" data-id=\"https:\/\/dart.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">Dart<\/a>. <\/p>\n\n\n\n<p>Vediamolo pi\u00f9 nel dettaglio&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Che cos&#8217;\u00e8 Flutter?<\/h3>\n\n\n\n<p>Flutter, direttamente dal <a href=\"https:\/\/flutter.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">sito ufficiale<\/a>, \u00e8 un framework open source realizzato da <a href=\"http:\/\/www.google.com\" data-type=\"URL\" data-id=\"www.google.com\" target=\"_blank\" rel=\"noreferrer noopener\">Google<\/a>, per lo sviluppo di applicazioni multi piattaforma, compilate nativamente, partendo da un singolo codice sorgente. Per intenderci, un rivale del framework <a href=\"https:\/\/ionicframework.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Ionic<\/a>.<\/p>\n\n\n\n<p>Per chi ha gi\u00e0 utilizzato&nbsp;<a href=\"https:\/\/ionicframework.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Ionic<\/a>, appunto, ecco una tabella comparativa che ne evidenzia le differenze:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"807\" height=\"396\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image.png\" alt=\"\" class=\"wp-image-5978\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image.png 807w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-300x147.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-768x377.png 768w\" sizes=\"auto, (max-width: 807px) 100vw, 807px\" \/><figcaption><em><a href=\"https:\/\/ionic.io\/resources\/articles\/ionic-vs-flutter-comparison-guide\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/ionic.io\/resources\/articles\/ionic-vs-flutter-comparison-guide<\/a><\/em><\/figcaption><\/figure><\/div>\n\n\n\n<p>I due framework sono molto simili dal punto di vista di ci\u00f2 che viene offerto, community, pacchetti disponibili e dalla visione condivisa che hanno, un solo codice sorgente, deploy mobile, desktop e web.<\/p>\n\n\n\n<p>La scelta quindi, pi\u00f9 che da un esigenza di progetto, pu\u00f2 essere dettata meramente da gusto e\/o conoscenze personali.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Requisiti ed installazione<\/h3>\n\n\n\n<p>Per iniziare a sviluppare utilizzando Flutter, bisogna prima eseguire l&#8217;installazione dell&#8217;SDK sulla macchina che stiamo utilizzando. L&#8217;SDK \u00e8 disponibile per diversi sistemi operativi: Windows, macOS, Linux e Chrome OS (<a href=\"https:\/\/docs.flutter.dev\/get-started\/install\" data-type=\"URL\" data-id=\"https:\/\/docs.flutter.dev\/get-started\/install\" target=\"_blank\" rel=\"noreferrer noopener\">link<\/a>).<\/p>\n\n\n\n<p>In questa guida seguiremo la procedura per l&#8217;installazione su una macchina con sistema operativo Windows. <\/p>\n\n\n\n<p>Iniziamo&#8230;<\/p>\n\n\n\n<p>Per prima cosa assicuriamoci di soddisfare i requisiti minimi richiesti per eseguire l&#8217;installazione:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Windows 7 SP1 o superiore (64bit) come sistema operativo;<\/li><li>Almeno 1,64 GB disponibili sul disco per l&#8217;SDK Flutter;<\/li><li><a href=\"https:\/\/docs.microsoft.com\/en-us\/powershell\/scripting\/install\/installing-windows-powershell\" target=\"_blank\" rel=\"noreferrer noopener\">Windows PowerShell 5.0<\/a>&nbsp;o superiore (preinstallato se utilizzate Windows 10\/11);<\/li><li><a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio Code<\/a> come editor;<\/li><li><a href=\"https:\/\/developer.android.com\/studio\" target=\"_blank\" rel=\"noreferrer noopener\">Android Studio + SDK Android<\/a>&nbsp;per eseguire il deploy come applicazione Android su emulatore o su dispositivo fisico.<\/li><\/ul>\n\n\n\n<p>Verificati ed allineati ai requisiti richiesti, scarichiamo l&#8217;ultima versione stabile di Flutter per Windows (all&#8217;istante di scrittura di questa guida <strong>2.10.4<\/strong>) dal seguente&nbsp;<a href=\"https:\/\/storage.googleapis.com\/flutter_infra_release\/releases\/stable\/windows\/flutter_windows_2.10.4-stable.zip\" target=\"_blank\" rel=\"noreferrer noopener\">link<\/a>.<\/p>\n\n\n\n<p>Estraiamo il contenuto dell&#8217;archivio in una cartella di nostra preferenza, per esempio,&nbsp;<code>C:\\src\\flutter<\/code>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><em>Nota: il percorso in cui viene estratto l&#8217;archivio non deve contenere caratteri speciali e non deve essere estratto in cartelle che richiedono privilegi elevati per la scrittura su di esse, come ad esempio la cartella&nbsp;<code>C:\\Program Files\\<\/code>.<\/em><\/p><\/blockquote>\n\n\n\n<p>Per poter utilizzare i comandi Flutter all&#8217;interno del prompt dei comandi di Windows, aggiungiamo il percorso alla cartella&nbsp;<code>flutter\\bin<\/code>&nbsp;estratta in precedenza, alla variabile d&#8217;ambiente&nbsp;<em>Path<\/em>&nbsp;dell&#8217;utente corrente.<\/p>\n\n\n\n<p>Riavviamo il prompt dei comandi, se gi\u00e0 avviato, e lanciamo il comando&nbsp;<code>flutter doctor<\/code>&nbsp;per verificare che tutti gli strumenti siano installati e funzionino correttamente. Dovremo ottenere una risposta simile alla seguente:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"979\" height=\"344\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-1.png\" alt=\"\" class=\"wp-image-5980\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-1.png 979w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-1-300x105.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-1-768x270.png 768w\" sizes=\"auto, (max-width: 979px) 100vw, 979px\" \/><\/figure><\/div>\n\n\n\n<p>Il comando ci mostra se tutti gli strumenti sono installati e configurati correttamente. Nel caso sopra ci viene mostrato per esempio un problema con il software&nbsp;<em>Visual Studio Enterprise 2022<\/em>&nbsp;in quando manca il componente&nbsp;<em>Desktop development with C++<\/em>&nbsp;necessario per il deploy di applicazioni Windows (non utile per\u00f2 per la nostra applicazione Android al momento).<\/p>\n\n\n\n<p>I punti che ci interessano sono:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Flutter;<\/li><li>Android toolchain;<\/li><li>Android Studio;<\/li><li>VS Code.<\/li><\/ul>\n\n\n\n<p>Se questi punti sono&nbsp;confermati con il checkmark \u2714 che indica <em>OK<\/em>, possiamo proseguire.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Visual Studio Code<\/h3>\n\n\n\n<p>Per lo sviluppo di un applicativo Flutter, la documentazione ufficiale propone tre strumenti ideali:&nbsp;<em>Android Studio con IntelliJ<\/em>,&nbsp;<em>Visual Studio Code<\/em>&nbsp;ed&nbsp;<em>Emacs<\/em>.<\/p>\n\n\n\n<p>Noi, per questa serie di articoli, utilizzeremo&nbsp;<strong>Visual Studio Code<\/strong>.<\/p>\n\n\n\n<p>Apriamo quindi <em>VSCode<\/em> e rechiamoci alla sezione delle estensioni aggiuntive, dal pannello laterale sinistro. Cerchiamo, scrivendo nella barra di ricerca, l&#8217;estensione <em>Flutter <\/em>ed installiamola:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1017\" height=\"259\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-2.png\" alt=\"\" class=\"wp-image-5981\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-2.png 1017w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-2-300x76.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-2-768x196.png 768w\" sizes=\"auto, (max-width: 1017px) 100vw, 1017px\" \/><\/figure><\/div>\n\n\n\n<p>Terminata l&#8217;installazione, dalla&nbsp;<em>Command Palette&#8230;<\/em>&nbsp;(Ctrl+Shift+P) cerchiamo e lanciamo il comando&nbsp;<code>Flutter: Run Flutter Doctor<\/code>. <\/p>\n\n\n\n<p>Nel caso di errori o problematiche ci verranno mostrati degli avvisi con una proposta di intervento da attuare per correggere il problema. Se non viene mostrato nulla, invece, il comando \u00e8 andato a buon fine e siamo pronti per iniziare un nuovo progetto Flutter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creazione del progetto<\/h3>\n\n\n\n<p>Dalla&nbsp;<em>Command Palette&#8230;<\/em>&nbsp;(Ctrl+Shift+P) di <em>VSCode<\/em>, cerchiamo e lanciamo il comando&nbsp;<code>Flutter: New Project<\/code>. <\/p>\n\n\n\n<p>Selezioniamo successivamente&nbsp;<code>Application<\/code>.<\/p>\n\n\n\n<p>Dalla finestra popup selezioniamo la cartella dove verr\u00e0 salvato il nuovo progetto. Per esempio&nbsp;<code>C:\\repos\\demo_flutter<\/code>.<\/p>\n\n\n\n<p>Infine, quando richiesto, il nome dell&#8217;applicazione. Nel nostro esempio&nbsp;<code>demo_application<\/code>.<\/p>\n\n\n\n<p>Terminata la procedura ci troveremo davanti il progetto appena creato:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"952\" height=\"685\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-3.png\" alt=\"\" class=\"wp-image-5983\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-3.png 952w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-3-300x216.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-3-768x553.png 768w\" sizes=\"auto, (max-width: 952px) 100vw, 952px\" \/><\/figure><\/div>\n\n\n\n<p>Proviamo a compilare ed eseguire l&#8217;applicazione per verificare se tutto funziona correttamente. <\/p>\n\n\n\n<p>Per farlo selezioniamo l&#8217;emulatore Android o il dispositivo fisico collegato tramite <em>ADB <\/em>(<em>USB <\/em>o <em>Wifi<\/em>) nel pannello in basso a destra di <em>VSCode<\/em>:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"136\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-4.png\" alt=\"\" class=\"wp-image-5984\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-4.png 895w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-4-300x46.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-4-768x117.png 768w\" sizes=\"auto, (max-width: 895px) 100vw, 895px\" \/><\/figure><\/div>\n\n\n\n<p>Avviamo poi l&#8217;applicazione, menu&nbsp;<code>Run<\/code>, <code>Start Debuggging<\/code>&nbsp;o pi\u00f9 semplicemente il tasto&nbsp;<em>F5<\/em>&nbsp;sulla tastiera.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"793\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-5-1024x793.png\" alt=\"\" class=\"wp-image-5985\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-5-1024x793.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-5-300x232.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-5-768x595.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-5.png 1198w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p>Se otterrete la schermata sopra, tutto funziona come deve.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>La funzionalit\u00e0 del linguaggio&nbsp;<em>Dart<\/em>&nbsp;<strong>Hot Reload<\/strong>&nbsp;\u00e8 abilitata di default, modificando quindi una stringa nel codice, per esempio la stringa &#8220;<em>Flutter Demo Home Page<\/em>&#8221; in &#8220;<em>Demo Application<\/em>&#8220;, noteremo che la modifica viene mostrata sul dispositivo (emulato o fisico) direttamente, senza necessit\u00e0 di ricompilare o rieseguire l&#8217;app.<\/p><\/blockquote>\n\n\n\n<p>Prima di proseguire nello sviluppo della nostra applicazione, vediamo che strumenti di <em>debug <\/em>Flutter ci mette a disposizione.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dart DevTools<\/h3>\n\n\n\n<p>Per eseguire il debug completo dell&#8217;applicazione, possiamo utilizzare <em>Dart DevTools<\/em>, uno strumento di&nbsp;<em>debugging<\/em>&nbsp;del tutto simile, per esempio, ai&nbsp;<em>Chrome DevTools<\/em>&nbsp;che si utilizzano nello sviluppo Web su&nbsp;<em>Google Chrome<\/em>&nbsp;o nello sviluppo di applicazioni&nbsp;<em>Ionic<\/em> (per chi venisse da quel mondo).<\/p>\n\n\n\n<p>Nel pannello in basso a destra di <em>VSCode<\/em> \u00e8 presente un collegamento rapido per accederci,&nbsp;<code>Dart DevTools<\/code>, clicchiamolo e selezioniamo&nbsp;<code>Open DevTools in Web Browser<\/code>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-6-1024x555.png\" alt=\"\" class=\"wp-image-5987\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-6-1024x555.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-6-300x163.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-6-768x416.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-6.png 1403w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p>VI rimando al <a href=\"https:\/\/dart.dev\/tools\/dart-devtools\" data-type=\"URL\" data-id=\"https:\/\/dart.dev\/tools\/dart-devtools\" target=\"_blank\" rel=\"noreferrer noopener\">sito ufficiale<\/a> per approfondire lo strumento.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Struttura del progetto<\/h3>\n\n\n\n<p>Vediamo ora pi\u00f9 nel dettaglio come \u00e8 strutturato un progetto Flutter.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"218\" height=\"424\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-7.png\" alt=\"\" class=\"wp-image-5989\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-7.png 218w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-7-154x300.png 154w\" sizes=\"auto, (max-width: 218px) 100vw, 218px\" \/><figcaption>Struttura di un progetto di tipo <em>Application<\/em> appena creato<\/figcaption><\/figure><\/div>\n\n\n\n<p>Le cartelle&nbsp;<code>android<\/code>&nbsp;ed&nbsp;<code>ios<\/code>&nbsp;contengono i progetti rispettivi&nbsp;<em>Android Studio<\/em>&nbsp;ed&nbsp;<em>XCode<\/em>.<\/p>\n\n\n\n<p>Le cartelle&nbsp;<code>web<\/code>&nbsp;e&nbsp;<code>windows<\/code>, invece, contengono i progetti per il deploy Web o Windows appunto.<\/p>\n\n\n\n<p>La cartella&nbsp;<code>lib<\/code>&nbsp;contiene la logica dell&#8217;applicazione, quella che andremo a sviluppare per intenderci, quindi tutti i file&nbsp;<em>.dart<\/em>&nbsp;in linguaggio&nbsp;<em>Dart<\/em>&nbsp;e i file&nbsp;<em>.arb<\/em>&nbsp;per le traduzioni per esempio (queste le vedremo nella seconda parte di questa serie di articoli).<\/p>\n\n\n\n<p>Questa cartella, per ottimizzare la lettura del codice, pu\u00f2 essere a sua volta suddivisa come segue:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>pages<\/code>&nbsp;&#8211; i file&nbsp;<em>.dart<\/em>&nbsp;che identificano le pagine dell&#8217;applicazione. Per esempio:&nbsp;<code>login.dart<\/code>,&nbsp;<code>main.dart<\/code>, ecc&#8230;;<\/li><li><code>components<\/code>&nbsp;&#8211; i file&nbsp;<em>.dart<\/em>&nbsp;che identificano componenti che vengono riutilizzati all&#8217;interno delle pagine sopra per esempio. Se abbiamo un oggetto che utilizziamo pi\u00f9 volte in pi\u00f9 pagine dell&#8217;applicativo, \u00e8 buona norma estrapolare la sua logica dalla logica della pagina stessa e creare un componente esterno, magari parametrizzabile per personalizzazioni future, evitando cos\u00ec ridondanza di codice;<\/li><li><code>l10n<\/code>&nbsp;&#8211; pu\u00f2 contenere i file&nbsp;<em>.arb<\/em>&nbsp;in lingua per le traduzioni. Per esempio&nbsp;<code>intl_en.arb<\/code>,&nbsp;<code>intl_it.arb<\/code>, ecc&#8230;;<\/li><li><code>providers<\/code>&nbsp;&#8211; contenente i file&nbsp;<em>.dart<\/em>&nbsp;delle varie classi&nbsp;<em>singleton<\/em>&nbsp;utilizzate in vari punti dell&#8217;applicazione. In questa sezione rientra per esempio il provider delle API web&nbsp;<code>api_provider.dart<\/code>&nbsp;(che vedremo nei prossimi articoli).<\/li><\/ul>\n\n\n\n<p>Il file&nbsp;<code>pubspec.yaml<\/code>&nbsp;definisce alcuni aspetti del progetto, dal nome, la descrizione, i pacchetti dipendenza del progetto, fino ad alcune impostazioni di Flutter come per esempio l&#8217;opzione&nbsp;<code>uses-material-design<\/code>&nbsp;che indica se l&#8217;applicativo deve usare o meno il&nbsp;<em>Material Design<\/em>&nbsp;per l&#8217;interfaccia grafica.<br>Questo file \u00e8 comparabile al file&nbsp;<code>package.json<\/code>&nbsp;di un progetto&nbsp;<em>Angular<\/em>.<\/p>\n\n\n\n<p>Iniziamo con lo sviluppo vero e proprio&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pagina di Login<\/h3>\n\n\n\n<p>Creiamo all&#8217;interno della cartella&nbsp;<code>lib<\/code>&nbsp;una nuova cartella&nbsp;<code>pages<\/code>&nbsp;ed al suo interno creiamo un nuovo file chiamato&nbsp;<code>login.dart<\/code>. Questo file conterr\u00e0 la logica della nostra pagina di autenticazione.<\/p>\n\n\n\n<p>All&#8217;interno del file&nbsp;<code>login.dart<\/code>&nbsp;importiamo il componente&nbsp;<em>Material<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;package:flutter\/material.dart&#039;;\n<\/pre><\/div>\n\n\n<p>e creiamo una nuova classe chiamata&nbsp;<strong>LoginPage<\/strong>&nbsp;che estende una classe astratta&nbsp;<em>StatefulWidget<\/em>&nbsp;(questa classe astratta a sua volta estende un&#8217;oggetto&nbsp;<em>Widget<\/em>. Tutti gli elementi grafici in Flutter sono&nbsp;<em>Widget<\/em>) come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass LoginPage extends StatefulWidget {\n  const LoginPage({Key? key}) : super(key: key);\n}\n<\/pre><\/div>\n\n\n<p>Creiamo ora, sempre all&#8217;interno dello stesso file, una classe che estende lo stato del nostro nuovo&nbsp;<em>StatefulWidget<\/em>&nbsp;<strong>LoginPage<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass _LoginState extends State&lt;LoginPage&gt; {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text(&quot;Login&quot;),\n      ),\n      body: const Center(\n        child: Text(&quot;Demo&quot;),\n      ),\n    );\n  }\n}\n<\/pre><\/div>\n\n\n<p>Torniamo poi all&#8217;interno della classe&nbsp;<strong>LoginPage<\/strong>&nbsp;ed implementiamo il metodo&nbsp;<em>createState()<\/em>&nbsp;indicando la nuova classe stato&nbsp;<strong>_LoginState<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass LoginPage extends StatefulWidget {\n  const LoginPage({Key? key}) : super(key: key);\n\n  @override\n  State&lt;LoginPage&gt; createState() =&gt; _LoginState();\n}\n<\/pre><\/div>\n\n\n<p>A questo punto abbiamo creato una nuova classe&nbsp;<strong>LoginPage<\/strong>&nbsp;che, istanziata, disegna un&#8217;oggetto&nbsp;<a href=\"https:\/\/api.flutter.dev\/flutter\/material\/Scaffold-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/Scaffold-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Scaffold<\/em>&nbsp;<\/a>(oggetto che indica una pagina in stile&nbsp;<em>Material<\/em>) con un&#8217;app bar contenente un titolo (stringa &#8220;Login&#8221; nel nostro esempio) ed un corpo con un&#8217;oggetto di tipo testo al centro (stringa &#8220;Demo&#8221;).<\/p>\n\n\n\n<p>Ora non resta che impostare l&#8217;applicazione in modo tale da avviare la pagina di autenticazione&nbsp;<strong>LoginPage<\/strong>&nbsp;anzich\u00e9 l&#8217;attuale pagina&nbsp;<strong>MyHomePage<\/strong> all&#8217;avvio. <\/p>\n\n\n\n<p>Per fare questo apriamo il file&nbsp;<code>main.dart<\/code>&nbsp;all&#8217;interno dell&#8217;editor ed eliminiamo le classi&nbsp;<strong>MyHomePage<\/strong>&nbsp;e&nbsp;<strong>_MyHomePageState<\/strong>.<\/p>\n\n\n\n<p>Importiamo il componente&nbsp;<code>login.dart<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;package:demo_application\/pages\/login.dart&#039;;\n<\/pre><\/div>\n\n\n<p>ed impostiamo la propriet\u00e0&nbsp;<em>home<\/em>&nbsp;dell&#8217;oggetto&nbsp;<a href=\"https:\/\/api.flutter.dev\/flutter\/material\/MaterialApp-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/MaterialApp-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>MaterialApp<\/em>&nbsp;<\/a>con l&#8217;oggetto&nbsp;<strong>LoginPage<\/strong>&nbsp;creato in precedenza:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass MyApp extends StatelessWidget {\n  const MyApp({Key? key}) : super(key: key);\n\n  \/\/ This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: &#039;Flutter Demo&#039;,\n      theme: ThemeData(\n        \/\/ This is the theme of your application.\n        \/\/\n        \/\/ Try running your application with &quot;flutter run&quot;. You&#039;ll see the\n        \/\/ application has a blue toolbar. Then, without quitting the app, try\n        \/\/ changing the primarySwatch below to Colors.green and then invoke\n        \/\/ &quot;hot reload&quot; (press &quot;r&quot; in the console where you ran &quot;flutter run&quot;,\n        \/\/ or simply save your changes to &quot;hot reload&quot; in a Flutter IDE).\n        \/\/ Notice that the counter didn&#039;t reset back to zero; the application\n        \/\/ is not restarted.\n        primarySwatch: Colors.blue,\n      ),\n      home: const LoginPage(),\n    );\n  }\n}\n<\/pre><\/div>\n\n\n<p>Eseguendo l&#8217;applicazione con <em>F5 <\/em>otteniamo il seguente risultato:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-8.png\" alt=\"\" class=\"wp-image-5996\" width=\"284\" height=\"600\"\/><\/figure><\/div>\n\n\n\n<p>Utilizzando i&nbsp;<a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Widget-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Widget-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Widget<\/em>&nbsp;<\/a>a nostra disposizione, disegniamo un modulo form di autenticazione:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass _LoginState extends State&lt;LoginPage&gt; {\n  String? _email;\n  String? _password;\n\n  final GlobalKey&lt;FormState&gt; _formKey = GlobalKey&lt;FormState&gt;();\n\n  Widget _buildEmail() {\n    return Padding(\n      padding: const EdgeInsets.all(8.0),\n      child: TextFormField(\n        keyboardType: TextInputType.emailAddress,\n        decoration: const InputDecoration(\n          labelText: &quot;Email&quot;,\n          border: OutlineInputBorder(),\n        ),\n        validator: (String? value) {\n          if (value == null || value.isEmpty) {\n            return &#039;Email richiesta&#039;;\n          }\n          return null;\n        },\n        onSaved: (String? value) {\n          _email = value;\n        },\n      ),\n    );\n  }\n\n  Widget _buildPassword() {\n    return Padding(\n      padding: const EdgeInsets.all(8.0),\n      child: TextFormField(\n        obscureText: true,\n        decoration: const InputDecoration(\n          labelText: &quot;Password&quot;,\n          border: OutlineInputBorder(),\n        ),\n        validator: (String? value) {\n          if (value == null || value.isEmpty) {\n            return &#039;Password richiesta&#039;;\n          }\n          return null;\n        },\n        onSaved: (String? value) {\n          _password = value;\n        },\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: const Text(&quot;Login&quot;),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Center(\n          child: Form(\n            key: _formKey,\n            child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: &#x5B;\n                const SizedBox(\n                  width: 200,\n                  height: 100,\n                  child: FlutterLogo(),\n                ),\n                const SizedBox(height: 8),\n                _buildEmail(),\n                _buildPassword(),\n                const SizedBox(height: 8),\n                ElevatedButton(\n                  onPressed: () {\n                    if (!_formKey.currentState!.validate()) {\n                      return;\n                    }\n\n                    _formKey.currentState!.save();\n\n                    print(_email);\n                    print(_password);\n                  },\n                  child: const SizedBox(\n                    width: 200,\n                    height: 50,\n                    child: Center(\n                      child: Text(\n                        &quot;Login&quot;,\n                        textScaleFactor: 1.5,\n                      ),\n                    ),\n                  ),\n                )\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n<\/pre><\/div>\n\n\n<p>Eseguendo otteniamo il seguente risultato:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-9.png\" alt=\"\" class=\"wp-image-5998\" width=\"284\" height=\"600\"\/><\/figure><\/div>\n\n\n\n<p>Premendo il pulsante&nbsp;<em>Login<\/em>&nbsp;il modulo form verr\u00e0 validato (<code>_formKey.currentState!.validate()<\/code>):<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-10.png\" alt=\"\" class=\"wp-image-5999\" width=\"284\" height=\"185\"\/><\/figure><\/div>\n\n\n\n<p>Inserendo i campi richiesti invece, i valori dei due, <em>Email<\/em>&nbsp;e&nbsp;<em>Password<\/em>, verranno stampati&nbsp;all&#8217;interno della console:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"855\" height=\"364\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-11.png\" alt=\"\" class=\"wp-image-6000\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-11.png 855w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-11-300x128.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-11-768x327.png 768w\" sizes=\"auto, (max-width: 855px) 100vw, 855px\" \/><\/figure><\/div>\n\n\n\n<p>Vediamo ora pi\u00f9 nel dettaglio le parti essenziali del codice scritto sopra.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nString? _email;\nString? _password;\n\nfinal GlobalKey&lt;FormState&gt; _formKey = GlobalKey&lt;FormState&gt;();\n<\/pre><\/div>\n\n\n<p>In questo punto vengono dichiarate le due variabili&nbsp;di tipo <em>String<\/em>&nbsp;<em>NULLABLE<\/em>&nbsp;che conterranno i due valori&nbsp;<em>Email<\/em>&nbsp;e&nbsp;<em>Password<\/em>&nbsp;e viene dichiarata ed assegnata la variabile di tipo&nbsp;<a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/GlobalKey-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/widgets\/GlobalKey-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>GlobalKey<\/em>&nbsp;<\/a>che identifica la chiave univoca dell&#8217;oggetto di tipo&nbsp;<em><a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Form-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Form-class.html\" target=\"_blank\" rel=\"noreferrer noopener\">Form<\/a><\/em>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nWidget _buildEmail() {\n    return Padding(\n      padding: const EdgeInsets.all(8.0),\n      child: TextFormField(\n        keyboardType: TextInputType.emailAddress,\n        decoration: const InputDecoration(\n          labelText: &quot;Email&quot;,\n          border: OutlineInputBorder(),\n        ),\n        validator: (String? value) {\n          if (value == null || value.isEmpty) {\n            return &#039;Email richiesta&#039;;\n          }\n          return null;\n        },\n        onSaved: (String? value) {\n          _email = value;\n        },\n      ),\n    );\n}\n<\/pre><\/div>\n\n\n<p>Questo metodo, cos\u00ec come il metodo&nbsp;<strong>_buildPassword()<\/strong>, serve per separare la dichiarazione dei vari componenti&nbsp;<em>Widget<\/em>&nbsp;migliorando cos\u00ec la lettura e manutenzione futura del codice. L&#8217;oggetto <em>Widget <\/em>che ritorna da questo metodo <strong>_buildEmail()<\/strong>, pu\u00f2 essere scritto direttamente all&#8217;interno del metodo <strong>build()<\/strong>, rendendone per\u00f2 la lettura molto nidificata e confusionaria.<\/p>\n\n\n\n<p>Qui per esempio viene dichiarato il campo&nbsp;<em>Email<\/em>, widget&nbsp;<a href=\"https:\/\/api.flutter.dev\/flutter\/material\/TextFormField-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/TextFormField-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>TextFormField<\/em>&nbsp;<\/a>e le sue propriet\u00e0 per impostarne il comportamento e la sua visualizzazione a schermo:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>keyboardType: TextInputType.emailAddress<\/code>&nbsp;seleziona il tipo di tastiera che il sistema visualizzer\u00e0 quando il campo viene selezionato. In questo caso verr\u00e0 mostrata una tastiera con il carattere @ a vista per agevolare l&#8217;inserimento di un indirizzo email;<\/li><li><code>labelText: \"Email\"<\/code>&nbsp;indica la stringa mostrata come etichetta del campo;<\/li><li><code>border: OutlineInputBorder()<\/code>&nbsp;indica il tipo di bordo del campo, in questo caso il classico bordo&nbsp;<em><a href=\"https:\/\/material.io\/components\/text-fields#usage\" data-type=\"URL\" data-id=\"https:\/\/material.io\/components\/text-fields#usage\" target=\"_blank\" rel=\"noreferrer noopener\">Outlined Material<\/a><\/em>.<\/li><li><code>validator: (String? value) {}<\/code>&nbsp;contiene la validazione che il valore del campo deve rispettare e il possibile errore mostrato nel caso non venga rispettata.<\/li><li><code>onSaved: (String? value) {}<\/code>&nbsp;indica le azioni da fare al salvataggio del modulo <em>Form<\/em>. In questo caso il valore del campo viene assegnato alla variabile&nbsp;<strong>_email<\/strong>&nbsp;creata in precedenza.<\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nElevatedButton(\n  onPressed: () {\n    if (!_formKey.currentState!.validate()) {\n      return;\n    }\n\n    _formKey.currentState!.save();\n\n    print(_email);\n    print(_password);\n  },\n  child: const SizedBox(\n    width: 200,\n    height: 50,\n    child: Center(\n      child: Text(\n        &quot;Login&quot;,\n        textScaleFactor: 1.5,\n      ),\n    ),\n  ),\n)\n<\/pre><\/div>\n\n\n<p>Il codice sopra disegna all&#8217;interno della pagina il pulsante&nbsp;<strong>Login<\/strong>&nbsp;ed imposta il suo comportamento, oltre che la sua etichetta.<\/p>\n\n\n\n<p>Invocato l&#8217;evento&nbsp;<code>onPressed: () {}<\/code>, il modulo <em>Form<\/em> visto sopra, identificato dalla variabile&nbsp;<strong>_formKey<\/strong>, viene validato e, se corretto, salvato, cos\u00ec da popolare correttamente le variabili&nbsp;<strong>_email<\/strong>&nbsp;e&nbsp;<strong>_password<\/strong>&nbsp;(come visto all&#8217;interno dei metodi&nbsp;<strong>_buildEmail()<\/strong>&nbsp;e&nbsp;<strong>_buildPassword()<\/strong>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prossimamente&#8230;<\/h3>\n\n\n\n<p>Nel prossimo articolo vedremo come rendere la nostra applicazione multilingua ed integreremo un provider per interagire con uno strano API web.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GitHub<\/h3>\n\n\n\n<p>Il progetto Flutter realizzato in questo articolo \u00e8 disponibile su <a href=\"https:\/\/github.com\/Cloudsurfers-Dev\/flutter_demo_application_pt1\" data-type=\"URL\" data-id=\"https:\/\/github.com\/Cloudsurfers-Dev\/flutter_demo_application_pt1\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fonti<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/flutter.dev\/\">https:\/\/flutter.dev\/<\/a><\/li><li><a href=\"https:\/\/dart.dev\/\">https:\/\/dart.dev\/<\/a><\/li><li><a href=\"https:\/\/material.io\/design\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/material.io\/design<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p><!-- wp:paragraph --><\/p>\n<p>Questo sar\u00e0 il primo di una serie di articoli dove andremo a realizzare passo passo un&#8217;applicazione mobile multipiattaforma, Android ed iOS, con le seguenti funzionalit\u00e0:<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n<p><!-- wp:list --><\/p>\n<ul>\n<li>Supporto multilingua;<\/li>\n<li>Autenticazione tramite uno strato API web con relativa gestione del token di autenticazione per eseguire chiamate API autenticate;<\/li>\n<li>Base dati SQLite locale con salvataggio ed interrogazione di dati.<\/li>\n<\/ul>\n<p><!-- \/wp:list --><\/p>\n","protected":false},"author":3,"featured_media":5975,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wds_primary_category":0,"footnotes":""},"categories":[185,181,36],"tags":[96,184,182,183,110],"class_list":["post-5974","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dart","category-flutter","category-guide","tag-app","tag-dart","tag-flutter","tag-mobile","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/5974","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/comments?post=5974"}],"version-history":[{"count":0,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/5974\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media\/5975"}],"wp:attachment":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media?parent=5974"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/categories?post=5974"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/tags?post=5974"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}