Basics

NyState

Introduzione

NyState e' una versione estesa della classe State standard di Flutter. Fornisce funzionalita' aggiuntive per aiutare a gestire lo stato delle pagine e dei widget in modo piu' efficiente.

Puoi interagire con lo stato esattamente come faresti con un normale stato Flutter, ma con i vantaggi aggiuntivi di NyState.

Vediamo come usare NyState.

Come usare NyState

Puoi iniziare a usare questa classe estendendola.

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () async {

  };

  @override
  view(BuildContext context) {
    return Scaffold(
        body: Text("The page loaded")
    );
  }

Il metodo init viene utilizzato per inizializzare lo stato della pagina. Puoi usare questo metodo come asincrono o senza async e dietro le quinte, gestira' la chiamata asincrona e mostrera' un loader.

Il metodo view viene utilizzato per visualizzare l'interfaccia utente della pagina.

Creare un nuovo widget stateful con NyState

Per creare un nuovo widget stateful in Nylo Website, puoi eseguire il comando seguente.

metro make:stateful_widget ProfileImage

Stile di Caricamento

Puoi usare la proprieta' loadingStyle per impostare lo stile di caricamento per la tua pagina.

Esempio

class _ProfileImageState extends NyState<ProfileImage> {

  @override
  LoadingStyleType get loadingStyle => LoadingStyleType.normal();

  @override
  get init => () async {
    await sleep(3); // simulate a network call for 3 seconds
  };

Il loadingStyle predefinito sara' il tuo Widget di caricamento (resources/widgets/loader_widget.dart). Puoi personalizzare il loadingStyle per aggiornare lo stile di caricamento.

Ecco una tabella per i diversi stili di caricamento che puoi utilizzare: // normal, skeletonizer, none

Stile Descrizione
normal Stile di caricamento predefinito
skeletonizer Stile di caricamento a scheletro
none Nessuno stile di caricamento

Puoi cambiare lo stile di caricamento in questo modo:

@override
LoadingStyle get loadingStyle => LoadingStyle.normal();
// or
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer();

Se vuoi aggiornare il Widget di caricamento in uno degli stili, puoi passare un child al LoadingStyle.

@override
LoadingStyle get loadingStyle => LoadingStyle.normal(
    child: Center(
        child: Text("Loading..."),
    ),
);
// same for skeletonizer
@override
LoadingStyle get loadingStyle => LoadingStyle.skeletonizer(
    child: Container(
        child: PageLayoutForSkeletonizer(),
    )
);

Ora, quando la scheda sta caricando, il testo "Loading..." verra' visualizzato.

Esempio qui sotto:

class _HomePageState extends NyState<HomePage> {
    get init => () async {
        await sleep(3); // simulate a network call for 3 seconds
    };

    @override
    LoadingStyle get loadingStyle => LoadingStyle.normal(
        child: Center(
            child: Text("Loading..."),
        ),
    );

    @override
    Widget view(BuildContext context) {
        return Scaffold(
            body: Text("The page loaded")
        );
    }
    ...
}

Azioni di Stato

In Nylo, puoi definire piccole azioni nei tuoi Widget che possono essere chiamate da altre classi. Questo e' utile se vuoi aggiornare lo stato di un widget da un'altra classe.

Per prima cosa, devi definire le tue azioni nel tuo widget. Questo funziona per NyState e NyPage.

class _MyWidgetState extends NyState<MyWidget> {

  @override
  get init => () async {
    // handle how you want to initialize the state
  };

  @override
  get stateActions => {
    "hello_world_in_widget": () {
      print('Hello world');
    },
    "update_user_name": (User user) async {
      // Example with data
      _userName = user.name;
      setState(() {});
    },
    "show_toast": (String message) async {
      showToastSuccess(description: message);
    },
  };
}

Poi, puoi chiamare l'azione da un'altra classe utilizzando il metodo stateAction.

stateAction('hello_world_in_widget', state: MyWidget.state);

// Another example with data
User user = User(name: "John Doe");
stateAction('update_user_name', state: MyWidget.state, data: user);
// Another example with data
stateAction('show_toast', state: MyWidget.state, data: "Hello world");

Se stai usando stateActions con un NyPage, devi usare il path della pagina.

stateAction('hello_world_in_widget', state: ProfilePage.path);

// Another example with data
User user = User(name: "John Doe");
stateAction('update_user_name', state: ProfilePage.path, data: user);

// Another example with data
stateAction('show_toast', state: ProfilePage.path, data: "Hello world");

C'e' anche un'altra classe chiamata StateAction, questa ha alcuni metodi che puoi usare per aggiornare lo stato dei tuoi widget.

  • refreshPage - Aggiorna la pagina.
  • pop - Chiudi la pagina.
  • showToastSorry - Visualizza una notifica toast di scuse.
  • showToastWarning - Visualizza una notifica toast di avviso.
  • showToastInfo - Visualizza una notifica toast informativa.
  • showToastDanger - Visualizza una notifica toast di pericolo.
  • showToastOops - Visualizza una notifica toast oops.
  • showToastSuccess - Visualizza una notifica toast di successo.
  • showToastCustom - Visualizza una notifica toast personalizzata.
  • validate - Valida i dati dal tuo widget.
  • changeLanguage - Aggiorna la lingua nell'applicazione.
  • confirmAction - Esegui un'azione di conferma.

Esempio

class _UpgradeButtonState extends NyState<UpgradeButton> {

  view(BuildContext context) {
    return Button.primary(
      onPressed: () {
        StateAction.showToastSuccess(UpgradePage.state,
          description: "You have successfully upgraded your account",
        );
      },
      text: "Upgrade",
    );
  }
}

Puoi usare la classe StateAction per aggiornare lo stato di qualsiasi pagina/widget nella tua applicazione purche' il widget sia gestito con lo stato.

Helper

lockRelease
showToast isLoading
validate afterLoad
afterNotLocked afterNotNull
whenEnv setLoading
pop isLocked
changeLanguage confirmAction
showToastSuccess showToastOops
showToastDanger showToastInfo
showToastWarning showToastSorry

Reboot

Questo metodo rieseguira' il metodo init nel tuo stato. E' utile se vuoi aggiornare i dati sulla pagina.

Esempio

class _HomePageState extends NyState<HomePage> {

  List<User> users = [];

  @override
  get init => () async {
    users = await api<ApiService>((request) => request.fetchUsers());
  };

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Users"),
          actions: [
            IconButton(
              icon: Icon(Icons.refresh),
              onPressed: () {
                reboot(); // refresh the data
              },
            )
          ],
        ),
        body: ListView.builder(
          itemCount: users.length,
          itemBuilder: (context, index) {
            return Text(users[index].firstName);
          }
        ),
    );
  }
}

Pop

pop - Rimuove la pagina corrente dallo stack.

Esempio

class _HomePageState extends NyState<HomePage> {

  popView() {
    pop();
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: popView,
        child: Text("Pop current view")
      )
    );
  }

showToast

Mostra una notifica toast sul contesto.

Esempio

class _HomePageState extends NyState<HomePage> {

  displayToast() {
    showToast(
        title: "Hello",
        description: "World",
        icon: Icons.account_circle,
        duration: Duration(seconds: 2),
        style: ToastNotificationStyleType.INFO // SUCCESS, INFO, DANGER, WARNING
    );
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: displayToast,
        child: Text("Display a toast")
      )
    );
  }

validate

L'helper validate esegue un controllo di validazione sui dati.

Puoi saperne di piu' sul validatore qui.

Esempio

class _HomePageState extends NyState<HomePage> {
TextEditingController _textFieldControllerEmail = TextEditingController();

  handleForm() {
    String textEmail = _textFieldControllerEmail.text;

    validate(rules: {
        "email address": [textEmail, "email"]
      }, onSuccess: () {
      print('passed validation')
    });
  }

changeLanguage

Puoi chiamare changeLanguage per cambiare il file json /lang utilizzato sul dispositivo.

Scopri di piu' sulla localizzazione qui.

Esempio

class _HomePageState extends NyState<HomePage> {

  changeLanguageES() {
    await changeLanguage('es');
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: changeLanguageES,
        child: Text("Change Language".tr())
      )
    );
  }

whenEnv

Puoi usare whenEnv per eseguire una funzione quando la tua applicazione e' in un determinato stato. Ad esempio, la tua variabile APP_ENV all'interno del file .env e' impostata su 'developing', APP_ENV=developing.

Esempio

class _HomePageState extends NyState<HomePage> {

  TextEditingController _textEditingController = TextEditingController();

  @override
  get init => () {
    whenEnv('developing', perform: () {
      _textEditingController.text = 'test-email@gmail.com';
    });
  };

lockRelease

Questo metodo blocchera' lo stato dopo che una funzione viene chiamata, solo fino a quando il metodo non ha terminato permettera' all'utente di fare richieste successive. Questo metodo aggiornera' anche lo stato, usa isLocked per controllare.

Il miglior esempio per mostrare lockRelease e' immaginare di avere una schermata di login quando l'utente tocca 'Login'. Vogliamo eseguire una chiamata asincrona per effettuare il login dell'utente ma non vogliamo che il metodo venga chiamato piu' volte perche' potrebbe creare un'esperienza indesiderata.

Ecco un esempio qui sotto.

class _LoginPageState extends NyState<LoginPage> {

  _login() async {
    await lockRelease('login_to_app', perform: () async {

      await Future.delayed(Duration(seconds: 4), () {
        print('Pretend to login...');
      });

    });
  }

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (isLocked('login_to_app'))
              AppLoader(),
            Center(
              child: InkWell(
                onTap: _login,
                child: Text("Login"),
              ),
            )
          ],
        )
    );
  }

Una volta toccato il metodo _login, blocchera' qualsiasi richiesta successiva fino a quando la richiesta originale non e' terminata. L'helper isLocked('login_to_app') viene usato per controllare se il pulsante e' bloccato. Nell'esempio sopra, puoi vedere che lo usiamo per determinare quando visualizzare il nostro Widget di caricamento.

isLocked

Questo metodo controllera' se lo stato e' bloccato utilizzando l'helper lockRelease.

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  Widget view(BuildContext context) {
    return Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (isLocked('login_to_app'))
              AppLoader(),
          ],
        )
    );
  }

view

Il metodo view viene utilizzato per visualizzare l'interfaccia utente della pagina.

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  Widget view(BuildContext context) {
      return Scaffold(
          body: Center(
              child: Text("My Page")
          )
      );
  }
}

confirmAction

Il metodo confirmAction visualizzera' un dialogo all'utente per confermare un'azione. Questo metodo e' utile se vuoi che l'utente confermi un'azione prima di procedere.

Esempio

_logout() {
 confirmAction(() {
    // logout();
 }, title: "Logout of the app?");
}

showToastSuccess

Il metodo showToastSuccess visualizzera' una notifica toast di successo all'utente.

Esempio

_login() {
    ...
    showToastSuccess(
        description: "You have successfully logged in"
    );
}

showToastOops

Il metodo showToastOops visualizzera' una notifica toast oops all'utente.

Esempio

_error() {
    ...
    showToastOops(
        description: "Something went wrong"
    );
}

showToastDanger

Il metodo showToastDanger visualizzera' una notifica toast di pericolo all'utente.

Esempio

_error() {
    ...
    showToastDanger(
        description: "Something went wrong"
    );
}

showToastInfo

Il metodo showToastInfo visualizzera' una notifica toast informativa all'utente.

Esempio

_info() {
    ...
    showToastInfo(
        description: "Your account has been updated"
    );
}

showToastWarning

Il metodo showToastWarning visualizzera' una notifica toast di avviso all'utente.

Esempio

_warning() {
    ...
    showToastWarning(
        description: "Your account is about to expire"
    );
}

showToastSorry

Il metodo showToastSorry visualizzera' una notifica toast di scuse all'utente.

Esempio

_sorry() {
    ...
    showToastSorry(
        description: "Your account has been suspended"
    );
}

isLoading

Il metodo isLoading controllera' se lo stato sta caricando.

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  Widget build(BuildContext context) {
    if (isLoading()) {
      return AppLoader();
    }

    return Scaffold(
        body: Text("The page loaded", style: TextStyle(
          color: colors().primaryContent
        )
      )
    );
  }

afterLoad

Il metodo afterLoad puo' essere usato per visualizzare un loader fino a quando lo stato non ha finito di 'caricare'.

Puoi anche controllare altre chiavi di caricamento utilizzando il parametro loadingKey afterLoad(child: () {}, loadingKey: 'home_data').

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () {
    awaitData(perform: () async {
        await sleep(4);
        print('4 seconds after...');
    });
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: afterLoad(child: () {
          return Text("Loaded");
        })
    );
  }

afterNotLocked

Il metodo afterNotLocked controllera' se lo stato e' bloccato.

Se lo stato e' bloccato visualizzera' il widget [loading].

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: afterNotLocked('login', child: () {
            return MaterialButton(
              onPressed: () {
                login();
              },
              child: Text("Login"),
            );
          }),
        )
    );
  }

  login() async {
    await lockRelease('login', perform: () async {
      await sleep(4);
      print('4 seconds after...');
    });
  }
}

afterNotNull

Puoi usare afterNotNull per mostrare un widget di caricamento fino a quando una variabile non e' stata impostata.

Immagina di dover recuperare l'account di un utente da un DB usando una chiamata Future che potrebbe richiedere 1-2 secondi, puoi usare afterNotNull su quel valore fino a quando hai i dati.

Esempio

class _HomePageState extends NyState<HomePage> {

  User? _user;

  @override
  get init => () async {
    _user = await api<ApiService>((request) => request.fetchUser()); // example
    setState(() {});
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: afterNotNull(_user, child: () {
          return Text(_user!.firstName);
        })
    );
  }

setLoading

Puoi passare a uno stato di 'caricamento' utilizzando setLoading.

Il primo parametro accetta un bool per indicare se sta caricando o meno, il parametro successivo ti permette di impostare un nome per lo stato di caricamento, ad es. setLoading(true, name: 'refreshing_content');.

Esempio

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () async {
    setLoading(true, name: 'refreshing_content');

    await sleep(4);

    setLoading(false, name: 'refreshing_content');
  };

  @override
  Widget build(BuildContext context) {
    if (isLoading(name: 'refreshing_content')) {
      return AppLoader();
    }

    return Scaffold(
        body: Text("The page loaded")
    );
  }