Basics

NyState

Einleitung

NyState ist eine erweiterte Version der Standard-Flutter-State-Klasse. Sie bietet zusätzliche Funktionen, um den State Ihrer Seiten und Widgets effizienter zu verwalten.

Sie können mit dem State interagieren genau wie mit einem normalen Flutter-State, aber mit den zusätzlichen Vorteilen von NyState.

Schauen wir uns an, wie man NyState verwendet.

NyState verwenden

Sie können diese Klasse verwenden, indem Sie sie erweitern.

Beispiel

class _HomePageState extends NyState<HomePage> {

  @override
  get init => () async {

  };

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

Die init-Methode wird verwendet, um den State der Seite zu initialisieren. Sie können diese Methode mit oder ohne async verwenden, und im Hintergrund wird der asynchrone Aufruf behandelt und ein Loader angezeigt.

Die view-Methode wird verwendet, um die UI der Seite anzuzeigen.

Ein neues Stateful Widget mit NyState erstellen

Um eine neue Seite in Nylo Website zu erstellen, können Sie den folgenden Befehl ausführen.

metro make:stateful_widget ProfileImage

Ladestil

Sie können die loadingStyle-Eigenschaft verwenden, um den Ladestil für Ihre Seite festzulegen.

Beispiel

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

Der Standard-loadingStyle wird Ihr Lade-Widget (resources/widgets/loader_widget.dart) sein. Sie können den loadingStyle anpassen, um den Ladestil zu aktualisieren.

Hier ist eine Tabelle für die verschiedenen Ladestile, die Sie verwenden können: // normal, skeletonizer, none

Stil Beschreibung
normal Standard-Ladestil
skeletonizer Skeleton-Ladestil
none Kein Ladestil

Sie können den Ladestil wie folgt ändern:

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

Wenn Sie das Lade-Widget in einem der Stile aktualisieren möchten, können Sie ein child an den LoadingStyle übergeben.

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

Wenn der Tab lädt, wird der Text "Loading..." angezeigt.

Beispiel unten:

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

State-Aktionen

In Nylo können Sie kleine Aktionen in Ihren Widgets definieren, die von anderen Klassen aufgerufen werden können. Dies ist nützlich, wenn Sie den State eines Widgets von einer anderen Klasse aktualisieren möchten.

Zuerst müssen Sie Ihre Aktionen in Ihrem Widget definieren. Dies funktioniert für NyState und 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);
    },
  };
}

Dann können Sie die Aktion von einer anderen Klasse mit der stateAction-Methode aufrufen.

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

Wenn Sie stateActions mit einem NyPage verwenden, müssen Sie den Pfad der Seite verwenden.

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

Es gibt auch eine weitere Klasse namens StateAction, die einige Methoden hat, mit denen Sie den State Ihrer Widgets aktualisieren können.

  • refreshPage - Die Seite aktualisieren.
  • pop - Die Seite entfernen.
  • showToastSorry - Eine Sorry-Toast-Benachrichtigung anzeigen.
  • showToastWarning - Eine Warnung-Toast-Benachrichtigung anzeigen.
  • showToastInfo - Eine Info-Toast-Benachrichtigung anzeigen.
  • showToastDanger - Eine Gefahr-Toast-Benachrichtigung anzeigen.
  • showToastOops - Eine Oops-Toast-Benachrichtigung anzeigen.
  • showToastSuccess - Eine Erfolgs-Toast-Benachrichtigung anzeigen.
  • showToastCustom - Eine benutzerdefinierte Toast-Benachrichtigung anzeigen.
  • validate - Daten aus Ihrem Widget validieren.
  • changeLanguage - Die Sprache in der Anwendung aktualisieren.
  • confirmAction - Eine Bestätigungsaktion ausführen.

Beispiel

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

Sie können die StateAction-Klasse verwenden, um den State jeder Seite/jedes Widgets in Ihrer Anwendung zu aktualisieren, solange das Widget state-verwaltet ist.

Helfer

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

Reboot

Diese Methode führt die init-Methode in Ihrem State erneut aus. Dies ist nützlich, wenn Sie die Daten auf der Seite aktualisieren möchten.

Beispiel

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 - Die aktuelle Seite vom Stack entfernen.

Beispiel

class _HomePageState extends NyState<HomePage> {

  popView() {
    pop();
  }

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

showToast

Eine Toast-Benachrichtigung im Kontext anzeigen.

Beispiel

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

Der validate-Helfer führt eine Validierungsprüfung auf Daten durch.

Sie können mehr über den Validator hier erfahren.

Beispiel

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

  handleForm() {
    String textEmail = _textFieldControllerEmail.text;

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

changeLanguage

Sie können changeLanguage aufrufen, um die json-/lang-Datei zu ändern, die auf dem Gerät verwendet wird.

Erfahren Sie mehr über Lokalisierung hier.

Beispiel

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

Sie können whenEnv verwenden, um eine Funktion auszuführen, wenn sich Ihre Anwendung in einem bestimmten Zustand befindet. Z.B. Ihre APP_ENV-Variable in Ihrer .env-Datei ist auf 'developing' gesetzt, APP_ENV=developing.

Beispiel

class _HomePageState extends NyState<HomePage> {

  TextEditingController _textEditingController = TextEditingController();

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

lockRelease

Diese Methode sperrt den State, nachdem eine Funktion aufgerufen wurde, und erlaubt dem Benutzer erst dann nachfolgende Anfragen, wenn die Methode beendet ist. Diese Methode aktualisiert auch den State. Verwenden Sie isLocked, um den Status zu prüfen.

Das beste Beispiel, um lockRelease zu demonstrieren, ist sich vorzustellen, dass wir einen Login-Bildschirm haben und wenn der Benutzer auf 'Login' tippt. Wir möchten einen asynchronen Aufruf durchführen, um den Benutzer anzumelden, aber wir möchten nicht, dass die Methode mehrfach aufgerufen wird, da dies zu einem unerwünschten Erlebnis führen könnte.

Hier ist ein Beispiel unten.

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

Sobald Sie die _login-Methode aufrufen, blockiert sie alle nachfolgenden Anfragen, bis die ursprüngliche Anfrage beendet ist. Der isLocked('login_to_app')-Helfer wird verwendet, um zu prüfen, ob der Button gesperrt ist. Im obigen Beispiel können Sie sehen, dass wir damit bestimmen, wann unser Lade-Widget angezeigt wird.

isLocked

Diese Methode prüft, ob der State mit dem lockRelease-Helfer gesperrt ist.

Beispiel

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

Die view-Methode wird verwendet, um die UI der Seite anzuzeigen.

Beispiel

class _HomePageState extends NyState<HomePage> {

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

confirmAction

Die confirmAction-Methode zeigt dem Benutzer einen Dialog an, um eine Aktion zu bestätigen. Diese Methode ist nützlich, wenn Sie möchten, dass der Benutzer eine Aktion bestätigt, bevor er fortfährt.

Beispiel

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

showToastSuccess

Die showToastSuccess-Methode zeigt dem Benutzer eine Erfolgs-Toast-Benachrichtigung an.

Beispiel

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

showToastOops

Die showToastOops-Methode zeigt dem Benutzer eine Oops-Toast-Benachrichtigung an.

Beispiel

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

showToastDanger

Die showToastDanger-Methode zeigt dem Benutzer eine Gefahr-Toast-Benachrichtigung an.

Beispiel

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

showToastInfo

Die showToastInfo-Methode zeigt dem Benutzer eine Info-Toast-Benachrichtigung an.

Beispiel

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

showToastWarning

Die showToastWarning-Methode zeigt dem Benutzer eine Warnung-Toast-Benachrichtigung an.

Beispiel

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

showToastSorry

Die showToastSorry-Methode zeigt dem Benutzer eine Sorry-Toast-Benachrichtigung an.

Beispiel

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

isLoading

Die isLoading-Methode prüft, ob der State lädt.

Beispiel

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

Die afterLoad-Methode kann verwendet werden, um einen Loader anzuzeigen, bis der State das 'Laden' beendet hat.

Sie können auch andere Ladeschlüssel mit dem Parameter loadingKey prüfen: afterLoad(child: () {}, loadingKey: 'home_data').

Beispiel

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

Die afterNotLocked-Methode prüft, ob der State gesperrt ist.

Wenn der State gesperrt ist, wird das [loading]-Widget angezeigt.

Beispiel

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

Sie können afterNotNull verwenden, um ein Lade-Widget anzuzeigen, bis eine Variable gesetzt wurde.

Stellen Sie sich vor, Sie müssen das Konto eines Benutzers aus einer DB mit einem Future-Aufruf abrufen, der 1-2 Sekunden dauern könnte. Sie können afterNotNull auf diesen Wert anwenden, bis Sie die Daten haben.

Beispiel

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

Sie können mit setLoading in einen 'Lade'-Zustand wechseln.

Der erste Parameter akzeptiert einen bool für den Ladezustand, der nächste Parameter ermöglicht es Ihnen, einen Namen für den Ladezustand festzulegen, z.B. setLoading(true, name: 'refreshing_content');.

Beispiel

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