Flutter - Providers 和 Future 调用,如何共享同一个实例?
Flutter - Providers and Future calls, how to share the same instance?
我正在学习 Flutter,有些事情我无法理解。
我用一个包(infine_scroll_pagination
)实现了无限滚动分页,
它工作正常,但此 Package
获取的数据来自 Future
调用,该调用从 WEB
获取数据并在我的 Provider Class
.[= 中解析它34=]
我的问题是,Infinite Scroll widget
加载的 data
无法在其 state
的其他任何地方访问。
例子:
让我们以一次加载 10 个联系人的 contact list
为例:
class ContactsBody extends StatefulWidget {
@override
_ContactsBodyState createState() => _ContactsBodyState();
}
class _ContactsBodyState extends State<ContactsBody> {
static const _pageSize = 10;
final PagingController<int, Contact> pagingController =
PagingController(firstPageKey: 0);
@override
void initState() {
super.initState();
pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
Future<void> _fetchPage(int pageKey) async {
try {
final newItems = await ContactsService().fetchContactsPaged(pageKey, _pageSize);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
pagingController.appendLastPage(newItems.contacts);
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(newItems.contacts, nextPageKey);
}
} catch (error) {
pagingController.error = error;
}
}
@override
Widget build(BuildContext context) {
return ContactsList(pagingController);
}
@override
void dispose() {
pagingController.dispose();
super.dispose();
}
基本上这个 Infinite Scroll 包会获取我的联系人,一次 10 个,这里是我的 ContactsService
电话:
Future<Contacts> fetchContactsPaged(int pageKey, int pageSize) async {
final response = await http.get(.....);
if (response.statusCode == 200) {
return Contacts.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load contacts');
}
}
最后,正如您在上面看到的,它使用其工厂方法“fromJson()
”和 returns [=25] 初始化我的 Provider class
(联系人) =].
现在我的 Provider class
:
class Contacts extends ChangeNotifier {
List<Contact> _contacts = <Contact>[];
Contacts();
factory Contacts.fromJson(final Map<String, dynamic> json) {
final Contacts contacts = Contacts();
if (json['data'] != null) {
json['data'].forEach((contact) {
contacts.add(Contact.fromJson(contact));
});
}
return contacts;
}
void add(final Contact contact) {
this._contacts.add(contact);
this.notifyListeners();
}
我在这里遇到的问题是,当加载 Inifinite Scroll listView
时,例如我更改了单个联系人的 state
(例如可以将联系人设置为收藏夹),
如何访问 Contacts() class
的 SAME instanc
e,FUTURE
调用已初始化,以便我可以访问 current state
中的数据class?
当然,如果我要 POST
我的更改到 API,并在我需要的地方重新获取新值,我会得到我的数据的更新状态,但我想了解如何在此处访问同一实例并使当前数据在应用程序内随处可用
编辑:我删除了原始答案,以便更好地了解 OP 想要实现的目标。
我在 GitHub 上做了一个回购,试图向您展示您想要实现的目标:https://github.com/Kobatsu/Whosebug_66578191
您的代码中有一些令人困惑的地方:
- 何时创建 objects(ContactsService、联系人)
的实例
- 提供商使用情况
- (正在访问 pagingController 列表?)
- 解析一个JSON/使用工厂方法
当您更新列表(通过向下滚动)时,黄色容器会更新为联系人数量和收藏夹数量。
如果您单击某个联系人,它就会成为收藏夹,黄色容器也会更新。
我评论了存储库以向您解释每个部分。
注意:您代码中的联系人 class 在我的代码中变成了 ContactProvider。
ContactsService class 进行 API 调用:
class ContactsService {
static Future<List<Contact>> fetchContactsPaged(
int pageKey, int pageSize) async {
// Here, you should get your data from your API
// final response = await http.get(.....);
// if (response.statusCode == 200) {
// return Contacts.fromJson(jsonDecode(response.body));
// } else {
// throw Exception('Failed to load contacts');
// }
// I didn't do the backend part, so here is an example
// with what I understand you get from your API:
var responseBody =
"{\"data\":[{\"name\":\"John\", \"isFavorite\":false},{\"name\":\"Rose\", \"isFavorite\":false}]}";
Map<String, dynamic> decoded = json.decode(responseBody);
List<dynamic> contactsDynamic = decoded["data"];
List<Contact> listOfContacts =
contactsDynamic.map((c) => Contact.fromJson(c)).toList();
// you can return listOfContacts, for this example, I will add
// more Contacts for the Pagination plugin since my json only has 2 contacts
for (int i = pageKey + listOfContacts.length; i < pageKey + pageSize; i++) {
listOfContacts.add(Contact(name: "Name $i"));
}
return listOfContacts;
}
}
提供商的使用情况:
Consumer<ContactProvider>(
builder: (_, foo, __) => Container(
child: Text(
"${foo.contacts.length} contacts - ${foo.contacts.where((c) => c.isFavorite).length} favorites"),
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
color: Colors.amber,
)),
Expanded(child: ContactsBody())
]),
)
ContactsBody class 中的获取页面方法,我们将联系人添加到我们的 ContactProvider 中:
Future<void> _fetchPage(int pageKey) async {
try {
// Note : no need to make a ContactsService, this can be a static method if you only need what's done in the fetchContactsPaged method
final newItems =
await ContactsService.fetchContactsPaged(pageKey, _pageSize);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
_pagingController.appendLastPage(newItems);
} else {
final nextPageKey = pageKey + newItems.length;
_pagingController.appendPage(newItems, nextPageKey);
}
// Important : we add the contacts to our provider so we can get
// them in other parts of our app
context.read<ContactProvider>().addContacts(newItems);
} catch (error) {
print(error);
_pagingController.error = error;
}
}
ContactItem 小部件,我们在其中更新最喜欢的状态并通知听众:
class ContactItem extends StatefulWidget {
final Contact contact;
ContactItem({this.contact});
@override
_ContactItemState createState() => _ContactItemState();
}
class _ContactItemState extends State<ContactItem> {
@override
Widget build(BuildContext context) {
return InkWell(
child: Padding(child: Row(children: [
Expanded(child: Text(widget.contact.name)),
if (widget.contact.isFavorite) Icon(Icons.favorite)
]), padding: EdgeInsets.symmetric(vertical: 8, horizontal: 10),),
onTap: () {
// the below code updates the item
// BUT others parts of our app won't get updated because
// we are not notifying the listeners of our ContactProvider !
setState(() {
widget.contact.isFavorite = !widget.contact.isFavorite;
});
// To update other parts, we need to use the provider
context.read<ContactProvider>().notifyContactUpdated(widget.contact);
});
}
}
以及 ContactProvider :
class ContactProvider extends ChangeNotifier {
final List<Contact> _contacts = [];
List<Contact> get contacts => _contacts;
void addContacts(List<Contact> newContacts) {
_contacts.addAll(newContacts);
notifyListeners();
}
void notifyContactUpdated(Contact contact) {
// You might want to update the contact in your database,
// send it to your backend, etc...
// Here we don't have these so we just notify our listeners :
notifyListeners();
}
}
我正在学习 Flutter,有些事情我无法理解。
我用一个包(infine_scroll_pagination
)实现了无限滚动分页,
它工作正常,但此 Package
获取的数据来自 Future
调用,该调用从 WEB
获取数据并在我的 Provider Class
.[= 中解析它34=]
我的问题是,Infinite Scroll widget
加载的 data
无法在其 state
的其他任何地方访问。
例子:
让我们以一次加载 10 个联系人的 contact list
为例:
class ContactsBody extends StatefulWidget {
@override
_ContactsBodyState createState() => _ContactsBodyState();
}
class _ContactsBodyState extends State<ContactsBody> {
static const _pageSize = 10;
final PagingController<int, Contact> pagingController =
PagingController(firstPageKey: 0);
@override
void initState() {
super.initState();
pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
Future<void> _fetchPage(int pageKey) async {
try {
final newItems = await ContactsService().fetchContactsPaged(pageKey, _pageSize);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
pagingController.appendLastPage(newItems.contacts);
} else {
final nextPageKey = pageKey + 1;
pagingController.appendPage(newItems.contacts, nextPageKey);
}
} catch (error) {
pagingController.error = error;
}
}
@override
Widget build(BuildContext context) {
return ContactsList(pagingController);
}
@override
void dispose() {
pagingController.dispose();
super.dispose();
}
基本上这个 Infinite Scroll 包会获取我的联系人,一次 10 个,这里是我的 ContactsService
电话:
Future<Contacts> fetchContactsPaged(int pageKey, int pageSize) async {
final response = await http.get(.....);
if (response.statusCode == 200) {
return Contacts.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load contacts');
}
}
最后,正如您在上面看到的,它使用其工厂方法“fromJson()
”和 returns [=25] 初始化我的 Provider class
(联系人) =].
现在我的 Provider class
:
class Contacts extends ChangeNotifier {
List<Contact> _contacts = <Contact>[];
Contacts();
factory Contacts.fromJson(final Map<String, dynamic> json) {
final Contacts contacts = Contacts();
if (json['data'] != null) {
json['data'].forEach((contact) {
contacts.add(Contact.fromJson(contact));
});
}
return contacts;
}
void add(final Contact contact) {
this._contacts.add(contact);
this.notifyListeners();
}
我在这里遇到的问题是,当加载 Inifinite Scroll listView
时,例如我更改了单个联系人的 state
(例如可以将联系人设置为收藏夹),
如何访问 Contacts() class
的 SAME instanc
e,FUTURE
调用已初始化,以便我可以访问 current state
中的数据class?
当然,如果我要 POST
我的更改到 API,并在我需要的地方重新获取新值,我会得到我的数据的更新状态,但我想了解如何在此处访问同一实例并使当前数据在应用程序内随处可用
编辑:我删除了原始答案,以便更好地了解 OP 想要实现的目标。
我在 GitHub 上做了一个回购,试图向您展示您想要实现的目标:https://github.com/Kobatsu/Whosebug_66578191
您的代码中有一些令人困惑的地方:
- 何时创建 objects(ContactsService、联系人) 的实例
- 提供商使用情况
- (正在访问 pagingController 列表?)
- 解析一个JSON/使用工厂方法
当您更新列表(通过向下滚动)时,黄色容器会更新为联系人数量和收藏夹数量。 如果您单击某个联系人,它就会成为收藏夹,黄色容器也会更新。
我评论了存储库以向您解释每个部分。
注意:您代码中的联系人 class 在我的代码中变成了 ContactProvider。
ContactsService class 进行 API 调用:
class ContactsService {
static Future<List<Contact>> fetchContactsPaged(
int pageKey, int pageSize) async {
// Here, you should get your data from your API
// final response = await http.get(.....);
// if (response.statusCode == 200) {
// return Contacts.fromJson(jsonDecode(response.body));
// } else {
// throw Exception('Failed to load contacts');
// }
// I didn't do the backend part, so here is an example
// with what I understand you get from your API:
var responseBody =
"{\"data\":[{\"name\":\"John\", \"isFavorite\":false},{\"name\":\"Rose\", \"isFavorite\":false}]}";
Map<String, dynamic> decoded = json.decode(responseBody);
List<dynamic> contactsDynamic = decoded["data"];
List<Contact> listOfContacts =
contactsDynamic.map((c) => Contact.fromJson(c)).toList();
// you can return listOfContacts, for this example, I will add
// more Contacts for the Pagination plugin since my json only has 2 contacts
for (int i = pageKey + listOfContacts.length; i < pageKey + pageSize; i++) {
listOfContacts.add(Contact(name: "Name $i"));
}
return listOfContacts;
}
}
提供商的使用情况:
Consumer<ContactProvider>(
builder: (_, foo, __) => Container(
child: Text(
"${foo.contacts.length} contacts - ${foo.contacts.where((c) => c.isFavorite).length} favorites"),
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
color: Colors.amber,
)),
Expanded(child: ContactsBody())
]),
)
ContactsBody class 中的获取页面方法,我们将联系人添加到我们的 ContactProvider 中:
Future<void> _fetchPage(int pageKey) async {
try {
// Note : no need to make a ContactsService, this can be a static method if you only need what's done in the fetchContactsPaged method
final newItems =
await ContactsService.fetchContactsPaged(pageKey, _pageSize);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
_pagingController.appendLastPage(newItems);
} else {
final nextPageKey = pageKey + newItems.length;
_pagingController.appendPage(newItems, nextPageKey);
}
// Important : we add the contacts to our provider so we can get
// them in other parts of our app
context.read<ContactProvider>().addContacts(newItems);
} catch (error) {
print(error);
_pagingController.error = error;
}
}
ContactItem 小部件,我们在其中更新最喜欢的状态并通知听众:
class ContactItem extends StatefulWidget {
final Contact contact;
ContactItem({this.contact});
@override
_ContactItemState createState() => _ContactItemState();
}
class _ContactItemState extends State<ContactItem> {
@override
Widget build(BuildContext context) {
return InkWell(
child: Padding(child: Row(children: [
Expanded(child: Text(widget.contact.name)),
if (widget.contact.isFavorite) Icon(Icons.favorite)
]), padding: EdgeInsets.symmetric(vertical: 8, horizontal: 10),),
onTap: () {
// the below code updates the item
// BUT others parts of our app won't get updated because
// we are not notifying the listeners of our ContactProvider !
setState(() {
widget.contact.isFavorite = !widget.contact.isFavorite;
});
// To update other parts, we need to use the provider
context.read<ContactProvider>().notifyContactUpdated(widget.contact);
});
}
}
以及 ContactProvider :
class ContactProvider extends ChangeNotifier {
final List<Contact> _contacts = [];
List<Contact> get contacts => _contacts;
void addContacts(List<Contact> newContacts) {
_contacts.addAll(newContacts);
notifyListeners();
}
void notifyContactUpdated(Contact contact) {
// You might want to update the contact in your database,
// send it to your backend, etc...
// Here we don't have these so we just notify our listeners :
notifyListeners();
}
}