Introduzione a Dart: seconda parte

Di - 27 October 2012 - in

In questo secondo appuntamento con la rubrica dedicata a Dart, ci occuperemo della realizzazione di una prima, semplice, web application.

Nell’articolo precedente abbiamo sottolineato il fatto che gli strumenti offerti da Dart rappresentino essi stessi una buona risorsa per imparare il linguaggio: per comprendere il funzionamento di una web application infatti ci serviremo dell’applicazione di default generata da DartEditor.

Apriamo Dart Editor, creiamo una nuova applicazione dal menù File -> New Application, chimiamola “HelloWorld”, selezioniamo il check box Generate content for a basic web app e diamo “OK”.

Nota: ogni settimana il team rilascia una nuova build di Dart SDK, Dart Editor e Dartium, ci si dovrà assicurare quindi di avere la versione più aggiornata; in questo articolo si fa riferimento alla versione dell’editor 0.2.0.r13841, build 13841 e alla versione di Dart SDK 13851.

Dart Editor genererà a questo punto una nuova web application con le seguente struttura:

├── HelloWorld.css
├── HelloWorld.html
├── pubspec.yaml
└── web
    └── HelloWorld.dart

Nel file pubspec.yaml vengono definiti dettagli e dipendenze dell’applicazione, informazioni richieste da pub, il package manager, che per il momento ignoriamo.

Il file HelloWorld.dart è il core dell’applicazione, contiene le variabili e le funzioni; HelloWorld.html è il file che viene caricato all’avvio dell’applicazione e contiene la struttura dell’interfaccia HTML; HelloWorld.css è il foglio di stile.

All’esecuzione dell’app viene avviato Dartium, un fork di Chromium che incorpora la virtual machine di Dart e viene caricato il file HelloWorld.html in un server locale all’indirizzo http://127.0.0.1:3030/percorso/a/HelloWorld.html.

Quest’applicazione mostra un’etichetta, “Click Me!”, che ruota al click.

Diamo uno sguardo al codice di HelloWorld.html e HelloWorld.dart per capire come funziona;

HelloWorld.html:

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    <title>HelloWorld</title>
    <link rel="stylesheet" href="HelloWorld.css">
  </head>
  <body>
    <h1>HelloWorld</h1>

    <p>Hello world from Dart!</p>

    <div id="container">
      <p id="text"></p>
    </div>

    <script type="application/dart" src="web/HelloWorld.dart"></script>
    <script src="https://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script>
  </body>
</html>

Gli elementi che ci interessano sono 2:

  • È a questo elemento che sarà applicata l’etichetta “Click Me!” ed a questo elemento è associata la funzione di rotazione

  • Il file HelloWorld.dart viene caricato nell’applicazione

HelloWorld.dart:

import 'dart:html';

num rotatePos = 0;

void main() {
    query("#text")
        ..text = "Click me!"
        ..on.click.add(rotateText);
}

void rotateText(Event event) {
    rotatePos += 360;
    query("#text").style
        ..transition = "1s"
        ..transform = "rotate(${rotatePos}deg)";
}

Come prima cosa viene importata la libreria dart:html che contiene le definizioni delle funzioni per la gestione degli elementi HTML e viene inizializzata la variabile rotatePos con valore 0.

Come in altri linguaggi di programmazione, anche in Dart l’esecuzione del codice inizia da main(); query("#text") è un’istanza di ParagraphElementImpl che rappresenta l’elemento paragrafo

, e a questo punto notiamo una interessante caratteristica della sintassi detta “a cascata” di Dart, ovvero i due punti prima dell’attributo:

..text = "Click me!"
..on.click.add(rotateText);

Entrambi gli attributi si riferiscono a query("#text"), questa struttura ci consente di risparmiare tempo nella scrittura del codice garantendone una migliore leggibilità.

L’attributo .text imposta il testo di

su “Click Me!”, mentre .on.click.add(rotateText) abbina all’evento click la funzione rotateText.

Occupiamoci ora della funzione rotateText(): essa innanzitutto incrementa il valore rotatePos di 360, dopodichè imposta all’elemento paragrafo

gli attributi transition e transform, il primo stabilisce che la durata della transizione sarà di 1 secondo mentre l’ultimo che la trasformazione sarà una rotazione di 360 di gradi.

Nota: rotatePos += 360 è la forma contratta di rotatePos = rotatePos + 360, sintassi comune a molti altri linguaggi.

Avviamo l’applicazione dal menù Run -> Run e clicchiamo su “Click Me!” per farlo ruotare.

Konami Code

Adesso, dopo esserci divertiti a far ruotare un’etichetta di testo, occupiamoci di un altro esempio un po’ più complesso per capire come funzionano le librerie in Dart. Per questo scopo abbiamo scelto di servirci di qualcosa che gli amanti dei videogames sapranno senza dubbio apprezzare: il Konami Code.

Il concetto è molto semplice: data una sequenza di input, se questa corrisponde ad una determinata sequenza allora viene invocata una funzione.

La sequenza che dobbiamo aspettare è:

38384040373937396665

ovvero:

38 38 40 40 37 39 37 39 66 65

ovvero i keyCodes dei seguenti tasti:

↑  ↑  ↓  ↓  ←  →  ←  →  B  A

Scarichiamo da GitHub l’applicazione KonamiDart, sviluppata da Nicolas François, in formato ZIP oppure cloniamo la repository con git:

git clone git://github.com/nfrancois/KonamiDart.git

La struttura dell’applicazione è:

├── Readme.md
├── example
│   ├── sample.dart
│   └── sample.html
├── lib
│   └── konami_code.dart
└── pubspec.yaml

Il file lib/konami_code.dart contiene la classe KonamiCode, mentre example/sample.dart ed example/sample.html ne contengono una implementazione.

Apriamo Dart editor, scegliamo “Open Folder” e selezioniamo la cartella del progetto “KonamiDart”.

konami_code.dart:

#library('konami_code');
#import('dart:html');

class KonamiCode {

const KONAMICODE_SEQUENCE = "38384040373937396665";

var _onPerformedCallback;
String _lastInputs;

KonamiCode(){
  _lastInputs = "";
  window.on.keyDown.add(_onKeyDown);
}

_onKeyDown(KeyboardEvent event){
  var keyCode = event.keyCode;
  _lastInputs = _lastInputs.concat(keyCode.toString());
  if(KONAMICODE_SEQUENCE == _lastInputs){
    _lastInputs = "";
    if(_onPerformedCallback != null){
      _onPerformedCallback();        
    }
  } else if(_lastInputs.length > KONAMICODE_SEQUENCE.length){
    _lastInputs = _lastInputs.substring(_lastInputs.length-KONAMICODE_SEQUENCE.length);
  }
}

void set onPerformed(callback()) {
  _onPerformedCallback = callback;
}
}

La prima istruzione stabilisce che si sta definendo una libreria.

Vediamo quali sono gli elementi principali:

  • KONAMICODE_SEQUENCE

    È una costante che contiene la sequenza di input che stiamo aspettando

  • lastInputs

    È una variabile, contiene gli ultimi input ricevuti

  • KonamiCode()

    Istruzioni da eseguire quando viene inizializzata una istanza di KonamiCode():

    • a _lastInputs viene dato un valore (vuoto) iniziale
    • il metodo _OnKeyDown() viene abbinato all’evento keyDown
  • callback

    La funzione da eseguire quando è stato riconosciuto il Konami Code

  • onPerformed()

    Imposta la funzione di callback da invocare quando la sequenza viene riconosciuta

  • onKeyDown():

    Questo è il nucleo principale di KonamiCode() che riceve l’input da tastiera

      var keyCode = event.keyCode 
    

    lo accoda agli input precedenti memorizzati in _lastInputs

      _lastInputs = _lastInputs.concat(keyCode.toString());
    

    se _lastInputs è il Konami Code allora resetta _lastInputs ed invoca la funzione onPerformedCallback() (se esiste)

      if(KONAMICODE_SEQUENCE == _lastInputs){
      _lastInputs = "";
      if(_onPerformedCallback != null){
        _onPerformedCallback();        
      }
    

    altrimenti continua ad accodare input, mantenendo solo gli ultimi n input tali che la lunghezza di _lastInputs non sia maggiore di quella di KONAMICODE_SEQUENCE

    else if(_lastInputs.length > KONAMICODE_SEQUENCE.length){
      _lastInputs = _lastInputs.substring(_lastInputs.length-KONAMICODE_SEQUENCE.length);
    

Vediamo ora come viene implementata questa classe in un’applicazione di esempio, che trovate all’interno del progetto. Proprio come nell’esempio precedente, anche in questo caso sample.html non contiene altro che l’interfaccia grafica dell’applicazione.

Guardiamo sample.dart:

#import("dart:html");
#import('packages/konami_code/konami_code.dart');

main(){
  var konami = new KonamiCode();
  konami.onPerformed = _changeText;
}

_changeText(){
  query('#container').text = "Konami code performed !";
}  

Dopo l’importazione di dart:html si procede con l’importazione della libreria konami_code.dart. Il percorso di questa libreria potrebbe a prima vista disorientarci:

packages/konami_code/konami_code.dart

Nel progetto che abbiamo scaricato non c’era nessuna directory chiamata packages, in più nel percorso non c’è nessun riferimento alla directory lib o al fatto che i files di esempio risiedano in una sotto directory del progetto chiamata example.

Come è possibile?

Quando abbiamo aperto il progetto in DartEditor, il package manager pub ha silenziosamente analizzato le dipendenze di tutti i files e creato una serie di collegamenti simbolici tali da agevolarne l’importazione. Nella scheda “Output” dell’editor infatti possiamo vedere questo messaggio:

Running pub install ...
Resolving dependencies...
Dependencies installed!

Tornando alla directory del progetto, notiamo come sia cambiata la struttura:

├── Readme.md
├── example
│   ├── packages -> /percorso/di/KonamiDart/packages
│   ├── sample.dart
│   └── sample.html
├── lib
│   └── konami_code.dart
├── packages
│   └── konami_code -> /percorso/di/KonamiDart/lib
├── pubspec.lock
└── pubspec.yaml

Torniamo al codice e analizziamo le due funzioni:

main() inizializza una nuova istanza di KonamiCode() identificata con konami e stabilisce che la funzione da invocare al riconoscimento del Konami Code sia _changeText:

konami.onPerformed = _changeText;

La funzione _changeText() non fa altro che impostare il testo di

con “Konami code performed!”.

Come per l’esempio precedente, avviamo l’applicazione dal menù Run -> Run.

Termina qui la parte introduttiva di questa serie di articoli su Dart: abbiamo introdotto le caratteristiche del linguaggio e le novità principali, e abbiamo notato quanto siano user-friendly gli strumenti offerti e come sia intuitivo iniziare a sviluppare semplici applicazioni.

Nei prossimi articoli esploreremo tutte le funzioni di DartEditor e pub, soffermandoci più a lungo sulle librerie del core e sulle librerie principali.

Leave a Reply

Claudio d'Angelis Articolo scritto da

Programmatore e studente di Informatica, appassionato di musica, web e sistemi UNIX. Collabora con Googlab dall'Ottobre 2012.

Contatta l'autore

Previous post:

Next post: