允许提供者之间的区别
Allow distinction between providers
我正在构建一个带有 flutter 和提供者模式的应用程序。我有一个特定的 ViewModel,由 Provider.of<AddressBookModel>(context)
提供。
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AddressBookViewModel>(
builder:(_) => AddressBookViewModel(),
child: Scaffold(
body: _getBody(context);
}
Widget _getBody(BuildContext context) {
AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);
// AddressBookViewModel holds a list of contact objects
// (id, name, street, starred etc.)
List<Contact> contacts = vm.contacts;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) => ListTile(
title: Text(contacts[index].name),
trailing: contacts[index].starred
? Icon(Icons.star))
: null,
/**
* Changing one object rebuilds and redraws the whole list
*/
onLongPress: () => vm.toggleStarred(contacts[index]);
));
}
}
以及各自的ViewModel
class AddressBookViewModel with ChangeNotifier {
final List<Contact> contacts;
AddressBookViewModel({this.contacts = []});
void toggleStarred(Contact contact) {
int index = contacts.indexOf(contact);
// the contact object is immutable
contacts[index] = contact.copy(starred: !contact.starred);
notifyListeners();
}
}
我面临的问题是,一旦我用 toggleStarred()
更改列表中的一个联系人对象,
提供商正在重建并重新绘制整个列表。这在我看来是没有必要的,因为只是
一个条目需要重建。有没有办法让提供者只负责
对于一个列表项?或者有什么其他方法可以解决这个问题?
注意:完整代码在文末
第 1 步:使用 ChangeNotifier class
扩展 Contact class
class Contact with ChangeNotifier { }
第 2 步:删除最终表单 已加星标 字段
bool starred;
第 3 步:将 toggleStarred 方法从 AddressBookViewModel class 移动到 Contact class
void toggleStarred() {
starred = !starred;
notifyListeners();
}
步骤 [1,2,3] 代码更改审查:
class Contact with ChangeNotifier {
final String name;
bool starred;
Contact(this.name, this.starred);
void toggleStarred() {
starred = !starred;
notifyListeners();
}
}
第 4 步:将 ListTile 移动到名为 ContactView 的 StatelessWidget
class ContactView extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile();
}
}
第 5 步:更改 ListView itemBuilder 方法
(context, index) {
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
第 6 步:在新的 StatelessWidget ContactView 上使用 Provider 获取联系人
final contact = Provider.of<Contact>(context);
第 7 步:更改 onLongPress 以使用新的 toggleStarred
onLongPress: () => contact.toggleStarred(),
步骤 [4,6,7] 代码更改审查:
class ContactView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final contact = Provider.of<Contact>(context);
print("building ListTile item with contact " + contact.name);
return ListTile(
title: Text(contact.name),
trailing: contact.starred ? Icon(Icons.star) : null,
onLongPress: () => contact.toggleStarred(),
);
}
}
步骤 [5] 代码更改审查:
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
print("building ListView item with index $index");
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
},
);
完整代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider<AddressBookViewModel>(
builder: (context) => AddressBookViewModel(),
child: HomeScreen(),
),
);
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AddressBookViewModel>(
builder: (context) => AddressBookViewModel(),
child: MaterialApp(
home: Scaffold(
body: _getBody(context),
),
),
);
}
Widget _getBody(BuildContext context) {
AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);
final contacts = vm.contacts;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
print("building ListView item with index $index");
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
},
);
}
}
// product_item.dart
class ContactView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final contact = Provider.of<Contact>(context);
print("building ListTile item with contact " + contact.name);
return ListTile(
title: Text(contact.name),
trailing: contact.starred ? Icon(Icons.star) : null,
onLongPress: () => contact.toggleStarred(),
);
}
}
class AddressBookViewModel with ChangeNotifier {
final contacts = [
Contact("Contact A", false),
Contact("Contact B", false),
Contact("Contact C", false),
Contact("Contact D", false),
];
void addcontacts(Contact contact) {
contacts.add(contact);
notifyListeners();
}
}
class Contact with ChangeNotifier {
final String name;
bool starred;
Contact(this.name, this.starred);
void toggleStarred() {
starred = !starred;
notifyListeners();
}
}
参考:
使用列表时,您需要为列表中的每个项目创建一个 "provider" 并将列表项目提取到一个常量中 – 特别是如果与您的项目关联的数据是不可变的。
而不是:
final contactController = Provider.of<ContactController>(context);
return ListView.builder(
itemCount: contactController.contacts.length,
builder: (_, index) {
reurn Text(contactController.contacts[index].name);
}
)
更喜欢:
final contactController = Provider.of<ContactController>(context);
return ListView.builder(
itemCount: contactController.contacts.length,
builder: (_, index) {
reurn Provider.value(
value: contactController.contacts[index],
child: const ContactItem(),
);
}
)
其中 ContactItem
是一个 StatelessWidget
,通常看起来像这样:
class ContactItem extends StatelessWidget {
const ContactItem({Key key}): super(key: key);
@override
Widget build(BuildContext context) {
return Text(Provider.of<Contact>(context).name);
}
}
我正在构建一个带有 flutter 和提供者模式的应用程序。我有一个特定的 ViewModel,由 Provider.of<AddressBookModel>(context)
提供。
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AddressBookViewModel>(
builder:(_) => AddressBookViewModel(),
child: Scaffold(
body: _getBody(context);
}
Widget _getBody(BuildContext context) {
AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);
// AddressBookViewModel holds a list of contact objects
// (id, name, street, starred etc.)
List<Contact> contacts = vm.contacts;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) => ListTile(
title: Text(contacts[index].name),
trailing: contacts[index].starred
? Icon(Icons.star))
: null,
/**
* Changing one object rebuilds and redraws the whole list
*/
onLongPress: () => vm.toggleStarred(contacts[index]);
));
}
}
以及各自的ViewModel
class AddressBookViewModel with ChangeNotifier {
final List<Contact> contacts;
AddressBookViewModel({this.contacts = []});
void toggleStarred(Contact contact) {
int index = contacts.indexOf(contact);
// the contact object is immutable
contacts[index] = contact.copy(starred: !contact.starred);
notifyListeners();
}
}
我面临的问题是,一旦我用 toggleStarred()
更改列表中的一个联系人对象,
提供商正在重建并重新绘制整个列表。这在我看来是没有必要的,因为只是
一个条目需要重建。有没有办法让提供者只负责
对于一个列表项?或者有什么其他方法可以解决这个问题?
注意:完整代码在文末
第 1 步:使用 ChangeNotifier class
扩展 Contact classclass Contact with ChangeNotifier { }
第 2 步:删除最终表单 已加星标 字段
bool starred;
第 3 步:将 toggleStarred 方法从 AddressBookViewModel class 移动到 Contact class
void toggleStarred() {
starred = !starred;
notifyListeners();
}
步骤 [1,2,3] 代码更改审查:
class Contact with ChangeNotifier {
final String name;
bool starred;
Contact(this.name, this.starred);
void toggleStarred() {
starred = !starred;
notifyListeners();
}
}
第 4 步:将 ListTile 移动到名为 ContactView 的 StatelessWidget
class ContactView extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile();
}
}
第 5 步:更改 ListView itemBuilder 方法
(context, index) {
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
第 6 步:在新的 StatelessWidget ContactView 上使用 Provider 获取联系人
final contact = Provider.of<Contact>(context);
第 7 步:更改 onLongPress 以使用新的 toggleStarred
onLongPress: () => contact.toggleStarred(),
步骤 [4,6,7] 代码更改审查:
class ContactView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final contact = Provider.of<Contact>(context);
print("building ListTile item with contact " + contact.name);
return ListTile(
title: Text(contact.name),
trailing: contact.starred ? Icon(Icons.star) : null,
onLongPress: () => contact.toggleStarred(),
);
}
}
步骤 [5] 代码更改审查:
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
print("building ListView item with index $index");
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
},
);
完整代码
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider<AddressBookViewModel>(
builder: (context) => AddressBookViewModel(),
child: HomeScreen(),
),
);
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AddressBookViewModel>(
builder: (context) => AddressBookViewModel(),
child: MaterialApp(
home: Scaffold(
body: _getBody(context),
),
),
);
}
Widget _getBody(BuildContext context) {
AddressBookViewModel vm = Provider.of<AddressBookViewModel>(context);
final contacts = vm.contacts;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
print("building ListView item with index $index");
return ChangeNotifierProvider.value(
value: contacts[index],
child: ContactView(),
);
},
);
}
}
// product_item.dart
class ContactView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final contact = Provider.of<Contact>(context);
print("building ListTile item with contact " + contact.name);
return ListTile(
title: Text(contact.name),
trailing: contact.starred ? Icon(Icons.star) : null,
onLongPress: () => contact.toggleStarred(),
);
}
}
class AddressBookViewModel with ChangeNotifier {
final contacts = [
Contact("Contact A", false),
Contact("Contact B", false),
Contact("Contact C", false),
Contact("Contact D", false),
];
void addcontacts(Contact contact) {
contacts.add(contact);
notifyListeners();
}
}
class Contact with ChangeNotifier {
final String name;
bool starred;
Contact(this.name, this.starred);
void toggleStarred() {
starred = !starred;
notifyListeners();
}
}
参考:
使用列表时,您需要为列表中的每个项目创建一个 "provider" 并将列表项目提取到一个常量中 – 特别是如果与您的项目关联的数据是不可变的。
而不是:
final contactController = Provider.of<ContactController>(context);
return ListView.builder(
itemCount: contactController.contacts.length,
builder: (_, index) {
reurn Text(contactController.contacts[index].name);
}
)
更喜欢:
final contactController = Provider.of<ContactController>(context);
return ListView.builder(
itemCount: contactController.contacts.length,
builder: (_, index) {
reurn Provider.value(
value: contactController.contacts[index],
child: const ContactItem(),
);
}
)
其中 ContactItem
是一个 StatelessWidget
,通常看起来像这样:
class ContactItem extends StatelessWidget {
const ContactItem({Key key}): super(key: key);
@override
Widget build(BuildContext context) {
return Text(Provider.of<Contact>(context).name);
}
}