英文:
Flutter: how can I use service when using riverpod provider?
问题
Here's the translated content from your code:
刚开始学习关于Riverpod,我想在Provider中使用FlutterSecureStorage。
问题是我不知道我做的是否正确。
我想创建一个服务并实现一些方法来帮助我使用`FlutterSecureStorage`,然后使用Provider来使用这个服务,以便我可以从整个应用程序中访问它。
以下是代码:
storage_service.dart
```dart
class StorageService {
final _secureStorage = const FlutterSecureStorage();
AndroidOptions _getAndroidOptions() => const AndroidOptions(
encryptedSharedPreferences: true,
);
Future<void> writeSecureData(StorageItem newItem) async {
await _secureStorage.write(
key: newItem.key, value: newItem.value, aOptions: _getAndroidOptions());
}
...
Future<List<StorageItem>> readAllSecureData() async {
var allData = await _secureStorage.readAll(aOptions: _getAndroidOptions());
List<StorageItem> list =
allData.entries.map((e) => StorageItem(e.key, e.value)).toList();
return list;
}
...
}
final storageServiceProvider = Provider((ref) => StorageService());
Home Screen:
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
@override
Widget build(BuildContext context) {
final storageService = ref.watch(storageServiceProvider);
List<StorageItem> data = [];
storageService.readAllSecureData().then((value) => data = value);
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
'HomeScreen',
),
Expanded(
flex: 2,
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Column(
children: [
Text('Key: ${data[index].key}'),
Text('Value: ${data[index].value}'),
],
);
},
),
),
Expanded(
flex: 1,
child: TextButton(
onPressed: () async {
final List<StorageItem> st =
await storageService.readAllSecureData();
debugPrint('button Pressed ${st.length}');
st.map(
(e) => debugPrint('Key: ${e.key} | Value: ${e.value}'));
final StorageItem newItem = StorageItem('sup', 'test');
storageService.writeSecureData(newItem);
// context.goNamed(AppRoutingConstans.loginScreenRouteName);
},
style: TextButton.styleFrom(
backgroundColor: Colors.amber,
minimumSize: const Size(50, 50),
padding: const EdgeInsets.symmetric(horizontal: 148.0),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
child: const Text(
'Go To Login Screen',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
);
;
}
}
Please note that the code comments and variable names remain in English as they are not specified for translation.
英文:
just start learning about riverpod and I want to use FlutterSecureStorage inside provider
the problem is I don't know if what I did is right or not
I want to create a service and implement some methods to help me use FlutterSecureStorage
the use this service with Provider so I can access it from all the app
here's the code:
storage_service.dart
class StorageService {
final _secureStorage = const FlutterSecureStorage();
AndroidOptions _getAndroidOptions() => const AndroidOptions(
encryptedSharedPreferences: true,
);
Future<void> writeSecureData(StorageItem newItem) async {
await _secureStorage.write(
key: newItem.key, value: newItem.value, aOptions: _getAndroidOptions());
}
...
Future<List<StorageItem>> readAllSecureData() async {
var allData = await _secureStorage.readAll(aOptions: _getAndroidOptions());
List<StorageItem> list =
allData.entries.map((e) => StorageItem(e.key, e.value)).toList();
return list;
}
...
}
final storageServiceProvider = Provider((ref) => StorageService());
Home Screen:
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
@override
Widget build(BuildContext context) {
final storageService = ref.watch(storageServiceProvider);
List<StorageItem> data = [];
storageService.readAllSecureData().then((value) => data = value);
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
'HomeScreen',
),
Expanded(
flex: 2,
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Column(
children: [
Text('Key: ${data[index].key}'),
Text('Value: ${data[index].value}'),
],
);
},
),
),
Expanded(
flex: 1,
child: TextButton(
onPressed: () async {
final List<StorageItem> st =
await storageService.readAllSecureData();
debugPrint('button Pressed ${st.length}');
st.map(
(e) => debugPrint('Key: ${e.key} | Value: ${e.value}'));
final StorageItem newItem = StorageItem('sup', 'test');
storageService.writeSecureData(newItem);
// context.goNamed(AppRoutingConstans.loginScreenRouteName);
},
style: TextButton.styleFrom(
backgroundColor: Colors.amber,
minimumSize: const Size(50, 50),
padding: const EdgeInsets.symmetric(horizontal: 148.0),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
child: const Text(
'Go To Login Screen',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
);
;
}
}
答案1
得分: 2
你可以保持原样。但是,需要处理这一行代码:
storageService.readAllSecureData().then((value) => data = value);
有两个选择:
- 使用
FutureBuilder
或者 - 在
then
中使用setState
:
storageService.readAllSecureData().then((value) {
setState((){data = value;});
});
接下来,你需要将所有代码放在 initState
中:
@override
void initState() {
super.initState();
final storageService = ref.read(storageServiceProvider);
storageService.readAllSecureData().then((value) {
setState((){data = value;});
});
}
并且将列表的创建添加到 _HomeScreenState
类的字段中:
class _HomeScreenState extends ConsumerState<HomeScreen> {
List<StorageItem> data = [];
...
接下来,你需要考虑正确的架构和单一职责原则。你的界面应该只显示已经准备好的信息,什么都不多。它不应该知道关于存储库的任何事情,不知道如何改变已经就位的数据,也不应该有不必要的改变数据的方法。
一个类可以做的事情应该通过构造函数传递,或者(在我们的例子中,使用 Riverpod)通过 ref.watch(onlyNecessaryMethodsProvider)
。
因此,有必要将所有与存储引用的逻辑分配到一个单独的小部件辅助类中。通常称为PageController
或者PagePresenter
,其中包含与该页面的所有逻辑以及在用户交互期间将调用的方法。
更新:
现在,随着 Riverpod 2.0 的推出,你应该使用(Async)NotifierProvider
来构建你的状态。你可以直接在build
方法中使用ref.watch
将所有依赖项写入其中,提供程序将正确地重新构建。
英文:
You can leave it the way it is. However, something needs to be done about this line:
storageService.readAllSecureData().then((value) => data = value);
Two options to choose from:
- use
FutureBuilder
or - use
setState
withinthen
:
storageService.readAllSecureData().then((value) {
setState((){data = value;});
});
Next, you need to put it all in initState
:
@override
void initState() {
super.initState();
final storageService = ref.read(storageServiceProvider);
storageService.readAllSecureData().then((value) {
setState((){data = value;});
});
}
and add the creation of the list to the fields of the _HomeScreenState
class:
class _HomeScreenState extends ConsumerState<HomeScreen> {
List<StorageItem> data = [];
...
Next, you need to think about the right architecture and Single-responsibility principle. Your interface should only display the already PREPARED information and nothing more. He must not know anything about the repository, about how to change the data already in place, and not have unnecessary ways to change the data.
Everything that a class can do must be passed as a dependency in the constructor, or (in our case, Riverpod) as ref.watch(onlyNecessaryMethodsProvider)
.
Therefore, it would be necessary to allocate all the logic of reference to the storage, the conversion of existing data in a separate widget helper class. Usually it is called PageController
or PagePresenter
, which contains all the logic for working with this page and the methods that will be called during user interactions
Update:
Nowadays, with the advent of Riverpod 2.0, you should be using exactly (Async)NotifierProvider
to build your states. You can write all dependencies in it directly in the build
method using ref.watch
and the provider will rebuild correctly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论