skip to Main Content

Hi I am quite new to flutter, and just learned about the Provider package. I want to be able to update my selectionSet (the ingredients the user has selected). I update it in two ways — through typing in a textfieldsearch and through taking a picture. The picture returns a list of predicted ingredients. The textfieldsearch works, but the image does not update the selectionSet across widgets. It is returning the correct list, and the selectionSet is updated within the DisplayPictureScreen, but the selectionSet in IngredientsInput is unchanged.

main.dart — I have also tried wrapping with ChangeNotifierProvider.value

Future<void> main() async {
  runApp(
    ChangeNotifierProvider(
      create: (context) => IngredientSelection(),
      child: MaterialApp(
        theme: ThemeData(
          colorScheme: ColorScheme.fromSwatch(
            primarySwatch: Colors.green,
          ),
        ),
        home: TossUp(),
      ),
    ),
  );
}

ingredient_selection.dart

class IngredientSelection extends ChangeNotifier {
  Set<String> selectionSet = {}; 
  void add(String ingredient) {
    selectionSet.add(ingredient); 
    notifyListeners();
  }

  void addAll(List<String> ingredients) {
    selectionSet.addAll(ingredients);
    notifyListeners();
  }
}

display_picture_screen.dart — part of interest is in the elevated button

class DisplayPictureScreen extends StatelessWidget {

  DisplayPictureScreen({super.key, required this.picture});

  final XFile picture;
  final IngredientsClassifier classifier = IngredientsClassifier();
   
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Preview Page')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min, 
          children: [
            Image.file(File(picture.path), fit: BoxFit.cover, width: 250),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: (
                () async {
                  var ingredientSelection = context.read<IngredientSelection>();
                  List<String> ingredients = await classifier.getPicIngredients(picture);
                  ingredientSelection.addAll(ingredients);
                  print("After: ${ingredientSelection.selectionSet}"); //returns correctly
                  
                  Navigator.pop(context);
                  Navigator.pop(context); 
                  }
              ), 
              child: Text("get ingredients")
            )
          ]
        ),
      ),
    );
  }
}

ingredients_input.dart — I have also tried to wrap viewSelections return with Consumer

class IngredientsInput extends StatefulWidget {
  IngredientsInput({super.key});
  @override
  State<IngredientsInput> createState() {
    return _IngredientsInput();
  }
}

class _IngredientsInput extends State<IngredientsInput> {
  //...other code 

  Widget viewSelections() {
        IngredientSelection ingredientSelection;
        ingredientSelection = Provider.of<IngredientSelection>(context);
        //var ingredientSelection = context.watch<IngredientSelection>();
        print(ingredientSelection.selectionSet);
        if(ingredientSelection.selectionSet.isNotEmpty) {
          return Container(
                height: 400,
                child: ListView.builder(
                  padding: const EdgeInsets.all(8),
                  itemCount: ingredientSelection.selectionSet.length,
                  itemBuilder: (BuildContext context, int i) {
                    return Container(
                      height: 40,
                      child: Center(
                        child: Text(
                          ingredientSelection.selectionSet.elementAt(i),
                          style: TextStyle(fontSize: 20),

                          ),
                      )
                    );
                  }
                ),
              );
        }
        return SizedBox(height: 400,);
      }
  @override
  void dispose() {
    myController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
          TextFieldSearch(
            initialList: _ingredientsList, 
            label: label, 
            controller: myController,
            
          ),
          Consumer(
            builder: (context, ingredientSelection, child)  {
                return viewSelections();
            } 
          )
          
      ],
    );
  }
}

Any help greatly appreciated

I have tried to play around with consumers and provider.of and context.read/watch. All of them have the same result: Un-updated set in IngredientsInput, yet updated set in DisplayPictureScreen

2

Answers


  1. Inside viewSelections(), notice how you’re taking the context of the State rather than the Consumer.

    Widget viewSelections() {
      IngredientSelection ingredientSelection;
      ingredientSelection = Provider.of<IngredientSelection>(context); // <--
    

    In fact, the context from your Consumer is not being used at all.

    Consumer(
      builder: (context, ingredientSelection, child)  { // <-- context not being used
          return viewSelections();
      } 
    )
    

    You should move the code inside viewSelections() to the Consumer‘s builder, so the provider will take the context from the Consumer.

    Login or Signup to reply.
  2. Currently ingredientSelection is not listening for changes so it can’t update.

    Change

     ingredientSelection = Provider.of<IngredientSelection>(context);
    

    to

     ingredientSelection = Provider.of<IngredientSelection>(context, listen : true);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search