一个受 SolidJS 启发的简单状态管理库。
该项目的目标是
- 简单易学
- 不以奇怪的解决方法对抗框架(例如 Flutter)。
- 不要有一个全局状态,而是将多个状态放在最合适的位置
学习
您应该了解 4 个主要概念
信号
信号是 solidart 中响应式的基础。它们包含随时间变化的值;当您更改信号的值时,它会自动更新使用它的任何内容。
要创建信号,您必须使用 createSignal 方法
final counter = createSignal(0);
传递给 create 调用的参数是初始值,返回值是信号。
// Retrieve the current counter value
print(counter.value); // prints 0
// Increment the counter value
counter.value++;
如果您正在使用 flutter_solidart,您可以使用 SignalBuilder 小部件来自动响应信号值,例如
SignalBuilder(
signal: counter,
builder: (_, value, __) {
return Text('$value');
},
)
副作用
信号是可跟踪的值,但它们只占等式的一半。为了补充它们,还有可以被这些可跟踪值更新的观察者。副作用就是这样一个观察者;它会运行依赖于信号的副作用。
可以通过使用 createEffect 来创建副作用。副作用订阅 signals 数组中提供的任何信号,并在其中任何一个发生更改时重新运行。
因此,让我们创建一个在 counter 更改时重新运行的副作用
createEffect(() {
print("The count is now ${counter.value}");
}, signals: [counter]);
资源
资源是专门用于处理异步加载的特殊信号。它们的目的以一种易于交互的方式包装异步值。
资源可以由一个 source 信号驱动,该信号为异步数据 fetcher 函数提供查询,该函数返回一个 Future。
fetcher 函数的内容可以是任何东西。您可以命中典型的 REST 端点或 GraphQL 或任何生成 Future 的内容。资源对加载数据的方式没有意见,只是它们由 Future 驱动。
让我们创建一个资源
// The fetcher
Future<String> fetchUser() async {
final response = await http.get(
Uri.parse('https://swapi.dev/api/people/${userId.value}/'),
);
return response.body;
}
// The source
final userId = createSignal(1);
// The resource
final user = createResource(fetcher: fetchUser, source: userId);
如果您正在使用 ResourceBuilder,您可以响应资源的状态
ResourceBuilder(
resource: user,
builder: (_, resource) {
return resource.on(
// the call was successful
ready: (data, refreshing) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text(data),
subtitle: Text('refreshing: $refreshing'),
),
ElevatedButton(
// you can refetch if you want to update the data
onPressed: user.refetch,
child: const Text('Refresh'),
),
],
);
},
// the call failed.
error: (e, _) => Text(e.toString()),
// the call is loading.
loading: () {
return const RepaintBoundary(
child: CircularProgressIndicator(),
);
},
);
},
)
on 方法强制您处理资源的所有状态(ready、error 和 loading)。还有其他便捷方法可以仅处理特定状态。
Solid
Flutter 框架就像一个树。有祖先,也有后代。
您可能需要将信号传递到树的深处,这是不推荐的。您永远不应该将信号作为参数传递。
为了避免这种情况,我们有 Solid 小部件。
使用此小部件,您可以将信号传递到树中任何需要它的地方。
您已经看到过 Theme.of(context) 或 MediaQuery.of(context),这个过程几乎相同。
让我们看一个例子来理解这个概念。
您将看到如何使用 Solid 构建一个切换主题功能,此示例也在此处 https://github.com/nank1ro/solidart/tree/main/examples/toggle_theme
/// The identifiers used for [Solid] signals.
enum SignalId { // [1]
themeMode,
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Provide the theme mode signal to descendats
return Solid( // [2]
signals: {
// the id of the signal and the signal associated.
SignalId.themeMode: () => createSignal<ThemeMode>(ThemeMode.light),
},
child:
// using a builder here because the `context` must be a descendant of [Solid]
Builder(
builder: (context) {
// observe the theme mode value this will rebuild every time the themeMode signal changes.
final themeMode = context.observe<ThemeMode>(SignalId.themeMode); // [3]
return MaterialApp(
title: 'Toggle theme',
themeMode: themeMode,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: const MyHomePage(),
);
},
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
// retrieve the theme mode signal
final themeMode = context.get<Signal<ThemeMode>>(SignalId.themeMode); // [4]
return Scaffold(
appBar: AppBar(
title: const Text('Toggle theme'),
),
body: Center(
child:
// Listen to the theme mode signal rebuilding only the IconButton
SignalBuilder( // [5]
signal: themeMode,
builder: (_, mode, __) {
return IconButton(
onPressed: () { // [6]
// toggle the theme mode
if (themeMode.value == ThemeMode.light) {
themeMode.value = ThemeMode.dark;
} else {
themeMode.value = ThemeMode.light;
}
},
icon: Icon(
mode == ThemeMode.light ? Icons.dark_mode : Icons.light_mode,
),
);
},
),
),
);
}
}
在这个示例中,发生了许多事情,首先在 [1] 中,我们使用了一个枚举来存储所有 [SignalId]。您可以使用 String、int 或任何您想要的东西。只需确保使用相同的 ID 来检索信号。
然后,在 [2] 中,我们使用 Solid 小部件将 themeMode 信号提供给后代。
Solid 小部件接受一个 signals 映射
- 映射的键是信号 ID,在本例中为
SignalId.themeMode。 - 映射的值是一个返回
SignalBase的函数。您可以创建一个信号或一个派生信号。值为函数是因为信号在首次使用时才被惰性创建,如果您从未访问该信号,它就不会被创建。
在 [3] 中,我们 observe 信号的值。observe 方法监听信号值并在值更改时重建小部件。它接受一个 id,即您要使用的信号标识符。此方法只能在 build 方法中调用。
在 [4] 中,我们使用给定的 id get 信号。这不会监听信号值。您可以在 initState 和 build 方法中使用此方法。
在 [5] 中,使用 SignalBuilder 小部件,我们每次信号值更改时都会重建 IconButton。
最后在 [6] 中,我们更新信号值。
必须传递信号值的类型,否则会遇到错误,例如
createSignal<ThemeMode>和context.observe<ThemeMode>,其中 ThemeMode 是信号值的类型。context.get<Signal<ThemeMode>>,其中Signal<ThemeMode>是带其类型值的信号类型。
待办事项列表
- 添加更多单元测试
- 创建文档页面
示例
使用 flutter_solidart 的示例功能
展示所有 flutter_solidart 功能
学习 flutter_solidart 的所有功能,包括
createSignalShow小部件- 使用
signal.select()的派生信号 - 使用基本和高级用法的
Effect SignalBuilder、DualSignalBuilder和TripleSignalBuildercreateResource和ResourceBuilderSolid及其细粒度响应式