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
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")
);
}