Fedy Belaid
Fedy Belaid

Reputation: 165

Choice chips do not dynamically update in flutter

i'm trying to add data to flutter database using choice chips. Here is how it works: The user has to first of all choose whether the state of the operation is success or failure. Depending on his choice, another choice chips field will have dynamic values. And then the price will be calculated based on that. If it's success the price variable will have a defined value, if it's failure, it'll have zero for all the chips except 2 of them. Anyway, the problem here is that once I switch between success and failure, the value do not get updated unless I unselect and then select it again.

Here is my code where this logic is implemented:

Wrap(
              runSpacing: spacing,
              spacing: spacing,
              children: choiceChipsInterventions
                  .map((cc) => ChoiceChip(
                        label: Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 10),
                          child: Container(
                            child: Align(
                                alignment: Alignment.center,
                                child: Text(cc.label)),
                            height: 30,
                            width: 100,
                          ),
                        ),
                        labelStyle: TextStyle(
                            color: Colors.white, fontWeight: FontWeight.bold),
                        onSelected: (isSelected) => setState(() {
                          intervention = cc.label;
                          if (etatLabel == "Succés") {
                            setState(() {
                              price = cc.prix;
                            });
                          } else if (etatLabel == "Echec") {
                            if (intervention == "SAV sans RDV" ||
                                intervention == "PP sans RDV") {
                              setState(() {
                                price = 25;
                              });
                            } else {
                              setState(() {
                                price = 0;
                              });
                            }
                          }

                          choiceChipsInterventions =
                              choiceChipsInterventions.map((otherChip) {
                            final newChip = otherChip.copy(
                                isSelected: false,
                                label: otherChip.label,
                                selectedColor: otherChip.selectedColor,
                                textColor: otherChip.textColor,
                                prix: otherChip.prix);
                            return cc == newChip
                                ? newChip.copy(
                                    isSelected: isSelected,
                                    label: otherChip.label,
                                    selectedColor: otherChip.selectedColor,
                                    textColor: otherChip.textColor,
                                    prix: otherChip.prix)
                                : newChip;
                          }).toList();
                        }),
                        selected: cc.isSelected,
                        selectedColor: etatLabel == "Echec"
                            ? Color.fromARGB(255, 212, 9, 5)
                            : Colors.green,
                        backgroundColor: Color(0xFF473F97),
                      ))
                  .toList(),
            ), 

Noting that chips data is already defined in a separate file based on a chip model. Here is a screenshot that explains it better:

enter image description here

In this screenshot, I first selected success,then selected an intervention that has a value of 60€, after that I switched the state to failure (Echec), and the price still has 60€, it should have 0€! PS: As mentionned above, if I unselect and select again it will get 0€ as price!

I appreciate your help, thanks in advance!

Upvotes: 0

Views: 992

Answers (1)

Zhar
Zhar

Reputation: 3540

As suggested avoid nested chips or something like. You may also consider using a ValueNotifier to update chips bewteen each others (communication between widgets).

Here is a full working example based on what you want achieve : https://dartpad.dev/?id=8f2d7f9e7d43b450dcedbb1fb520118c

Hope this will help others too !

import 'package:flutter/material.dart';

void main() {
  runApp(const MyHomePage(title: 'demo chips'));
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  final ValueNotifier<String?> _interventionStateNotifier = ValueNotifier<String?>(null);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              MyOptions(notifier: _interventionStateNotifier),
              const SizedBox(height: 25),
              Interventions(notifier: _interventionStateNotifier)
            ],
          ),
        ),
      ),
    );
  }
}

class Intervention{
  final String label;
  final int price;

  Intervention({required this.label, required this.price});
}

class Interventions extends StatefulWidget {
  final ValueNotifier<String?> notifier;
  final List<Intervention> interventions = [Intervention(label:"quick fix", price:30),
                                            Intervention(label:"long fix", price:80)];
  Interventions({Key? key, required this.notifier}) : super(key: key);

  @override
  State<Interventions> createState() => _InterventionsState();
}

class _InterventionsState extends State<Interventions> {
  int? _value;
  int? _priceSelected;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('Select intervention'),
        Wrap(
          children: List<Widget>.generate(
            widget.interventions.length,
                (int index) {
              return ChoiceChip(
                label: Text('${widget.interventions[index].label} (${widget.interventions[index].price})'),
                selected: _value == index,
                onSelected: (bool selected) {
                  setState(() {
                    _value = selected ? index : null;
                    _priceSelected = widget.interventions[index].price;
                  });
                },
              );
            },
          ).toList(),
        ),
        ValueListenableBuilder<String?>( // listen state changes to update price
          builder: (BuildContext context, String? state, Widget? child) {
            switch(state){
              case _MyOptionsState.failedString:
              case null:
                _priceSelected = 0;
                break;
              default:
                if(_value != null) _priceSelected = widget.interventions[_value!].price;
            }
            return Text('Current price intervention : $_priceSelected');
          },
          valueListenable: widget.notifier,
        ),
      ],
    );
  }
}


class MyOptions extends StatefulWidget {
  final ValueNotifier<String?> notifier;
  const MyOptions({super.key, required this.notifier});

  @override
  State<MyOptions> createState() => _MyOptionsState();
}

class _MyOptionsState extends State<MyOptions> {
  static const String failedString = "failed";
  int? _value = 1;
  List<String> items = ["success", failedString];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text("state"),
        Wrap(
          children: List<Widget>.generate(
            items.length,
                (int index) {
              return ChoiceChip(
                label: Text('Item ${items[index]}'),
                selected: _value == index,
                onSelected: (bool selected) {
                  setState(() {
                    _value = selected ? index : null;
                  });
                  widget.notifier.value = items[index];
                },
              );
            },
          ).toList(),
        ),
      ],
    );
  }
}

Upvotes: 1

Related Questions