跳到主要内容

为何需要不可变性

什么是不可变性?

不可变性指当一个对象中的所有域(属性)是final或late final的。 它们只能通过构造器赋值一次。

出于许多原因,不可变性是比较理想的方式:

  • 值相等而不是引用相等
  • 关于代码的局部推理
    • 远处的一段代码不能从你的下面获得引用并改变对象
  • 对于异步和并行任务更容易推理
    • 其他代码不能在操作中改变对象
  • API安全
    • 你传递给方法的东西不会被访问者或被访问者改变

当创建了一个只改变几处的新对象时,copyWith方法可以帮助你减少冗余

拷贝的效率比你想的更高,dart可以重用对未更改的字对象的引用。

危险

确保你的对象是深度不可变的,否则你必须实现某种深拷贝机制。

最佳实践

你可以用任何package来创建你想要的不可变状态。

对于不可变对象:

对于不可变集合 (Map, Set, List):

非常推荐freezed这个package,它除了创建不可变对象外,还提供了一些不错的附加功能,包括:

  • 生成copyWith方法
  • 深拷贝 (在嵌套的freezed对象的copyWith方法)
  • 联合类型
  • 联合映射函数

使用不可变状态不一定需要代码生成,但它能让这一过程变得更简单。

危险

如果你想使用内置的集合,请确保实现更新集合时执行复制集合的规则。 不复制集合的问题在于,riverpod会根据对对象的引用是否更改来确定是否发出新的状态。 如果仅调用一个改变对象的方法,那么使用引用也是可行的。

使用不可变状态

不可变状态最适合 StateNotifierStateNotifierProvider 结合使用。 StateNotifier允许你暴露一个可以“改变”状态的接口。 你不能从你定义的继承自 StateNotifier 的对象外面改变他的状态。 这分离了你的关注点,并将业务逻辑保留在UI之外。

下面是一个例子,通过一个简单的不可变设置类来改变应用主题。

final themeProvider = StateNotifierProvider((ref) => ThemeNotifier());

class ThemeNotifier extends StateNotifier<ThemeSettings> {
ThemeNotifier(): super(
ThemeSettings(
mode: ThemeMode.system,
primaryColor: Colors.blue,
));

void toggle() {
state = state.copyWith(mode: state.mode.toggle);
}
void setDarkTheme() {
state = state.copyWith(mode: ThemeMode.dark);
}
void setLightTheme() {
state = state.copyWith(mode: ThemeMode.light);
}
void setSystemTheme() {
state = state.copyWith(mode: ThemeMode.system);
}
void setPrimaryColor(Color color) {
state = state.copyWith(primaryColor: color);
}

}


class ThemeSettings with _$ThemeSettings {
const factory ThemeSettings({ThemeMode mode, Color primaryColor}) = _ThemeSettings;
}

extension ToggleTheme on ThemeMode {
ThemeMode get toggle {
switch (this){
case ThemeMode.dark:
return ThemeMode.light;
case ThemeMode.light:
return ThemeMode.dark;
case ThemeMode.system:
return ThemeMode.system;
}
}
}

要使用这段代码,记住你需要引入 freezed_annotation这个package,并运行 build_runner 来构建freezed生成的类。