Flutter微框架Nylo(十四):状态管理

207 阅读4分钟

在 Nylo 中创建页面时,页面将扩展 NyState 类。该类提供了有用的实用工具,使开发更容易。

如何使用NyState?

您可以通过扩展来开始使用此类。

class _HomePageState extends NyState<HomePage> {

  @override
  init() async {
    super.init();
    
  }

一旦您的页面扩展了 NyState,您就可以使用 init 方法初始化 widget。该方法在 initState 内部的 Flutter 状态中调用,可以更方便地调用异步函数。

要在 Nylo 中创建新页面,可以运行以下命令。

dart run nylo_framework:main make:page product_page

或者

metro make:page product_page

状态管理

class _SettingsTabState extends NyState<SettingsTab> {

  _SettingsTabState() {
    stateName = SettingsTab.state;
  }

  @override
  init() async {
    super.init();
    
  }
  
  @override
  void stateUpdated(data) {
    // e.g. to update this state from another class
    // updateState(SettingsTab.state, data: "example payload");
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Cart(),
    );
  }
}

配置

  • Color

返回当前主题的颜色。

class _HomePageState extends NyState<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Text("The page loaded", style: TextStyle(
          color: color().primaryContent
        )
      )
    );
  }
  • boot(启动)

boot 方法与 afterLoad 方法结合使用,使异步调用更容易。您可以在 boot 内对 Future 方法调用 await,在等待期间,afterLoad 方法将显示一个加载器(来自您的 config/design.dart 文件)。启动方法完成后,将显示您选择的子组件。

class _HomePageState extends NyState<HomePage> {

  @override
  boot() async {
    await Future.delayed(Duration(seconds: 4));
    print('After 4 seconds...');
  }

  @override
  Widget build(BuildContext context) {
      return Scaffold(
          body: Center(
              child: afterLoad(child: () {
                return Text("The page loaded");
              })
          )
      );
  }
  • Reboot(重启)

此方法将在您的状态下重新运行启动方法。如果要刷新页面上的数据,这个方法很有用。

class _HomePageState extends NyState<HomePage> {

  List<User> users = [];

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

  @override
  Widget build(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

从路由堆栈中删除当前页面。

class _HomePageState extends NyState<HomePage> {
  
  popView() {
    pop();
  }

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

显示 Toast 通知。

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 build(BuildContext context) {
    return Scaffold(
      body: InkWell(
        onTap: displayToast,
        child: Text("Display a toast")
      )
    );
  }
  • validate

validate 帮助程序对数据执行验证检查。

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

  handleForm() {
    String textEmail = _textFieldControllerEmail.text;

    validate(rules: {
        "email address": "email"
      }, data: {
        "email address": textEmail
    }, onSuccess: () {
      print('passed validation')
    });
  }
  • changeLanguage

您可以调用以 changeLanguage 更改设备上使用的 json /lang 文件。

class _HomePageState extends NyState<HomePage> {
  
  changeLanguageES() {
    await changeLanguage('es');
  }

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

当应用程序处于特定状态时,您可以使用 whenEnv 运行函数。例如,如果 .env 文件中的 APP_ENV 变量设置为 "开发中",则 APP_ENV=developing。

class _HomePageState extends NyState<HomePage> {

  TextEditingController _textEditingController = TextEditingController();
  
  @override
  init() async {
    super.init();
    whenEnv('developing', perform: () {
      _textEditingController.text = 'test-email@gmail.com';
    });
  }
  • lockRelease

该方法将在函数调用后锁定状态,只有在该方法结束后才允许用户提出后续请求。该方法也会更新状态,请使用 isLocked 进行检查。

要展示 lockRelease,最好的例子是设想当用户点击 "登录 "时,我们有一个登录屏幕。我们希望执行异步调用来登录用户,但我们不希望该方法被多次调用,因为这可能会产生不受欢迎的体验。

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

一旦您点击 _login 方法,它就会阻止任何后续请求,直到原始请求结束。isLocked('login_to_app') 辅助函数用于检查按钮是否锁定。在上面的示例中,您可以看到我们使用它来确定何时显示加载小工具。

  • isLocked

该方法将使用 lockRelease 辅助函数检查状态是否已锁定。

class _HomePageState extends NyState<HomePage> {

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

该方法将检查状态是否正在加载。

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

该方法可用于显示加载程序,直到状态完成“加载”。

您还可以使用afterLoad(child: () {}, loadingKey: 'home_data')参数检查其他加载键。

class _HomePageState extends NyState<HomePage> {

  @override
  init() async {
    super.init();

    awaitData(perform: () async {
        await Future.delayed(Duration(seconds: 4));
        print('4 seconds after...');
    });
  }

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

该方法将检查状态是否已锁定。

如果状态为锁定,则将显示加载组件。

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 Future.delayed(Duration(seconds: 4));
      print('4 seconds after...');
    });
  }
}
  • afterNotNull

我们可以使用 afterNotNull 来显示加载中的 widget,直到变量被设置。

试想一下,如果您需要使用 Future 调用从数据库中获取用户账户,这可能需要 1-2 秒的时间,那么您可以对该值使用 afterNotNull 直到获取到数据。

class _HomePageState extends NyState<HomePage> {

  User? _user;

  @override
  init() async {
    super.init();
    _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

您可以使用 setLoading 更改为 "加载 "状态。

第一个参数接受一个 bool 值,表示是否正在加载,下一个参数允许你为加载状态设置一个名称,例如 setLoading(true,name:'refreshing_content');

class _HomePageState extends NyState<HomePage> {

  @override
  init() async {
    super.init();
    setLoading(true, name: 'refreshing_content');

    await Future.delayed(Duration(seconds: 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")
    );
  }