Yaz

用于状态、内容和用户选项管理的 Yaz 包

此包支持 Web、移动和桌面应用程序。

Yaz 包包含两部分;

1) 状态管理

演示应用视频

  • Yaz 包首先是一个状态管理包。由于它仅使用 `ChangeNotifier` 来完成整个过程,因此它非常简单和快速。
    它只使用一个 widget。即“YazListenerWidget”widget

  • Yaz 帮助您通过简单的代码将任何对象转换为 `ChangeNotifier`。

  • Yaz 支持单独或全部集合的更改。

2) 内容管理

  • Yaz 使您能够通过简单的代码从应用程序的任何部分获取和更改用户选项。

  • Yaz 帮助您存储和缓存应用程序中常用的内容。

Flutter 计数器应用示例


/// You don't need a stateful widget
class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  /// 1 ) Define your variable and convert notifier
  final counter = 0.notifier;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            /// 2 ) Convert Widget your ChangeNotifier
            counter.builder((context) => Text(
                  '${counter.value}',
                  style: Theme.of(context).textTheme.headline4,
                )),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// 3) Increment Count
          counter.value++;
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

用法

如何将对象转换为 Change Notifier?

任何对象都可以转换为 `ChangeNotifier`:`YazNotifier`

`.notifier` 适用于任何对象并返回一个 `YazNotifier`

    var counter = 0.notifier;
    var editing = "Mehmet Yaz".notifier; 
    var foo = Foo().notifier; 

`.value` 获取和设置 `YazNotifier` 实例中的值;

此 setter 会触发所有监听器

    print(counter.value);
    // output - 0 -
    
    counter.value++;
    
    print(counter.value);
    // output - 1 -

!注意

如果您希望监听 `foo` 实例成员中的更改

    // DON'T
    var foo = Foo().notifier;
    // DO
    var bar = Foo().bar.notifier;

因为 `foo` 仅以此方式触发;

    foo.value =  Foo(); // new instance
    foo.value = anotherFoo;

BuiltNotifier

当 `BuiltNotifier` 包装的 widget 重建时,您可以看到它们。它们在重建时会闪烁。

  BuiltNotifier(
        child: YourWidget()
        );

single_changes--1-

YazListenerWidget

`YazListenerWidget` 可与任何 `ChangeNotifier` 实例一起使用。没有其他 widget 用于状态管理。

    YazListenerWidget(
      changeNotifier: changeNotifier,
      builder: (BuildContext c) {
        return MyWidget();  
      }     
    );

您还可以通过 `.builder` 方法创建 `YazListenerWidget` 实例,该方法可用于任何 `ChangeNotifier`。

  changeNotifier.builder((context) => MyWidget());

!性能提示

尽可能缩小此 widget 的范围。使用更多的 `YazListenerWidget` 比使用一个 widget 来包装所有内容更好。

单个变量

  /// define notifier
  var counter = 0.notifier;

  /// Listen with widget
  YazListenerWidget(
    changeNotifier: counter,
    builder: (BuildContext c) {
    return Text(counter.value.toString());
  }
  
  //or

  counter.builder((context) => Text(counter.value.toString()))
);

single_changes

多个变量

  /// define notifiers
  final counter1 = 0.notifier;
  final counter2 = 5.notifier;
  final counter3 = 10.notifier;

  /// Listen with widget
  YazListenerWidget(
    changeNotifier: counter1.combineWith([counter2, counter3]),
    /// or changeNotifier:  MultipleChangeNotifier([counter1 , counter2 ,counter3]),
    /// or changeNotifier: [counter1 , counter2, counter3].notifyAll
    builder: (BuildContext c) {
    return Text(counter.value.toString());
  }
);

multiple_changes

如何监听集合更改?

有 3 种方法可以监听集合

1) 经典总更改 - 糟糕的方式

通过这种方式,使用标准的 `.notifier`

但这仅以此方式触发;

    var list = [0,1,2,3].notifier
    
    list.value = [10 , 20 , 30]; // Triggered
    
    list.value[0] = 50; // Not Triggered

    // USAGE
    YazListenerWidget(
        changeNotifier: list,
        //....
    );

2) 任何更改时触发 - 中等方式

通过这种方式,创建了一个 YazList。

可以通过所有 `List` 实例的 `.listenAll` 来创建 `YazList`。

通过这种方式,也可以从所有 `Map` 创建 `YazMap`。

`YazList` 是一个 `ChangeNotifier`,并且所有监听器在任何更改时都会被触发。

    YazList<int> list = [0,1,2,3].listenAll;
    list[0] = 5; // Triggered
    list.add(10) = 10; // Triggered
    list.remove(0) = 30; // Triggered
    /// triggered by all methods similarly
    // USAGE
    YazListenerWidget(
      changeNotifier: list,
      //....
    );

list_2

3) 每隔元素单独触发 - 好的方式

您可以通过 `.listeners` 方法在任何 `List` 上获取 `List<YazNotifier>`。

通过这种方式,元素的 `value` 更改会触发所有监听器。

当进行长度更改(如添加或删除元素)或进行 `YazNotifier` 实例更改时;监听器不会被触发。

例如,假设有两个独立的 widget 分别监听索引 0 和索引 1。
当索引 0 更改时,只有 widget 0 会重建。
另一个则不会重建。

为了触发所有更改,可以使用 `List<YazNotifier>` 上的 `.notifyAll` 方法。

var listeners = [0,1].listeners;

// 0. Widget
YazListenerWidget(
changeNotifier: listeners[0],
//....
);

// 1. Widget
YazListenerWidget(
changeNotifier: listeners[1],
//....
);

// 2. Widget
YazListenerWidget(
changeNotifier: listeners.notifyAll,
//....
);

listeners[0].value++; // Rebuilt 0. and 2. Widgets
listeners[1].value++; // Rebuilt 1. and 2. Widgets

list_3

所有这些都可应用于 `Map`。
您可以通过 `Map` 上的 `.listeners` 获取 `Map<K, YazNotifier<V>>`。

如何监听 Stream?

所有 stream 都可以通过 Stream 上的 `.yazStream` 或 `.yazStreamWithDefault(value)` 进行转换。

`.yazStream` 返回可空的 `YazStream<T?>`。

`.yazStreamWithDefault(value)` 返回非可空的 `YazStream<T>`。

YazStream<User?> stream = db.getChanges().yazStream;
stream.builder((context) => YourWidget());

/// DON'T FORGET
stream.cancel();

用户选项

简单示例

您可以使用此服务管理所有用户选项。

///
class MyHomePage extends StatelessWidget {
  ///
  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        /// Wrap widgets with UserOption
        /// use default value or initialize on your splash screen
        child: OptionWrapper<double>(
          userOption: UserOption("font_size_multiplier", defaultValue: 0.5),
          builder: (c, option) {
            return Text(
              "User Option Example",
              style: TextStyle(fontSize: 10.0 * option.value), 
              /// Rebuild with new option
              /// or use directly :
              /// fontSize: 10.0 * UserOption("font_size_multiplier").value, 
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// Change options
          UserOption<double>("font_size_multiplier").value += 0.1;
        },
        tooltip: 'Increment Font Size',
        child: const Icon(Icons.add),
      ),
    );
  }
}

用法

您可以在任何地方获取选项

  UserOption<double>("font_size").value;

并且您可以更改

    UserOption<double>("font_size").value = 10.0;

您可以将 widget 包装在选项中,当选项更改时,widget 会重建;

  OptionsWrapper(
    option: UserOption<double>("font_size"),
    builder : (c , option) {
      return Text("Hello World!" , style:TextStyle(
        fontSize: option.value
      ));  
    }   
  )

您可以使用默认值进行初始化;

  UserOptions().init(List<UserOption> list);

您可以监听更改以在数据库中获取

UserOptions().onUserOptionsChange((options){
  //fetch
});

如果您不使用 init 函数,您可以在首次使用时设置默认值。
您不必为会话流程中的后续使用设置默认值。

UserOption<double>("font_size" , defaultValue: 10);

内容控制器

您可以存储或缓存常用的内容。

例如:好友用户、电子商务类别等。

只需!您将通过其标识符获取内容。

您不需要第二次从数据库查询来重复回忆内容。

但是,如果内容太旧,它将从数据库中恢复。

  var controller = UserController();
  await controller.getContent("user_1");
  /// If content was brought before will get from local
  /// else will get from db, save to local and return to you

用法

实现 `CacheAble`

您的可缓存类必须有一个唯一的标识符。


///
class User with CacheAble<User> {
  ///
  User(this.userId, this.userName);
  
  @override
  String get identifier => userId;

  ///
  String userId;

  ///
  String userName;
}

实现您的控制器

有两种内容控制器

  1. CacheContentController:仅存储会话中的内容
  2. StorageContentController:将内容存储在设备的键值存储中
///
class UserController extends StorageContentController<User> {
  
  /// Singleton recommended

  /// Getter for your content by identifier
  /// common use this getter get content from db
  @override
  Future<User?> contentGetter(String identifier) async {
    await Future.delayed(const Duration(milliseconds: 300));
    return User(identifier, "User Of $identifier");
  }

  /// DON'T NEED for CacheContentController
  /// NEED for store device storage
  @override
  User fromJson(Map<String, dynamic> map) {
    return User.fromJson(map);
  }

  /// Maximum storage count
  @override
  int get maxCount => 30;

  
  /// Maximum storage duration
  /// If your content older than, recall getter from db
  @override
  Duration get maxDuration => const Duration(minutes: 30);

  /// Save key for local storage
  /// 
  /// DON'T NEED for CacheContentController
  /// NEED for store device storage
  @override
  String get saveKey => "users";

  /// DON'T NEED for CacheContentController
  /// NEED for store device storage
  @override
  Map<String, dynamic> toJson(User instance) {
    return instance.toJson();
  }
}

您也可以使用未排序的内容进行初始化

controller.init(contents);

保存、更新或移除内容
但在许多情况下您不需要这样做。因为如果您仔细确定了最大值,
`getContent` 会为您处理这些。

controller.save(content);
controller.remove(id);
controller.update(id);

GitHub

https://github.com/Mehmetyaz/yaz