oczek
oczek

Reputation: 5

All CheckBoxListTile items in a List are being selected instead of just one - Flutter

I'm trying to build a shopping list based on meals ingredients that are passed in Route Settings on which user can select/deselect each items separately. Here is the code:

import 'package:cookup/src/consts/colors.dart';
import 'package:flutter/material.dart';
import '/dummy_data.dart';

class ShoppingListScreen extends StatefulWidget {
  static const routeName = '/shoppig-list';

  final Function toggleFavorite;
  final Function isFavorite;

  ShoppingListScreen(this.toggleFavorite, this.isFavorite);

  @override
  State<ShoppingListScreen> createState() => _ShoppingListScreenState();
}

class _ShoppingListScreenState extends State<ShoppingListScreen> {
  bool isSelected = false;

  @override
  Widget build(BuildContext context) {
    final mealId = ModalRoute.of(context)?.settings.arguments as String;
    final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);

    return Scaffold(
      appBar: AppBar(
        iconTheme: IconThemeData(color: kFontColorBlack),
        backgroundColor: kBackgroundColor,
        title: Text(
          '${selectedMeal.title}',
          maxLines: 2,
          overflow: TextOverflow.ellipsis,
          textAlign: TextAlign.center,
          style: TextStyle(color: kFontColorBlack),
        ),
        actions: [
          IconButton(
            icon: Icon(
              widget.isFavorite(mealId)
                  ? Icons.favorite
                  : Icons.favorite_border,
            ),
            onPressed: () => widget.toggleFavorite(mealId),
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              height: 250,
              width: double.infinity,
              child: Image.network(
                selectedMeal.imageUrl,
                fit: BoxFit.cover,
              ),
            ),
            Container(
              alignment: Alignment.topLeft,
              margin: EdgeInsets.symmetric(vertical: 10),
              child: const Padding(
                padding: EdgeInsets.all(16.0),
                child: Text(
                  "Ingredients",
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 22.0,
                  ),
                ),
              ),
            ),
            ListView.builder(
              itemCount: selectedMeal.ingredients.length,
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              itemBuilder: (context, index) {
                return CheckboxListTile(
                  title: Text(selectedMeal.ingredients[index]),
                  value: isSelected,
                  controlAffinity: ListTileControlAffinity.leading,
                  onChanged: (
                    value,
                  ) {
                    setState(() {
                      isSelected = value!;
                    });
                  },
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

and the result is visible here as I got stuck having all items being selcted/deselected on click: enter image description here Which is not the expeted behaviour as I need only one item being seletced on click. I was trying variious things but end up having either the above result, having some error regaridng incorrect list Range or I got error that dependOnInheritedWidgetOfExactType<_ModalScopeStatus>() or dependOnInheritedElement() was called before _ShoppingListScreenState.initState() completed. What I am doing wrong? Please help! :)

Upvotes: 0

Views: 127

Answers (2)

oczek
oczek

Reputation: 5

Thanks! I have updated the model from the list to a map with a boolean value and used below builder code - it works as expected now:

            ListView.builder(
          itemCount: selectedMeal.ingredients.length,
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          itemBuilder: (context, index) {
            final ingredient =
                selectedMeal.ingredients.keys.toList()[index];
            return CheckboxListTile(
              title: Text(ingredient),
              key: Key(ingredient),
              value: selectedIngredients[ingredient] ?? false,
              controlAffinity: ListTileControlAffinity.leading,
              onChanged: (
                value,
              ) {
                setState(() {
                  selectedIngredients[ingredient] = value!;
                });
              },
            );
          },
        ),

Upvotes: 0

VIREN GAJJAR
VIREN GAJJAR

Reputation: 134

The reason why you are getting this error is you are passing the same value isSelected in all your items in the ListView.builder.

You have to add a boolean field in your model class which will store the value for each and every item in your list.

Consider following example for model class.

class DummyMeals {
 final String name;
 final String id;
 bool isSelected;

 DummyMeals({
  required this.name,
  required this.id,
  this.isSelected = false,
 });
}

ListView.builder(
          itemCount: itemList.length,
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          itemBuilder: (context, index) {
            return CheckboxListTile(
              key: Key('$index'),
              title: Text(itemList[index].name),
              value: itemList[index].isSelected,
              controlAffinity: ListTileControlAffinity.leading,
              onChanged: (val) {
                setState(() {
                  itemList[index].isSelected = !itemList[index].isSelected;
                });
              },
            );
          },
        ),

Hope this helps !

Upvotes: 1

Related Questions