Reputation: 387
I'm trying to split firing update of widgets with Provider
.
I'm using latest Flutter
version. Within app I'm also using context.select()
, context.watch()
and context.read()
.
Now to the main goal. What exactly I'm talking about. So I have some notifier:
class ExpenseNotifier with ChangeNotifier {
List<Category> _selectedCategories = [];
Expense _currentExpense;
int _removeId;
Expense _editExpense;
}
Now, ExpenseNotifier
has a few consumers. All consumers will be updated when something will change. Except one case: when _editExpense
updated only one consumer should be updated. Thing is that the class Expense
already exists in the notifier, so all consumers connected to _currentExpense
also reacts on _editExpense
update...
I'm now using selectors. Like this:
context.select<ExpenseNotifier, Expense>((notifier) => notifier.currentExpense);
But looks like widget also reacts on _editExpense
updates for some reason...
What is the proper solution for that situation? Is it possible (without defining new type) achieve it within ExpenseNotifier
?
Probably, something like this should work:
class EditExpense {
final Expense expense;
EditExpense(this.expense);
}
So in this case wrapper class is needed. Correct me if I'm wrong
Upvotes: 0
Views: 753
Reputation: 785
I found your question interesting , so I thought it would be worth working on it . I am giving a sort of general answer but I think you will be benefited from it.
First add methods to data class which will update only the required fields , like this :
class DataClass with ChangeNotifier {
String firstString = " ";
String secondString = " ";
void updateFirst(String newString) {
firstString = newString;
notifyListeners();
}
void updateSecond(String newString) {
secondString = newString;
notifyListeners();
}
}
Now it's time to refactor , you have to make two classes(or methods) that will have their own build methods (you can also define two methods and pass the BuildContext
to them):
class StringOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("StringOne build method is called");
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
children: [
Text(context.select((DataClass value) => value.firstString)),
Container(
height: 100,
width: 100,
child: TextField(
onChanged: (text) {
context.read<DataClass>().updateFirst(text);
},
),
)
],
)
],
);
}
}
And
class StringTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("StringTwo build method is called");
return Column(
children: <Widget>[
Column(
children: [
Text(context.select((DataClass value) => value.secondString)),
Container(
height: 100,
width: 100,
child: TextField(
onChanged: (text) {
context.read<DataClass>().updateSecond(text);
},
),
),
],
)
],
);
}
}
Finally have these classes in other class which describes the UI:
class ProviderExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [StringOne(), StringTwo()],
);
}
}
you might say it will increase verbosity , actually , refactoring often makes the code more verbose but it also makes code clearer and easy to maintain. In your case it will also prevent unnecessary builds.
Console :
I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/flutter ( 1469): StringTwo build method is called
I/zygote64( 1469): Increasing code cache capacity to 1024KB
I/flutter ( 1469): StringOne build method is called
I/chatty ( 1469): uid=10140(com.example.stack_overflow) 1.ui identical 7 lines
I/flutter ( 1469): StringOne build method is called
I/flutter ( 1469): StringOne build method is called
Upvotes: 1