Reputation: 1401
I have the following FiltersScreen which contains 2 filters and a save button. When a user toggles the filter and clicks on save button, I would like to pass these values to the CategoryMealsScreen. How can I pass these values? There is no navigation between these two screens so I can't use push or pushNamed route.
Here are the two classes:
class FiltersScreen extends StatefulWidget {
@override
_FilterScreenState createState() => _FilterScreenState();
}
class _FilterScreenState extends State<FiltersScreen> {
var _glutenFree = false;
var _lactoseFree = false;
Widget _buildSwitchListTile(String title, String subTitle, bool currentValue, Function updateValue){
return SwitchListTile(title:Text(title),
value:currentValue,
subtitle: Text(subTitle),
onChanged: updateValue
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Filter'),
actions:<Widget>[
IconButton(icon:Icon(Icons.save), onPressed: () { },),
],
),
drawer: MainDrawer(),
body: Column(children:<Widget>[
Container(
padding: EdgeInsets.all(20),
child:Text('Adjust your meal selection',
style:Theme.of(context).textTheme.title), ),
Expanded(
child:ListView(
children:<Widget>[
_buildSwitchListTile('Gluten-free',
'Only include gluten free meals',
_glutenFree,
(newValue){setState((){
_glutenFree = newValue;
},);
}),
_buildSwitchListTile('Lactose',
'Only include lactose free meals',
_lactoseFree,
(newValue){setState((){
_lactoseFree = newValue;
},);
})]
)
)
]));
}
}
Here's the class which is supposed to receive the values:
class CategoryMealsScreen extends StatefulWidget {
final String categoryId;
final String categoryTitle;
CategoryMealsScreen(this.categoryId, this.categoryTitle);
@override
_CategoryMealsScreenState createState() => _CategoryMealsScreenState();
}
class _CategoryMealsScreenState extends State<CategoryMealsScreen> {
@override
Widget build(BuildContext context) {
final categoryMeals = DUMMY_MEALS.where((meal) {
return meal.categories.contains(widget.categoryId);
}).toList();
return Scaffold(
appBar: AppBar(title: Text(widget.categoryTitle)),
body: ListView.builder(
itemBuilder: (ctx, index) {
return MealItem(
id:categoryMeals[index].id,
title: categoryMeals[index].title,
imageUrl: categoryMeals[index].imageUrl,
duration: categoryMeals[index].duration,
affordability: categoryMeals[index].affordability,
complexity: categoryMeals[index].complexity,
);
},
itemCount: categoryMeals.length,
),
);
}
}
EDITED
In the CategoryMealsScreen
, how can I use both _glutenFree
and _lactoseFree
values?
Here's my updated code:
CategoryMealsScreen
class CategoryMealsScreen extends StatefulWidget {
final String categoryId;
final String categoryTitle;
CategoryMealsScreen(this.categoryId, this.categoryTitle);
@override
_CategoryMealsScreenState createState() => _CategoryMealsScreenState();
}
class _CategoryMealsScreenState extends State<CategoryMealsScreen> {
@override
Widget build(BuildContext context) {
ValueListenable<bool> _glutenFree;
ValueListenable<bool> _lactoseFree;
return ValueListenableBuilder(
valueListenable: _glutenFree,
builder: (context, value, _) {
final categoryMeals = DUMMY_MEALS.where((meal) {
if (_glutenFree.value && !meal.isGlutenFree) {
return false;
}
if (_lactoseFree.value && !meal.isLactoseFree) {
return false;
}
return true;
}).toList();
//Every independent screen must have a scaffold
return Scaffold(
appBar: AppBar(title: Text(widget.categoryTitle)),
body: ListView.builder(
itemBuilder: (ctx, index) {
return MealItem(
id: categoryMeals[index].id,
title: categoryMeals[index].title,
imageUrl: categoryMeals[index].imageUrl,
duration: categoryMeals[index].duration,
affordability: categoryMeals[index].affordability,
complexity: categoryMeals[index].complexity,
);
},
itemCount: categoryMeals.length,
),
);
});
}
}
FiltersScreen
class FiltersScreen extends StatefulWidget {
@override
_FilterScreenState createState() => _FilterScreenState();
}
class _FilterScreenState extends State<FiltersScreen> {
ValueNotifier<bool> _glutenFree = new ValueNotifier(false);
ValueNotifier<bool> _lactoseFree = new ValueNotifier(false);
Widget _buildSwitchListTile(String title, String subTitle, ValueNotifier<bool> currentValue, Function updateValue){
return SwitchListTile(title:Text(title),
value:currentValue.value,
subtitle: Text(subTitle),
onChanged: updateValue
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Filter'),
actions:<Widget>[
IconButton(icon:Icon(Icons.save), onPressed: () { },),
],
),
drawer: MainDrawer(),
body: Column(children:<Widget>[
Container(
padding: EdgeInsets.all(20),
child:Text('Adjust your meal selection',
style:Theme.of(context).textTheme.title), ),
Expanded(
child:ListView(
children:<Widget>[
_buildSwitchListTile('Gluten-free',
'Only include gluten free meals',
_glutenFree,
(newValue){setState((){
_glutenFree.value = newValue; //setting the value
_glutenFree.notifyListeners(); //all the widget who is listening to this value, let them know the change
},);
}),
_buildSwitchListTile('Lactose',
'Only include lactose free meals',
_lactoseFree,
(newValue){setState((){
_lactoseFree = newValue;
_lactoseFree.notifyListeners();
},);
}),
]
)
)
]));
}
}
Upvotes: 0
Views: 822
Reputation: 1121
You can achieve such kind of filter effect by using ValueNotifier to hold the changes of the filter class and using a ValueListenableBuilder
in the Category class to listen to that value change and do the necessary change in the UI.
let's jump to the solution, inside your FiltersScreen
, replace this code
var _glutenFree = false;
var _lactoseFree = false;
with ValueNotifier
kind of object. So it'll look like this
ValueNotifier<bool> _glutenFree = new ValueNotifier(false);
ValueNotifier<bool> _lactoseFree = new ValueNotifier(false);
Changing the value on click is also pretty similar. So this
setState((){
_glutenFree = newValue;
});
will change into this
setState((){
_glutenFree.value = newValue; //setting the value
_glutenFree.notifyListeners(); //all the widget who is listening to this value, let them know the change
});
Now the final step, setting up a widget builder that listen to this value change and rebuild all of it's widget in the tree.
To do that, simply add this as a widget inside your CategoryMealsScreen
ValueListenableBuilder(
valueListenable: _glutenFree,
builder: (context, value, _) {
});
Upvotes: 3