为何需要不可变性
什么是不可变性?
不可变性指当一个对象
中的所有域(属性)是final或late final的。
它们只能通过构造器赋值一次。
出于许多原因,不可变性是比较理想的方式:
- 值相等而不是引用相等
- 关于代码的局部推理
- 远处的一段代码不能从你的下面获得引用并改变对象
- 对于异步和并行任务更容易推理
- 其他代码不能在操作中改变对象
- API安全
- 你传递给方法的东西不会被访问者或被访问者改变
当创建了一个只改变几处的新对象时,copyWith方法可以帮助你减少冗余
拷贝的效率比你想的更高,dart可以重用对未更改的字对象的引用。
危险
确保你的对象是深度不可变的,否则你必须实现某种深拷贝机制。
最佳实践
你可以用任何package来创建你想要的不可变状态。
对于不可变对象:
对于不可变集合 (Map, Set, List):
非常推荐freezed这个package,它除了创建不可变对象外,还提供了一些不错的附加功能,包括:
- 生成copyWith方法
- 深拷贝 (在嵌套的freezed对象的copyWith方法)
- 联合类型
- 联合映射函数
使用不可变状态不一定需要代码生成,但它能让这一过程变得更简单。
危险
如果你想使用内置的集合,请确保实现更新集合时执行复制集合的规则。 不复制集合的问题在于,riverpod会根据对对象的引用是否更改来确定是否发出新的状态。 如果仅调用一个改变对象的方法,那么使用引用也是可行的。
使用不可变状态
不可变状态最适合 StateNotifier 和 StateNotifierProvider 结合使用。 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生成的类。