{"id":6027,"date":"2022-04-21T12:01:27","date_gmt":"2022-04-21T12:01:27","guid":{"rendered":"https:\/\/cloudsurfers.it\/?p=6027"},"modified":"2022-04-21T12:01:29","modified_gmt":"2022-04-21T12:01:29","slug":"sviluppare-applicazione-flutter-parte-2","status":"publish","type":"post","link":"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-2\/","title":{"rendered":"Sviluppare un\u2019applicazione Flutter \u2013 Parte 2"},"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-1.png\" alt=\"\" class=\"wp-image-6028\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover-1.png 800w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover-1-300x150.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/cover-1-768x384.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure><\/div>\n\n\n\n<p>Questo \u00e8 il secondo di una serie di articoli dove realizzeremo passo passo un&#8217;applicazione mobile multipiattaforma, Android ed iOS.<\/p>\n\n\n\n<p>Nel precedente articolo abbiamo installato e configurato tutti gli strumenti necessari di sviluppo, imbastito il progetto iniziale e creato la prima pagina.<\/p>\n\n\n\n<p>In questo andremo ad aggiungere il supporto multilingua e realizzeremo un <em>provider <\/em>(cos\u00ec chiamati in gergo) per interagire con uno strato API web.<\/p>\n\n\n\n<p>Vi consiglio la lettura della <a href=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" data-type=\"URL\" data-id=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" target=\"_blank\" rel=\"noreferrer noopener\">prima parte<\/a> nel caso non l&#8217;aveste gi\u00e0 fatto.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Supporto Multilingua<\/h3>\n\n\n\n<p>Vediamo ora come rendere la nostra applicazione multilingua. Per fare ci\u00f2 ci viene in soccorso un&#8217;estensione di <em>Visual Studio Code<\/em>, <strong><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=localizely.flutter-intl\" data-type=\"URL\" data-id=\"https:\/\/marketplace.visualstudio.com\/items?itemName=localizely.flutter-intl\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter Intl<\/a><\/strong>, installiamola.<\/p>\n\n\n\n<p>Apriamo <em>VSCode<\/em> e rechiamoci alla sezione delle estensioni aggiuntive, dal pannello laterale sinistro. Cerchiamo, scrivendo nella barra di ricerca, l&#8217;estensione <em><a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=localizely.flutter-intl\" data-type=\"URL\" data-id=\"https:\/\/marketplace.visualstudio.com\/items?itemName=localizely.flutter-intl\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter Intl<\/a><\/em> ed installiamola.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"302\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-12-1024x302.png\" alt=\"\" class=\"wp-image-6031\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-12-1024x302.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-12-300x88.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-12-768x226.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-12.png 1039w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p>Terminata l&#8217;installazione ed aperto il nostro progetto su <em>VSCode<\/em>, dalla <em>Command Palette&#8230;<\/em> (Ctrl+Shift+P) cerchiamo e lanciamo il comando <code>Flutter Intl: Initialize<\/code>.<\/p>\n\n\n\n<p>Questo comando automatizza diverse operazioni, vediamole elencate di seguito.<\/p>\n\n\n\n<p>Abilita <code>flutter_intl<\/code> all&#8217;interno del file di configurazione di progetto <code>pubspec.yaml<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\nflutter_intl:\n  enabled: true\n<\/pre><\/div>\n\n\n<p>Crea una cartella chiamata <code>l10n<\/code> all&#8217;interno della directory <code>lib<\/code> (che come abbiamo visto nel <a href=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" data-type=\"URL\" data-id=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" target=\"_blank\" rel=\"noreferrer noopener\">precedente articolo<\/a>, contiene la logica dell&#8217;applicazione).<\/p>\n\n\n\n<p>All&#8217;interno della cartella <code>l10n<\/code>, genera il file <code>intl_en.arb<\/code> (inizialmente vuoto):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n}\n<\/pre><\/div>\n\n\n<p>Per ogni lingua supportata dall&#8217;applicazione \u00e8 necessario creare un rispettivo file <em>.arb<\/em> con il codice <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_ISO_639-1_codes\" target=\"_blank\" rel=\"noreferrer noopener\">ISO 639-1<\/a> che identifica la lingua appunto.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><em>Per aggiungere una nuova lingua in maniera del tutto automatizzata si pu\u00f2 utilizzare il comando <code>Flutter Intl: Add locale<\/code> messo a disposizione dal componente aggiuntivo che abbiamo appena installato.<\/em><\/p><\/blockquote>\n\n\n\n<p>Questi file <em>.arb<\/em> possono essere considerati ad esempio come file <em>.json<\/em> non nidificati (non sono supportati oggetti nidificati all&#8217;interno dei file di lingua <em>l10n<\/em> su Flutter).<\/p>\n\n\n\n<p>Compiliamo il file <code>intl_en.arb<\/code> come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n    &quot;loginTitle&quot;: &quot;Login&quot;,\n    &quot;loginButton&quot;: &quot;Log In&quot;\n}\n<\/pre><\/div>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Per comodit\u00e0, non potendo utilizzare oggetti nidificati, il prefisso di ogni chiave indica la pagina in cui \u00e8 utilizzata quella chiave, in questo caso <em>login<\/em>.<\/p><\/blockquote>\n\n\n\n<p>Ogni volta che salviamo il contenuto di un file lingua, in automatico l&#8217;estensione <code>Flutter Intl<\/code> rigenerer\u00e0 il componente <em>Dart<\/em> <code>l10n.dart<\/code> all&#8217;interno della cartella <code>generated<\/code> (sempre all&#8217;interno della directory <code>lib<\/code>). <\/p>\n\n\n\n<p>Questo componente autogenerato ci servir\u00e0 per utilizzare le traduzioni all&#8217;interno dell&#8217;applicazione, come vedremo successivamente.<\/p>\n\n\n\n<p>Prima di poter utilizzare le due nuove chiavi che abbiamo creato, abbiamo bisogno di un paio di passaggi manuali per completare la configurazione multilingua.<\/p>\n\n\n\n<p>Apriamo il file <code>pubspec.yaml<\/code> e alla sezione <code>dependencies<\/code> aggiungiamo la nuova dipendenza <code>flutter_localizations<\/code> come sotto:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\ndependencies:\n    \/\/ Altre dipendenze...\n    flutter_localizations:\n        sdk: flutter\n<\/pre><\/div>\n\n\n<p>Al salvataggio verr\u00e0 eseguito in automatico da <em>VSCode<\/em> il comando <code>flutter pub get<\/code> per l&#8217;installazione dei nuovi pacchetti aggiunti.<\/p>\n\n\n\n<p>Apriamo ora il file <code>main.dart<\/code> ed importiamo il nuovo componente autogenerato <code>l10n.dart<\/code> e la dipendenza appena installata <code>flutter_localizations<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;package:flutter_localizations\/flutter_localizations.dart&#039;;\nimport &#039;generated\/l10n.dart&#039;;\n<\/pre><\/div>\n\n\n<p>Impostiamo poi, all&#8217;interno della nostra istanza <em><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\">MaterialApp<\/a><\/em>, l&#8217;array <em>localizationsDelegates<\/em> e la propriet\u00e0 <em>supportedLocales<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nreturn MaterialApp(\n      localizationsDelegates: const &#x5B;\n        S.delegate,\n        GlobalMaterialLocalizations.delegate,\n        GlobalWidgetsLocalizations.delegate,\n        GlobalCupertinoLocalizations.delegate,\n      ],\n      supportedLocales: S.delegate.supportedLocales,\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<\/pre><\/div>\n\n\n<p>Ora siamo pronti ad utilizzare le chiavi di traduzione.<\/p>\n\n\n\n<p>Apriamo il file <code>login.dart<\/code> ed all&#8217;interno dell&#8217;oggetto <a href=\"https:\/\/api.flutter.dev\/flutter\/material\/AppBar-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/AppBar-class.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>AppBar<\/em> <\/a>cambiamo il titolo stringa &#8220;<em>Login<\/em>&#8221; con la nuova chiave di traduzione:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nreturn Scaffold(\n  appBar: AppBar(\n    title: Text(S.current.loginTitle),\n  ),\n  ...\n<\/pre><\/div>\n\n\n<p>importiamo il componente autogenerato <code>l10n.dart<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;..\/generated\/l10n.dart&#039;;\n<\/pre><\/div>\n\n\n<p>Facciamo la stessa cosa con l&#8217;etichetta del pulsante <em><a href=\"https:\/\/api.flutter.dev\/flutter\/material\/AppBar-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/AppBar-class.html\" target=\"_blank\" rel=\"noreferrer noopener\">ElevatedButton<\/a><\/em> di accesso:<\/p>\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: SizedBox(\n    width: 200,\n    height: 50,\n    child: Center(\n      child: Text(\n        S.current.loginButton,\n        textScaleFactor: 1.5,\n      ),\n    ),\n  ),\n)\n<\/pre><\/div>\n\n\n<p>Selezionamo il nostro dispositivo Android (o l&#8217;emulatore) per il deploy e successivamente avviamo l&#8217;applicazione, menu <code>Run<\/code>, <code>Start Debugging<\/code> o pi\u00f9 semplicemente il tasto <em>F5<\/em> sulla tastiera.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-13.png\" alt=\"\" class=\"wp-image-6032\" width=\"284\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-13.png 400w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-13-134x300.png 134w\" sizes=\"(max-width: 400px) 100vw, 400px\" loading=\"lazy\" \/><figcaption>Il pulsante ed il titolo della pagina ora utilizzano le chiavi di traduzione<\/figcaption><\/figure><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Integrazione API Web<\/h3>\n\n\n\n<p>Iniziamo ora a vedere un metodo per poter integrare l&#8217;applicazione con uno strato API web di esempio.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p><em>Nota: per implementare uno strato API web di test in modo rapido \u00e8 possibile utilizzare lo strumento online <a href=\"https:\/\/beeceptor.com\/\">https:\/\/beeceptor.com\/<\/a>.<\/em><\/p><\/blockquote>\n\n\n\n<p>Consideriamo quindi di dover comunicare con un API che espone i seguenti metodi:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"post-apiauth\">POST &#8230;\/api\/auth<\/h4>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"input-oggetto-json\">Input (Oggetto JSON)<\/h5>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n    &quot;email&quot;: &quot;demo@cloudsurfers.it&quot;,\n    &quot;password&quot;: &quot;passworddemo&quot;\n}\n<\/pre><\/div>\n\n\n<h5 class=\"wp-block-heading\" id=\"output-oggetto-json\">Output (Oggetto JSON)<\/h5>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n    &quot;token&quot;: &quot;TOKEN_DEMO&quot;\n}\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\" id=\"get-apiitems\">GET &#8230;\/api\/items<\/h4>\n\n\n\n<h5 class=\"wp-block-heading\" id=\"header\">Header<\/h5>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n{\n    ...\n    &quot;token&quot;: &quot;TOKEN_DEMO&quot;,\n    ...\n}\n<\/pre><\/div>\n\n\n<h5 class=\"wp-block-heading\" id=\"output-oggetto-json-2\">Output (Oggetto JSON)<\/h5>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n&#x5B;\n    {\n        &quot;id&quot;: 0,\n        &quot;name&quot;: &quot;Item 1&quot;,\n        &quot;description&quot;: &quot;Blue item&quot;\n    },\n    {\n        &quot;id&quot;: 1,\n        &quot;name&quot;: &quot;Item 2&quot;,\n        &quot;description&quot;: &quot;Green item&quot;\n    }\n]\n<\/pre><\/div>\n\n\n<p>La chiamata <em>items<\/em> \u00e8 una chiamata autenticata, necessita quindi del token di accesso all&#8217;interno dell&#8217;header della richiesta.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-modelli\">I modelli<\/h3>\n\n\n\n<p>Per interagire con le API abbiamo bisogno di implementare tre nuovi oggetti all&#8217;interno dell&#8217;applicazione: <strong>AuthRequest<\/strong>, <strong>AuthResponse<\/strong> ed <strong>ItemResponse<\/strong>. Questi tre oggetti dovranno: essere generati partendo da un file <em>.json<\/em>, oppure generare a loro volta un file <em>.json<\/em> (per il modello <em>AuthRequest<\/em> per esempio).<\/p>\n\n\n\n<p>Creiamo quindi una nuova cartella <code>models<\/code> all&#8217;interno della cartella <code>libs<\/code>.<\/p>\n\n\n\n<p>All&#8217;interno di <code>models<\/code> creiamo tre file vuoti: <code>auth_request.dart<\/code>, <code>auth_response.dart<\/code> ed <code>item_response.dart<\/code>.<\/p>\n\n\n\n<p>Apriamo il file <code>auth_request.dart<\/code> e compiliamolo come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass AuthRequest {\n  String? email;\n  String? password;\n\n  AuthRequest({\n    this.email,\n    this.password,\n  });\n\n  Map&lt;String, dynamic&gt; toJson() {\n    final Map&lt;String, dynamic&gt; data = &lt;String, dynamic&gt;{};\n    data&#x5B;&quot;email&quot;] = email;\n    data&#x5B;&quot;password&quot;] = email;\n    return data;\n  }\n}\n<\/pre><\/div>\n\n\n<p>come vediamo il metodo <strong>toJson()<\/strong> trasforma l&#8217;oggetto <em>AuthRequest<\/em> in oggetto <em>JSON<\/em>.<\/p>\n\n\n\n<p>Compiliamo ora il file <code>auth_response.dart<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass AuthResponse {\n  String? token;\n\n  AuthResponse({\n    this.token,\n  });\n\n  AuthResponse.fromJson(Map&lt;String, dynamic&gt; json) {\n    token = json&#x5B;&quot;token&quot;];\n  }\n}\n<\/pre><\/div>\n\n\n<p>a differenza del metodo prima, qui, il metodo <strong>fromJson(json)<\/strong> trasforma un oggetto <em>JSON<\/em> assegnando le propriet\u00e0 dell&#8217;oggetto <em>AuthResponse<\/em>.<\/p>\n\n\n\n<p>Terminiamo infine con il file <code>item_response.dart<\/code> molto simile al precedente:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass ItemResponse {\n  int? id;\n  String? name;\n  String? description;\n\n  ItemResponse({\n    this.id,\n    this.name,\n    this.description,\n  });\n\n  ItemResponse.fromJson(Map&lt;String, dynamic&gt; json) {\n    id = json&#x5B;&quot;id&quot;];\n    name = json&#x5B;&quot;name&quot;];\n    description = json&#x5B;&quot;description&quot;];\n  }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-provider\">I <em>provider<\/em><\/h3>\n\n\n\n<p>Per una gestione pulita ed organizzata delle logiche dell&#8217;applicazione, che possono essere ad esempio: la gestione dell&#8217;autenticazione, la gestione di un database locale, ecc&#8230;, in Flutter, si usano i <em>provider<\/em> (<em>Services<\/em> per qui proviene dal mondo <em>Angular<\/em>).<\/p>\n\n\n\n<p>I <em>provider<\/em> non sono altro che classi di oggetti che si occupano di eseguire determinate operazioni che possono essere invocate all&#8217;interno di tutta l&#8217;applicazione, ad esempio all&#8217;interno di tutte le pagine.<\/p>\n\n\n\n<p>All&#8217;interno del nostro progetto, al momento, abbiamo bisogno di due <em>provider<\/em>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>auth_provider<\/strong>, si occupa della memorizzazione del <em>token<\/em> di autenticazione dopo che viene effettuato l&#8217;accesso tramite API web;<\/li><li><strong>api_provider<\/strong>, conterr\u00e0 i metodi per eseguire chiamate verso lo strato API web esterno.<\/li><\/ul>\n\n\n\n<p>Iniziamo quindi con lo sviluppo. <\/p>\n\n\n\n<p>Creaiamo all&#8217;interno della cartella <code>lib<\/code> una cartella <code>providers<\/code>, ed, al suo interno, un file <code>auth_provider.dart<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nclass AuthProvider {\n  String? _token;\n\n  setToken(String? token) {\n    _token = token;\n  }\n\n  String? get token {\n    return _token;\n  }\n}\n<\/pre><\/div>\n\n\n<p>Prima di proseguire con la creazione del <em>provider<\/em> per l&#8217;interazione con le API, abbiamo bisogno di installare nel progetto la dipendenza <strong><a href=\"https:\/\/pub.dev\/packages\/dio\" data-type=\"URL\" data-id=\"https:\/\/pub.dev\/packages\/dio\" target=\"_blank\" rel=\"noreferrer noopener\">dio<\/a><\/strong>, un client HTTP per progetti <em>Dart<\/em>.<\/p>\n\n\n\n<p>All&#8217;interno del file <code>pubspec.yaml<\/code> aggiungiamo quindi la dipendenza <code>dio<\/code> e salviamo successivamente il file per eseguire in automatico l&#8217;installazione del pacchetto:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\ndependencies:\n  flutter:\n    sdk: flutter\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\n  dio: ^4.0.6\n\n  flutter_localizations:\n        sdk: flutter\n<\/pre><\/div>\n\n\n<p>Creiamo ora, nella cartella <code>providers<\/code>, il file <code>api_provider.dart<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;package:demo_application\/models\/auth_request.dart&#039;;\nimport &#039;package:demo_application\/models\/auth_response.dart&#039;;\nimport &#039;package:demo_application\/models\/item_response.dart&#039;;\nimport &#039;package:dio\/dio.dart&#039;;\n\nclass ApiProvider {\n  final Dio _dio = Dio();\n  final String _url = &#039;http:\/\/api.demo\/&#039;;\n\n  ApiProvider();\n\n  Future&lt;AuthResponse?&gt; auth(AuthRequest request) async {\n    try {\n      Response response = await _dio.post(&#039;$_url\/api\/auth&#039;, data: request);\n      return AuthResponse.fromJson(response.data);\n    } catch (error) {\n      print(error);\n      return null;\n    }\n  }\n\n  Future&lt;List&lt;ItemResponse&gt;?&gt; items() async {\n    try {\n      Response response = await _dio.get(&#039;$_url\/api\/items&#039;);\n      return List&lt;ItemResponse&gt;.from(\n          response.data?.map((data) =&gt; ItemResponse.fromJson(data)));\n    } catch (error) {\n      print(error);\n      return null;\n    }\n  }\n}\n<\/pre><\/div>\n\n\n<p>Vediamo le operazioni pi\u00f9 nel dettaglio:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nfinal Dio _dio = Dio();\nfinal String _url = &#039;http:\/\/api.demo&#039;;\n<\/pre><\/div>\n\n\n<p>All&#8217;interno della classe viene dichiarato ed istanziato il client HTTP <a href=\"https:\/\/pub.dev\/packages\/dio\" data-type=\"URL\" data-id=\"https:\/\/pub.dev\/packages\/dio\" target=\"_blank\" rel=\"noreferrer noopener\"><em>dio<\/em> <\/a>utilizzando il pacchetto installato in precedenza.<br>Viene anche dichiarata ed assegnata la variabile stringa che contiene l&#8217;<em>endpoint<\/em> dello strato API web.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nFuture&lt;AuthResponse?&gt; auth(AuthRequest request) async {\n    try {\n      Response response = await _dio.post(&#039;$_url\/api\/auth&#039;, data: request);\n      return AuthResponse.fromJson(response.data);\n    } catch (error) {\n      print(error);\n      return null;\n    }\n}\n...\n<\/pre><\/div>\n\n\n<p>Il metodo <strong>auth<\/strong>, cos\u00ec come il metodo <strong>items<\/strong>, \u00e8 un metodo asincrono e ritorna l&#8217;oggetto <em>AuthResponse<\/em> contenente il <em>token<\/em> di autenticazione se la chiamata HTTP ottiene uno status 200. Oppure ritorna un oggetto <em>NULL<\/em> nel caso di errore.<\/p>\n\n\n\n<p>Per poter utilizzare i due nuovi <em>provider<\/em> che abbiamo creato, non ci resta che instanziarli e richiamare i metodi implementati. <\/p>\n\n\n\n<p>E invece no&#8230;<\/p>\n\n\n\n<p>Cos\u00ec facendo, se dovessimo utilizzare i <em>provider<\/em> su pi\u00f9 pagine della nostra applicazione, dovremo istanzarli pi\u00f9 volte, causando inefficienze e possibili errori di concorrenza all&#8217;interno dell&#8217;applicazione.<\/p>\n\n\n\n<p>Vediamo quindi come fare&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"get_it\">Utilizziamo get_it<\/h3>\n\n\n\n<p><a href=\"https:\/\/pub.dev\/packages\/get_it\" data-type=\"URL\" data-id=\"https:\/\/pub.dev\/packages\/get_it\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>get_it<\/strong> <\/a>\u00e8 un pacchetto <em>Dart<\/em> che permette di istanziare un&#8217;oggetto una sola volta nel processo di avvio dell&#8217;applicazione e richiamare successivamente la stessa istanza ovunque vogliamo.<\/p>\n\n\n\n<p>Come al solito come procedura d&#8217;installazione di nuovi pacchetti, aggiungiamo la dipendenza <code>get_it<\/code> all&#8217;interno del file di progetto <code>pubspec.yaml<\/code> e salviamo:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: yaml; title: ; notranslate\" title=\"\">\ndio: ^4.0.6\nget_it: ^7.2.0\n<\/pre><\/div>\n\n\n<p>Apriamo il file <code>main.dart<\/code> ed importiamo il nuovo pacchetto:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nimport &#039;package:get_it\/get_it.dart&#039;;\n<\/pre><\/div>\n\n\n<p>All&#8217;interno del metodo <strong>main()<\/strong> registriamo i due <em>provider<\/em> creati:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\nvoid main() {\n  GetIt.I.registerSingleton&lt;AuthProvider&gt;(AuthProvider());\n  GetIt.I.registerSingleton&lt;ApiProvider&gt;(ApiProvider());\n\n  runApp(const MyApp());\n}\n<\/pre><\/div>\n\n\n<p>I due <em>provider<\/em> ora verranno istanziati all&#8217;avvio e saranno disponibili in tutta l&#8217;applicazione richiamandoli con una chiamata simile a questa: <code>GetIt.I&lt;ApiProvider&gt;()...<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"pagina-di-login\">Pagina di login<\/h3>\n\n\n\n<p>Ora che i <em>provider<\/em> sono implementati, torniamo alla pagina di login <code>login.dart<\/code> e pi\u00f9 precisamente all&#8217;interno dell&#8217;evento <code>onPressed<\/code> dell&#8217;<em><a href=\"https:\/\/api.flutter.dev\/flutter\/material\/ElevatedButton-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/ElevatedButton-class.html\" target=\"_blank\" rel=\"noreferrer noopener\">ElevatedButton<\/a><\/em> che abbiamo realizzato nel precedente articolo.<\/p>\n\n\n\n<p>Implementiamo la chiamata API di autenticazione:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nonPressed: () {\n    if (!_formKey.currentState!.validate()) {\n      return;\n    }\n\n    _formKey.currentState!.save();\n\n    print(_email);\n    print(_password);\n\n    AuthRequest request = AuthRequest(\n      email: _email,\n      password: _password,\n    );\n\n    GetIt.I&lt;ApiProvider&gt;().auth(request).then((response) {\n      print(response?.token);\n    }).onError((error, stackTrace) {\n      print(error);\n    });\n},\n...\n<\/pre><\/div>\n\n\n<p>Eseguiamo l&#8217;applicazione con <em>F5<\/em>&#8230;<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"927\" height=\"601\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-14.png\" alt=\"\" class=\"wp-image-6034\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-14.png 927w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-14-300x194.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-14-768x498.png 768w\" sizes=\"auto, (max-width: 927px) 100vw, 927px\" \/><\/figure><\/div>\n\n\n\n<p>come potete vedere, <strong><em>TOKEN_DEMO<\/em> <\/strong>viene stampato correttamente come voluto all&#8217;interno della <em>DEBUG CONSOLE<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"chiamate-autenticate\">Chiamate autenticate<\/h3>\n\n\n\n<p>A differenza della chiamata <strong>auth<\/strong> di autenticazione, che non richiede appunto autenticazione per essere eseguita per ovvi motivi, la chiamata <strong>items<\/strong> necessita all&#8217;interno dell&#8217;<em>header<\/em> della richiesta, il <em>token<\/em> di accesso.<\/p>\n\n\n\n<p>Per fare questo possiamo utilizzare lo strumento <em>Interceptor<\/em> del client HTTP <em><a href=\"https:\/\/pub.dev\/packages\/dio\" data-type=\"URL\" data-id=\"https:\/\/pub.dev\/packages\/dio\" target=\"_blank\" rel=\"noreferrer noopener\">dio<\/a><\/em>.<\/p>\n\n\n\n<p>Prima per\u00f2, una volta ottenuto il <em>token<\/em> di autenticazione, dobbiamo salvarlo all&#8217;interno dell&#8217;<em>AuthProvider<\/em>. <\/p>\n\n\n\n<p>Sempre all&#8217;interno della pagina di login <code>login.dart<\/code>, evento <code>onPressed<\/code> dell&#8217;<em><a href=\"https:\/\/api.flutter.dev\/flutter\/material\/ElevatedButton-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/material\/ElevatedButton-class.html\" target=\"_blank\" rel=\"noreferrer noopener\">ElevatedButton<\/a><\/em>, aggiungiamo il salvataggio del <em>token<\/em>, se ricevuto, all&#8217;interno dell&#8217;<em>AuthProvider<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nGetIt.I&lt;ApiProvider&gt;().auth(request).then((response) {\n  print(response?.token);\n\n  if (response != null &amp;&amp; response.token != null) {\n    GetIt.I&lt;AuthProvider&gt;().setToken(response.token);\n  }\n}).onError((error, stackTrace) {\n  print(error);\n});\n...\n<\/pre><\/div>\n\n\n<p>Apriamo il file <code>api_provider.dart<\/code> ed all&#8217;interno della classe <em>ApiProvider<\/em> aggiungiamo tre nuovi metodi: <em>requestInterceptor<\/em>, <em>responseInterceptor<\/em> ed <em>errorInterceptor<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\ndynamic requestInterceptor(RequestOptions options, RequestInterceptorHandler handler) async {\n    String? token = GetIt.I&lt;AuthProvider&gt;().token;\n    if (token != null) {\n      options.headers.addAll({\n        &#039;token&#039;: token,\n      });\n    }\n    handler.next(options);\n}\n\ndynamic responseInterceptor(Response response, ResponseInterceptorHandler handler) async {\n    handler.next(response);\n}\n\ndynamic errorInterceptor(DioError error, ErrorInterceptorHandler handler) async {\n    handler.next(error);\n}\n...\n<\/pre><\/div>\n\n\n<p>All&#8217;interno del metodo <strong>requestInterceptor<\/strong> viene iniettato il <em>token<\/em>, se presente, nell&#8217;header della richiesta HTTP intercettata.<\/p>\n\n\n\n<p>I tre metodi appena creati, successivamente, vanno aggiunti all&#8217;istanza del client HTTP <em><a href=\"https:\/\/pub.dev\/packages\/dio\" data-type=\"URL\" data-id=\"https:\/\/pub.dev\/packages\/dio\" target=\"_blank\" rel=\"noreferrer noopener\">dio<\/a><\/em>. <\/p>\n\n\n\n<p>Creiamo quindi un nuovo metodo <em>addnterceptors<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\naddInterceptors() {\n    _dio.interceptors.add(InterceptorsWrapper(\n        onRequest: (options, handler) =&gt; requestInterceptor(options, handler),\n        onResponse: (response, handler) =&gt;\n            responseInterceptor(response, handler),\n        onError: (dioError, handler) =&gt; errorInterceptor(dioError, handler)));\n}\n...\n<\/pre><\/div>\n\n\n<p>e richiamiamolo all&#8217;interno del costruttore <em>ApiProvider<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nApiProvider() {\n    addInterceptors();\n}\n...\n<\/pre><\/div>\n\n\n<p>Ora, una volta che abbiamo ottenuto il token, questo verr\u00e0 inserito in tutte le chiamate HTTP effettuate tramite il client.<\/p>\n\n\n\n<p>Per verificare se l&#8217;<em>interceptor<\/em> funziona correttamente, aggiungiamo un pulsante nuovo sotto al pulsante <em>Login<\/em> (all&#8217;interno della pagina di Login) che chiamiamo <strong>Elenco Oggetti<\/strong>. Facciamo si, inoltre, che il nuovo pulsante sia visibile solo se siamo autenticati, quindi il token \u00e8 salvato in memoria.<\/p>\n\n\n\n<p>Per fare questo, all&#8217;interno del file <code>login.dart<\/code>, aggiungiamo un nuovo metodo di tipo <em>Getter<\/em>, chiamato <strong>authenticated<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nget authenticated {\n    return GetIt.I&lt;AuthProvider&gt;().token != null;\n}\n...\n<\/pre><\/div>\n\n\n<p>Aggiungiamo anche un metodo <strong>itemsButton<\/strong> che ritorni un <em>Widget<\/em> contente il nuovo pulsante:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nWidget itemsButton() {\n    return Column(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: &#x5B;\n        const SizedBox(height: 8),\n        ElevatedButton(\n          onPressed: () {\n            GetIt.I&lt;ApiProvider&gt;().items().then((response) {\n              print(response);\n            }).onError((error, stackTrace) {\n              print(error);\n            });\n          },\n          child: SizedBox(\n            width: 200,\n            height: 50,\n            child: Center(\n              child: Text(\n                S.current.loginItemsButton,\n                textScaleFactor: 1.5,\n              ),\n            ),\n          ),\n        )\n      ],\n    );\n}\n...\n<\/pre><\/div>\n\n\n<p>L&#8217;evento <code>onPressed: () {}<\/code> del pulsante contiene la chiamata API autenticata <em>items<\/em>.<\/p>\n\n\n\n<p>Infine non ci resta che aggiungere, sotto al pulsante <em>Login<\/em>, il nuovo metodo <strong>itemsButton()<\/strong>, condizionando la visibilit\u00e0 in base alla variabile (metodo di tipo <em>Getter<\/em>) <strong>authenticated<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nVisibility(\n  visible: authenticated,\n  child: itemsButton(),\n)\n...\n<\/pre><\/div>\n\n\n<p>Il <em>Widget <\/em><strong><a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Visibility-class.html\" data-type=\"URL\" data-id=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Visibility-class.html\" target=\"_blank\" rel=\"noreferrer noopener\">Visibility <\/a><\/strong>ci permette di mostrare o meno un <em>Widget <\/em>figlio.<\/p>\n\n\n\n<p>Provando ad eseguire l&#8217;applicazione ora, noteremo che nonostante si effettui l&#8217;accesso, e questo vada a buon fine, non venga mostrato il nuovo pulsante che abbiamo creato.<\/p>\n\n\n\n<p>Questo succede poich\u00e9 <strong>authenticated<\/strong> viene eseguito solo la prima volta, durante la creazione della pagina. <\/p>\n\n\n\n<p>Per farlo rieseguire ottenendo il valore aggiornato della variabile <em>token<\/em>, dobbiamo utilizzare il metodo <code>setState((){})<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: java; title: ; notranslate\" title=\"\">\n...\nsetState(() {\n  GetIt.I&lt;AuthProvider&gt;().setToken(response.token);\n});\n...\n<\/pre><\/div>\n\n\n<p>In questo modo verranno eseguite le operazioni presenti all&#8217;interno del metodo <code>setState((){})<\/code> e verranno aggiornati gli stati delle variabili che hanno subito una modifica, nel nostro caso la variabile <em>token<\/em> di <em>AuthProvider<\/em> richiamata per l&#8217;appunto all&#8217;interno del metodo <em>authenticated<\/em>.<\/p>\n\n\n\n<p>Eseguiamo l&#8217;applicazione con <em>F5<\/em> ed effettuiamo l&#8217;accesso:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-15.png\" alt=\"\" class=\"wp-image-6035\" width=\"284\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-15.png 400w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-15-134x300.png 134w\" sizes=\"(max-width: 400px) 100vw, 400px\" loading=\"lazy\" \/><\/figure><\/div>\n\n\n\n<p>Come si pu\u00f2 vedere, sempre che l&#8217;accesso vada a buon fine, comparir\u00e0 il nuovo pulsante <em>Elenco Oggetti<\/em>.<\/p>\n\n\n\n<p>Facendo uso ora dello strumento <em>Dart DevTools<\/em> visto nel <a href=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" data-type=\"URL\" data-id=\"https:\/\/cloudsurfers.it\/index.php\/sviluppare-applicazione-flutter-parte-1\/\" target=\"_blank\" rel=\"noreferrer noopener\">primo articolo<\/a> di questa serie, verifichiamo che, cliccando sul pulsante <em>Elenco Oggetti<\/em>, la chiamata API <em>items<\/em> venga effettuata con <em>TOKEN_DEMO<\/em> all&#8217;interno dell&#8217;<em>header<\/em>.<\/p>\n\n\n\n<p>Apriamo i <em>Dart DevTools<\/em> all&#8217;interno del Browser Web. <\/p>\n\n\n\n<p>Spostiamoci nella scheda <em>Network<\/em> e premiamo, sull&#8217;applicazione, il pulsante <em>Elenco Oggetti<\/em>:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"609\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-16-1024x609.png\" alt=\"\" class=\"wp-image-6036\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-16-1024x609.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-16-300x179.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-16-768x457.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/04\/image-16.png 1304w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><\/div>\n\n\n\n<p>All&#8217;interno dell&#8217;header della richiesta GET <em>items<\/em>, \u00e8 presente <em>TOKEN_DEMO<\/em> come <em>token<\/em>. La chiamata autenticata quindi verr\u00e0 eseguita correttamente.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"prossimamente\">Prossimamente&#8230;<\/h3>\n\n\n\n<p>Nel prossimo articolo vedremo come creare ed interagire con una base dati locale SQLite ed un accenno alla navigazione tra le pagine.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"github\">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_pt2\" data-type=\"URL\" data-id=\"https:\/\/github.com\/Cloudsurfers-Dev\/flutter_demo_application_pt2\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"fonti\">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><\/ul>\n","protected":false},"excerpt":{"rendered":"<p><!-- wp:paragraph --><\/p>\n<p>Questo \u00e8 il secondo di una serie di articoli dove realizzeremo passo passo un&#8217;applicazione mobile multipiattaforma, Android ed iOS.<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n<p><!-- wp:paragraph --><\/p>\n<p>Nel precedente articolo abbiamo installato e configurato tutti gli strumenti necessari di sviluppo, imbastito il progetto iniziale e creato la prima pagina.<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n<p><!-- wp:paragraph --><\/p>\n<p>In questo andremo ad aggiungere il supporto multilingua e realizzeremo un <em>provider <\/em>(cos\u00ec chiamati in gergo) per interagire con uno strato API web.<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n","protected":false},"author":3,"featured_media":6028,"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-6027","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\/6027","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=6027"}],"version-history":[{"count":0,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/6027\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media\/6028"}],"wp:attachment":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media?parent=6027"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/categories?post=6027"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/tags?post=6027"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}