11---
2- title : " Persistent storage architecture: Key-value data"
3- description : Save application data to a user's on-device key-value store.
2+ # title: "Persistent storage architecture: Key-value data"
3+ title : 持久存储架构:键值对数据
4+ # description: Save application data to a user's on-device key-value store.
5+ description : 以键值对形式将应用产生的数据保存到用户的设备中。
46contentTags :
57 - data
68 - shared-preferences
@@ -18,12 +20,23 @@ Most Flutter applications, no matter how small or big they are,
1820require storing data on the user’s device at some point, such as API keys,
1921user preferences or data that should be available offline.
2022
23+ 大多数 Flutter 应用程序,
24+ 无论规模大小,
25+ 往往需要在用户设备上存储数据。
26+ 例如:API 密钥、用户偏好内容,以及需要支持离线访问的数据。
27+
2128In this recipe, you will learn how to integrate persistent storage
2229for key-value data in a Flutter application
2330that uses the recommended [ Flutter architecture design] [ ] .
2431If you aren’t familiar with storing data to disk at all,
2532you can read the [ Store key-value data on disk] [ ] recipe.
2633
34+ 在本教程中,
35+ 你将学习如何遵循 [ Flutter 架构设计模式] [ Flutter architecture design ] ,
36+ 并在 Flutter 应用中实现基于键值对的数据持久化存储。
37+ 如果你尚且不熟悉如何将数据存储到磁盘上,
38+ 可以阅读 [ 将键值对数据存储到磁盘上] [ Store key-value data on disk ] 。
39+
2740Key-value stores are often used for saving simple data,
2841such as app configuration,
2942and in this recipe you’ll use it to save Dark Mode preferences.
@@ -32,11 +45,23 @@ you’ll likely want to use SQL.
3245In that case, take a look at the cookbook recipe
3346that follows this one called [ Persistent storage architecture: SQL] [ ] .
3447
48+ 键值对存储常用于存储简单的数据,
49+ 例如应用配置,
50+ 在本教程中,你将学习如何使用它来保存深色模式偏好设置。
51+ 如果你希望学习如何在设备上存储复杂的数据,
52+ 你可能需要使用 SQL。
53+ 此时,请阅读本教程之后的 [ 持久化存储架构:SQL] [ Persistent storage architecture: SQL ] 。
54+
3555## Example application: App with theme selection
3656
57+ ## 示例应用:带主题选择的应用
58+
3759The example application consists of a single screen with an app bar at the top,
3860a list of items, and a text field input at the bottom.
3961
62+ 该示例应用为单页面结构,主要包含:
63+ 顶部的 AppBar、中间的项目列表以及底部的文本输入框。
64+
4065<img src='/assets/images/docs/cookbook/architecture/todo_app_light.png'
4166class="site-mobile-screenshot" alt="ToDo application in light mode" >
4267
@@ -46,32 +71,52 @@ This setting is applied immediately and it’s stored in the device
4671using a key-value data storage service.
4772The setting is restored when the user starts the application again.
4873
74+ 在 ` AppBar ` 中,一个 ` Switch ` 允许用户在深色和浅色主题之间切换。
75+ 该设置立即生效,并通过键值对数据存储服务保存在设备上。
76+ 当用户再次启动应用时,该设置会被恢复。
77+
4978<img src='/assets/images/docs/cookbook/architecture/todo_app_dark.png'
5079class="site-mobile-screenshot" alt="ToDo application in dark mode" >
5180
5281::: note
5382The full, runnable source-code for this example is
5483available in [ ` /examples/app-architecture/todo_data_service/ ` ] [ ] .
84+
85+ 此示例完整且可运行的源代码,
86+ 可在 [ ` /examples/app-architecture/todo_data_service/ ` ] [ ] 中找到。
5587:::
5688
5789## Storing theme selection key-value data
5890
91+ ## 存储当前选择主题的键值对数据
92+
5993This functionality follows the recommended Flutter architecture design pattern,
6094with a presentation and a data layer.
6195
96+ 此功能遵循推荐的 Flutter 架构设计,
97+ 包含展示层和数据层(Data Layer)。
98+
6299- The presentation layer contains the ` ThemeSwitch ` widget
63100and the ` ThemeSwitchViewModel ` .
101+ 展示层包含 ` ThemeSwitch ` 组件和 ` ThemeSwitchViewModel ` 。
64102- The data layer contains the ` ThemeRepository `
65103and the ` SharedPreferencesService ` .
104+ 数据层包含 ` ThemeRepository ` 和 ` SharedPreferencesService ` 。
66105
67106### Theme selection presentation layer
68107
108+ ### 主题选择展示层
109+
69110The ` ThemeSwitch ` is a ` StatelessWidget ` that contains a ` Switch ` widget.
70111The state of the switch is represented
71112by the public field ` isDarkMode ` in the ` ThemeSwitchViewModel ` .
72113When the user taps the switch,
73114the code executes the command ` toggle ` in the view model.
74115
116+ ` ThemeSwitch ` 是一个 ` StatelessWidget ` ,它包含一个 ` Switch ` 组件。
117+ 开关的状态由 ` ThemeSwitchViewModel ` 中的公共字段 ` isDarkMode ` 表示。
118+ 当用户点击开关时,代码执行视图模型中的命令 ` toggle ` 。
119+
75120<? code-excerpt "lib/ui/theme_config/widgets/theme_switch.dart (ThemeSwitch)"?>
76121``` dart
77122class ThemeSwitch extends StatelessWidget {
@@ -109,24 +154,44 @@ as described in the MVVM pattern.
109154This view model contains the state of the ` ThemeSwitch ` widget,
110155represented by the boolean variable ` _isDarkMode ` .
111156
157+ ` ThemeSwitchViewModel ` 实现了 MVVM 模式中描述的视图模型。
158+ 该视图模型包含 ` ThemeSwitch ` 组件的状态,
159+ 由布尔变量 ` _isDarkMode ` 表示。
160+
112161The view model uses the ` ThemeRepository `
113162to store and load the dark mode setting.
114163
164+ 视图模型使用 ` ThemeRepository ` 存储和加载深色模式的设置。
165+
115166It contains two different command actions:
116167` load ` , which loads the dark mode setting from the repository,
117168and ` toggle ` , which switches the state between dark mode and light mode.
118169It exposes the state through the ` isDarkMode ` getter.
119170
171+ 该模型包含两个不同的命令操作:
172+ ` load ` ,从存储库中加载深色模式设置,
173+ ` toggle ` ,在深色模式和浅色模式之间切换状态。
174+ 它通过 ` isDarkMode ` getter 暴露状态。
175+
120176The ` _load ` method implements the ` load ` command.
121177This method calls ` ThemeRepository.isDarkMode `
122178to obtain the stored setting and calls ` notifyListeners() ` to refresh the UI.
123179
180+ ` _load ` 方法实现了 ` load ` 命令。
181+ 该方法调用 ` ThemeRepository.isDarkMode ` 获取存储的设置,
182+ 然后调用 ` notifyListeners() ` 刷新 UI。
183+
124184The ` _toggle ` method implements the ` toggle ` command.
125185This method calls ` ThemeRepository.setDarkMode `
126186to store the new dark mode setting.
127187As well, it changes the local state of ` _isDarkMode `
128188then calls ` notifyListeners() ` to update the UI.
129189
190+ ` _toggle ` 方法实现了 ` toggle ` 命令。
191+ 该方法调用 ` ThemeRepository.setDarkMode ` 存储新的深色模式设置。
192+ 与此同时,它修改本地状态 ` _isDarkMode ` ,
193+ 然后调用 ` notifyListeners() ` 更新 UI。
194+
130195<? code-excerpt "lib/ui/theme_config/viewmodel/theme_switch_viewmodel.dart (ThemeSwitchViewModel)"?>
131196``` dart
132197class ThemeSwitchViewModel extends ChangeNotifier {
@@ -168,27 +233,46 @@ class ThemeSwitchViewModel extends ChangeNotifier {
168233
169234### Theme selection data layer
170235
236+ ### 主题选择数据层
237+
171238Following the architecture guidelines,
172239the data layer is split into two parts:
173240the ` ThemeRepository ` and the ` SharedPreferencesService ` .
174241
242+ 根据架构指南,
243+ 数据层被分为两部分:
244+ ` ThemeRepository ` 和 ` SharedPreferencesService ` 。
245+
175246The ` ThemeRepository ` is the single source of truth
176247for all the theming configuration settings,
177248and handles any possible errors coming from the service layer.
178249
250+ ` ThemeRepository ` 是所有主题配置设置的唯一数据来源,
251+ 并处理来自服务层可能出现的任何错误。
252+
179253In this example,
180254the ` ThemeRepository ` also exposes the dark mode setting
181255through an observable ` Stream ` .
182256This allows other parts of the application
183257to subscribe to changes in the dark mode setting.
184258
259+ 在本示例中,
260+ ` ThemeRepository ` 还通过可观察的 ` Stream ` 暴露深色模式设置。
261+ 这允许应用程序的其他部分订阅深色模式设置的变化。
262+
185263The ` ThemeRepository ` depends on ` SharedPreferencesService ` .
186264The repository obtains the stored value from the service,
187265and stores it when it changes.
188266
267+ ` ThemeRepository ` 依赖于 ` SharedPreferencesService ` 。
268+ 该主题仓库从服务中获取存储的值,
269+ 并存储其改变后的值。
270+
189271The ` setDarkMode() ` method passes the new value to the ` StreamController ` ,
190272so that any component listening to the ` observeDarkMode ` stream
191273
274+ ` setDarkMode() ` 方法将新值传递给 ` StreamController ` ,
275+ 以便让任何监听 ` observeDarkMode ` 流的组件观察到数据变化。
192276
193277<? code-excerpt "lib/data/repositories/theme_repository.dart (ThemeRepository)"?>
194278``` dart
@@ -232,9 +316,15 @@ and calls to the `setBool()` and `getBool()` methods
232316to store the dark mode setting,
233317hiding this third-party dependency from the rest of the application
234318
319+ ` SharedPreferencesService ` 内部集成了 ` SharedPreferences ` 插件的功能,
320+ 并调用 ` setBool() ` 和 ` getBool() ` 方法来存储深色模式设置,
321+ 从而对应用隐藏第三方依赖项。
322+
235323::: note
236324A third-party dependency is a way to refer to packages and plugins
237325developed by other programmers outside of your organization.
326+
327+ 第三方依赖是指引用组织外程序员开发的 packages 和插件的一种方式。
238328:::
239329
240330<? code-excerpt "lib/data/services/shared_preferences_service.dart (SharedPreferencesService)"?>
@@ -256,11 +346,17 @@ class SharedPreferencesService {
256346
257347## Putting it all together
258348
349+ ## 整合业务
350+
259351In this example,
260352the ` ThemeRepository ` and ` SharedPreferencesService ` are created
261353in the ` main() ` method
262354and passed to the ` MainApp ` as constructor argument dependency.
263355
356+ 在本示例中,
357+ ` ThemeRepository ` 和 ` SharedPreferencesService ` 是在 ` main() ` 方法中创建的,
358+ 并作为构造函数参数依赖项传递给 ` MainApp ` 。
359+
264360<? code-excerpt "lib/main.dart (MainTheme)"?>
265361``` dart
266362void main() {
@@ -278,6 +374,10 @@ Then, when the `ThemeSwitch` is created,
278374also create ` ThemeSwitchViewModel `
279375and pass the ` ThemeRepository ` as dependency.
280376
377+ 然后,当创建 ` ThemeSwitch ` 时,
378+ 也创建 ` ThemeSwitchViewModel `
379+ 并将 ` ThemeRepository ` 作为依赖项注入。
380+
281381<? code-excerpt "lib/main.dart (AddThemeSwitch)"?>
282382``` dart
283383ThemeSwitch(
@@ -289,6 +389,9 @@ The example application also includes the `MainAppViewModel` class,
289389which listens to changes in the ` ThemeRepository `
290390and exposes the dark mode setting to the ` MaterialApp ` widget.
291391
392+ 该示例程序还包括 ` MainAppViewModel ` 类,
393+ 该类监听 ` ThemeRepository ` 的变化并向 ` MaterialApp ` 小部件暴露深色模式设置。
394+
292395<? code-excerpt "lib/main_app_viewmodel.dart (MainAppViewModel)"?>
293396``` dart
294397class MainAppViewModel extends ChangeNotifier {
0 commit comments