mcfred
mcfred

Reputation: 1401

Passing data between two unrelated screens in flutter

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

Answers (1)

Fahim Al Mahmud Ashik
Fahim Al Mahmud Ashik

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

Related Questions