Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 32 di 37
  • livello intermedio
Indice lezioni

Organizzazione del Progetto

Flutter lascia allo sviluppatore la libertà di scegliere come organizzare il proprio progetto: in questa lezione vedremo come organizzarlo al meglio.
Flutter lascia allo sviluppatore la libertà di scegliere come organizzare il proprio progetto: in questa lezione vedremo come organizzarlo al meglio.
Link copiato negli appunti

Nelle precedenti lezioni abbiamo illustrato le potenzialità del framework analizzando come:

  • utilizzare tutti i Widget offerti dal framework;
  • creare interfacce utenti moderne;
  • effettuare la navigazione tra le diverse schermate.

Nozioni, queste, fondamentali per muovere i primi passi e creare fin da subito delle buone applicazioni. Ciononostante, data la natura di Dart (che ricordiamo è un linguaggio di scripting) è alquanto semplice cadere nella tentazione di creare progetti costituiti da un unico file contenente ad esempio:

  • definizione della UI;
  • definizione della logica di business;
  • definizione dello strato di accesso ai dati.

Rispetto ad altri framework come Xamarin e Android dove l’organizzazione del progetto è chiara e ben definita, Flutter lascia allo sviluppatore ampia libertà di scelta, che spesso può creare progetti difficilmente mantenibili nel tempo.

In questa lezione approfondiremo proprio questo aspetto fornendo alcune linee guida generali da seguire per organizzare al meglio il proprio progetto.

Definizione della struttura

In questa tabella riassuntiva indichiamo i principali file e cartelle che andrebbero definiti al fine di ottenere una chiara e più semplice organizzazione del progetto.

Nome Tipo Descrizione
main.dart file dart è il file alla base di tutte le applicazioni basate su Flutter. Questo file deve contenere solo una definizione basilare dell’app importando temi, rotte, e molto altro ancora da altri file.
La definizione della classe da istanziare nel metodo runApp deve essere uno StatelessWidget e non deve essere più complicato di un MaterialApp, CupertinoApp o WidgetsApp
routes.dart file dart Per semplificare ulteriormente il main.dart file è utile definire questo file che gestisce solo una Map contenente tutte le rotte come visto nelle lezioni 27 e 28
theme cartella questa cartella deve contenere un file che chiameremo style.dart che:

  • definisce uno o più temi per la nostra applicazione;
  • viene importato e utilizzato dal file main.dart
services cartella questa cartella deve contenere una o più classi relative alla logica di business della nostra applicazione e interazioni con servizi di REST API
dao cartella in questa cartella vanno definite le classi che si occuperanno del Data Access Object, ossia l’accesso a dati salvati su un DB locale
components cartella definisce un insieme di custom widget da utilizzare nelle varie schermate della nostra applicazione come bottoni e dialog
models cartella deve contenere uno o più file .dart rappresentanti il modello a oggetti della nostra applicazione
bloc cartella contiene il file bloc.dart che definisce:

  • bloc.dart;
  • bloc-prov.dart;
  • bloc-prov-tree.dart;

fondamentali per la definizione del bloc pattern

blocs cartella differentemente dalla cartella bloc, questa definisce dei file bloc che estendono la classe astratta Bloc e un file blocs.dart che permette di esportare tutti i bloc definiti nella cartella
screens cartella questa cartella può contenere una o più sottocartelle, ognuna delle quali corrisponde a una diversa schermata dell’app. Ogni cartella deve contenere tre elementi:

  • un file .dart che definisce la schermata;
  • un bloc file utilizzato solo per quella schermata;
  • una cartella components che contiene i widget (ossia i componenti) utilizzati solo da quella schermata.

Qualsiasi sezione di uno schermo più complessa di alcuni widget dovrebbe definire i propri componenti e nel caso di componenti molto complessi è necessario ricorrere alla definizione di opportuni file bloc.

Questa semplice ma efficace suddivisione del progetto in file e sottocartelle permette di organizzare meglio i concetti che abbiamo già incontrato nelle lezioni precedenti di questa guida. Altri, come la definizione del Bloc pattern, dei servizi e del DAO saranno oggetto di lezioni future.

Esempio pratico

Vediamo un semplice esempio in cui definire la struttura che ci siamo posti. Come anticipato, concetti relativi al DAO, alla definizione dei servizi e all’implementazione del Bloc pattern saranno oggetto delle prossime lezioni e pertanto nell’esempio che segue non verranno trattati.

Come sempre, creiamo un nuovo progetto come descritto nella lezione 6. In questa applicazione vogliamo:

  • definire una classe Article che definisce un titolo e un autore;
  • un dialog utilizzato in ogni schermata;
  • creare due semplici schermate (Screen1 e Screen2) il cui body sarà definito in appositi file dart nelle cartelle components. Screen2, inoltre, mostrerà oggetto Article;
  • definrie un tema;
  • definire le rotte.

La struttura che si paleserà davanti è quella classica che siamo abituati a vedere.

Iniziamo quindi col creare la cartella models che conterrà il file article.dart avente la seguente definizione della classe Article.

class Article {
  final String title;
  final String author;
  Article({@required this.title, @required this.author});
}

Definiamo adesso l’alert dialog con bottone che verrà mostrato in ogni schermata dell’applicazione. Per farlo, creiamo la cartella components e al suo interno definiamo il file dart my_alert_dialog.dart. Questo file conterrà la definizione di un widget che abbiamo già definito nella lezione 22, ossia il widget CustomDialogWithButton, il cui codice è reperibile qui. Riportiamo lo stateless widget all’interno del nostro file .dart e modifichiamo la classe aggiungendo il costruttore come segue.

class CustomDialogWithButton extends StatelessWidget {
  final Widget title;
  final Widget content;
  final double elevation;
  CustomDialogWithButton(this.title, this.content, this.elevation);
  // . . .
}

Fatto questo, sostituiamo all’interno del metodo customAlertDialog le proprietà title, content ed elevation con i valori che verranno dati al costruttore.

A questo punto definiamo le nostre schermate. All’interno della cartella lib, creiamo la seguente struttura di cartelle e file.

lib/
│── screens/
│   │── screen1
│   │      │── components
│   │      │      │── body.dart
│   │      │── screen1.dart
│   │── screen2
│   │      │── components
│   │      │      │── body.dart
│   │      │── screen1.dart

In questo modo, all’interno della cartella screens avremo un insieme di cartelle che rappresenteranno le nostre schermate, ognuna delle quali definirà una cartella components e un file dart principale che permetterà di descrivere la schermata.

Si riporta a scopo illustrativo la definizione della schermata Screen1.

Apriamo il file body.dart. Questo file conterrà il corpo della nostra schermata composto da un RaisedButton per la navigazione verso la schermata Screen2 e il CustomDialogWithButton che prende in ingresso i tre parametri di interesse.

Di seguito riportiamo per semplicità le definizione della widget Body.

class Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
        child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
          RaisedButton(
            onPressed: () {
              Navigator.pushNamed(context, "/Screen2");
            },
            child: Text('Go To Screen2!'),
          ),
          CustomDialogWithButton(
              Text("Screen ! AlertDialog"), Text("this is Screen1"), 10),
        ]));
  }
}

A questo punto apriamo il file screen1.dart e definiamo il widget Screen1 come segue.

class Screen1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Screen1'),
      ),
      body: Body(),
    );
  }
}

Definiamo adesso la navigazione tra le schermate tramite il file routes.dart che si troverà allo stesso livello del file main.dart.

final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
  "/": (BuildContext context) => Screen1(),
  "/Screen2": (BuildContext context) => Screen2(),
};

Creiamo adesso la cartella theme al cui interno definiamo il file style.dart contenente la seguente definizione.

Modifichiamo infine il file main.dart come segue.

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lesson 32',
      theme: appTheme(),
      initialRoute: '/',
      routes: routes,
    );
  }
}

In questo modo il file main.dart conterrà la definizione del tema e delle rotte impostate per l’applicazione.

Effettuati tutti i cambi sopra descritti avremo la seguente organizzazione.

Figura 174. Organizzazione del progetto (click per ingrandire)Organizzazione del progetto

Eseguiamo la nostra applicazione per ottenere il seguente risultato.

Figura 175a. Esecuzione dell’app su Android (click per ingrandire)Esecuzione dell’app su Android
Figura 175b. Esecuzione dell’app su iOS (click per ingrandire)Esecuzione dell’app su iOS

Il codice di questa lezione è disponibile su GitHub.

Ti consigliamo anche