Flutter:在使用Riverpod provider时,我如何使用服务?

huangapple go评论57阅读模式
英文:

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() =&gt; const AndroidOptions(
encryptedSharedPreferences: true,
);
Future&lt;void&gt; writeSecureData(StorageItem newItem) async {
await _secureStorage.write(
key: newItem.key, value: newItem.value, aOptions: _getAndroidOptions());
}
...
Future&lt;List&lt;StorageItem&gt;&gt; readAllSecureData() async {
var allData = await _secureStorage.readAll(aOptions: _getAndroidOptions());
List&lt;StorageItem&gt; list =
allData.entries.map((e) =&gt; StorageItem(e.key, e.value)).toList();
return list;
}
...
}
final storageServiceProvider = Provider((ref) =&gt; StorageService());

Home Screen:

class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState&lt;ConsumerStatefulWidget&gt; createState() =&gt; _HomeScreenState();
}
class _HomeScreenState extends ConsumerState&lt;HomeScreen&gt; {
@override
Widget build(BuildContext context) {
final storageService = ref.watch(storageServiceProvider);
List&lt;StorageItem&gt; data = [];
storageService.readAllSecureData().then((value) =&gt; data = value);
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
&#39;HomeScreen&#39;,
),
Expanded(
flex: 2,
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Column(
children: [
Text(&#39;Key: ${data[index].key}&#39;),
Text(&#39;Value: ${data[index].value}&#39;),
],
);
},
),
),
Expanded(
flex: 1,
child: TextButton(
onPressed: () async {
final List&lt;StorageItem&gt; st =
await storageService.readAllSecureData();
debugPrint(&#39;button Pressed ${st.length}&#39;);
st.map(
(e) =&gt; debugPrint(&#39;Key: ${e.key} | Value: ${e.value}&#39;));
final StorageItem newItem = StorageItem(&#39;sup&#39;, &#39;test&#39;);
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(
&#39;Go To Login Screen&#39;,
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
);
;
}
}

答案1

得分: 2

你可以保持原样。但是,需要处理这一行代码:

storageService.readAllSecureData().then((value) =&gt; 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&lt;HomeScreen&gt; {
  List&lt;StorageItem&gt; 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) =&gt; data = value);

Two options to choose from:

  • use FutureBuilder or
  • use setState within then:
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&lt;HomeScreen&gt; {
  List&lt;StorageItem&gt; 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.

huangapple
  • 本文由 发表于 2023年5月18日 01:21:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76274682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定