0

I am creating dynamic radio button and managed to show it. However, when I tap on it, it is not changing the selection. I can see onchanged event fired I am getting the output but I think SetState is not working as expected.

Here is the code.

class FruitsList {
  String name;
  int index;
  FruitsList({this.name, this.index});
}

int  _selectedRadioIndex = 1;

// Default Radio Button Item
  String radioItem = 'Mango';
 
  // Group Value for Radio Button.
  int id = 1;
 
  List<FruitsList> fList = [
    FruitsList(
      index: 1,
      name: "Mango",
    ),
    FruitsList(
      index: 2,
      name: "Apple",
    ),
    FruitsList(
      index: 3,
      name: "Banana",
    ),
    FruitsList(
      index: 4,
      name: "Cheery",
    ),
  ];
Column(
              children: 
                fList.map((data) => RadioListTile(
                  title: Text("${data.name}"),
                  groupValue: id,
                  value: data.index,
                  onChanged: (val) {
                    setState(() {
                      radioItem = data.name ;
                      id = data.index;
                      print(id);
                      print(radioItem);
                      print(val);
                      _selectedRadioIndex = val;
                    });
                  }, 
                )).toList(),
            ),

What am I doing wrong?

Update

As suggested by chunhugun.

I tried below code.

showDialog(
  context: context,
  builder: (context) {
      return StatefulBuilder(
      builder: (context, setState) {
  Column(
              children:            
                fList.map((data) => RadioListTile(
                  title: Text("${data.name}"),
                  groupValue: id,//_selected,//id,
                  value: data.index,
                  onChanged: (val) {
                    setState(() {
                      radioItem = data.name ;
                      id = data.index;
                      print(id);
                      print(radioItem);
                      print(val);
                      _selectedRadioIndex = val;
                    });
                  }, 
                )).toList(),
            );
  }
  );
  },
  );

But it is giving error on these places.

showDialog(
  context: context,
  builder: (context) {

'showDialog' must have a method body because '_ApointmentPriceState' isn't abstract.
Try making '_ApointmentPriceState' abstract, or adding a body to 'showDialog'.
halfer
  • 19,824
  • 17
  • 99
  • 186
Roxx
  • 3,738
  • 20
  • 92
  • 155
  • your code works fine. if you do this in AlertDialog. you can use StatefulBuilder, see this https://stackoverflow.com/questions/51962272/how-to-refresh-an-alertdialog-in-flutter – chunhunghan Sep 16 '20 at 03:20
  • Thanks for the comment. So, where exactly i need to put this. – Roxx Sep 16 '20 at 03:25
  • return AlertDialog( content: StatefulBuilder( // You need this, notice the parameters below: builder: (BuildContext context, StateSetter setState) { – chunhunghan Sep 16 '20 at 03:26
  • sorry. does your original code use AlertDialog? could you post your reproduce original code. under my test. it's works fine. So I guess you show data in AlertDialog. If your original code does not use AlertDialog, I will need reproduce code. – chunhunghan Sep 16 '20 at 04:05
  • Complete code you can find from here. https://stackoverflow.com/questions/63895921/flutter-create-dynamic-radio-buttons – Roxx Sep 16 '20 at 04:52
  • OK. the reason is you use List, please check my answer. – chunhunghan Sep 16 '20 at 05:30

5 Answers5

3

You can copy paste run full code below
You can wrap Row with StatefulBuilder and use List<int> _selectedRadioIndexList and CardNo to control
code snippet

List<int> _selectedRadioIndexList = [];
int CardNo = -1;
...
_selectedRadioIndexList.add(0);
CardNo = CardNo + 1;
int thisCardNo = CardNo;
... 
_selectedRadioIndexList[thisCardNo] = val;  


StatefulBuilder(
              builder: (BuildContext context, StateSetter setState) {
            return Row(
              children: fList
                  .map((data) => Container(
                    width: 100,
                    child: RadioListTile(
                          title: Text("${data.name}"),
                          groupValue: id,
                          value: data.index,
                          onChanged: (val) {
                            setState(() {
                              radioItem = data.name;
                              id = data.index;
                              print(id);
                              print(radioItem);
                              print(val);
                              _selectedRadioIndex = val;
                            });
                          },
                        ),
                  ))
                  .toList(),
            );
          })

working demo

enter image description here

full code

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

class FruitsList {
  String name;
  int index;
  FruitsList({this.name, this.index});
}

class Price extends StatefulWidget {
  @override
  _PriceState createState() => _PriceState();
}

class _PriceState extends State<Price> {
  static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final _scaffoldKey = GlobalKey<ScaffoldState>();
  int currentIndex = 0;
  String person;
  String age;
  String job;

  // Default Radio Button Item
  String radioItem = 'Mango';

  // Group Value for Radio Button.
  int id = 1;
  //int _selectedRadioIndex = 1;
  List<int> _selectedRadioIndexList = [];

  List<FruitsList> fList = [
    FruitsList(
      index: 1,
      name: "Mango",
    ),
    FruitsList(
      index: 2,
      name: "Banana",
    ),
    FruitsList(
      index: 3,
      name: "Apple",
    ),
    FruitsList(
      index: 4,
      name: "Cherry",
    ),
  ];
  int CardNo = -1;
  @override
  void initState() {
    super.initState();
    cards.add(createCard());
  }

  var nameTECs = <TextEditingController>[];
  var ageTECs = <TextEditingController>[];
  var jobTECs = <TextEditingController>[];

  var cards = <Card>[];

  Card createCard() {
    var nameController = TextEditingController();
    var ageController = TextEditingController();
    var jobController = TextEditingController();
    nameTECs.add(nameController);
    ageTECs.add(ageController);
    jobTECs.add(jobController);
    _selectedRadioIndexList.add(0);
    CardNo = CardNo + 1;
    int thisCardNo = CardNo;

    return Card(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Text('Service ${cards.length + 1}'),
          TextFormField(
            style: TextStyle(color: Colors.blue),
            controller: nameController,
            decoration: InputDecoration(labelText: 'Name'),
            validator: validatetext,
            onSaved: (String val) {
              person = val;
            },
          ),

          TextFormField(
            style: TextStyle(color: Colors.blue),
            controller: ageController,
            decoration: InputDecoration(labelText: 'age'),
            validator: validatetext,
            onSaved: (String val) {
              age = val;
            },
          ),
          TextFormField(
            style: TextStyle(color: Colors.blue),
            controller: jobController,
            decoration: InputDecoration(labelText: 'Job'),
            validator: validatetext,
            onSaved: (String val) {
              job = val;
            },
          ),

          //Expanded(
          //            child: Container(
          //            height: 350.0,
          //            child:
          StatefulBuilder(
              builder: (BuildContext context, StateSetter setState) {
            return Row(
              children: fList
                  .map((data) => Container(
                        width: 100,
                        child: RadioListTile(
                          title: Text("${data.name}"),
                          groupValue: id,
                          value: data.index,
                          onChanged: (val) {
                            setState(() {
                              radioItem = data.name;
                              id = data.index;
                              print(id);
                              print(radioItem);
                              print(val);
                              _selectedRadioIndexList[thisCardNo] = val;
                            });
                          },
                        ),
                      ))
                  .toList(),
            );
          }),
          //)),

          /* CheckboxListTile(
        title: Text("title text"),
        value: checkedValue,
        onChanged: (newValue) {
                     setState(() {
                       checkedValue = newValue;
                     });
                   },
        //onChanged: (newValue) { ... },
        controlAffinity: ListTileControlAffinity.leading,  //  <-- leading Checkbox
      ), */

          SizedBox(height: 10),
        ],
      ),

      //   ),
    );
  }

  void _validateInputs() {
    print('button');
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      _onDone();
    } else {}
  }

  _onDone() {
    updateProfile();
    List<PersonEntry> entries = [];
    for (int i = 0; i < cards.length; i++) {
      var name = nameTECs[i].text;
      var age = ageTECs[i].text;
      var job = jobTECs[i].text;
      entries.add(PersonEntry(name, age, job));
    }
  }

  ///////// Save to DB ////////////////////
  Future updateProfile() async {
    try {
      for (int i = 0; i < cards.length; i++) {
        var name = nameTECs[i].text;
        var age = ageTECs[i].text;
        var job = jobTECs[i].text;

        Map<String, dynamic> body = {'name': name, 'age': age, 'job': job};

        print(body);
        nameTECs[i].clear();
        //if(rang == true){

        Response response = await Dio()
            .post("http://192.168.1.102:8080/adddetails.php", data: body);
        print(response.statusCode);
        if (response.statusCode == 404) {
          print('404');
        }
        if (response.statusCode == 200) {
          nameTECs[i].clear();
        }
      }
    } catch (e) {
      print("Exception Caught: $e");
    }
  }

  ///////////////////////////////

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      //appBar: myAppBar(),
      //endDrawer: myDrawer(),
      body: Column(
        children: <Widget>[
          Expanded(
            child: new Form(
              key: _formKey,
              child: ListView.builder(
                itemCount: cards.length,
                itemBuilder: (BuildContext context, int index) {
                  return cards[index];
                },
              ),
            ),
          ),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 2.0),
            color: Colors.grey,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                //    Container(
                Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: FloatingActionButton(
                      heroTag: "btn1",
                      child: Icon(Icons.add),
                      onPressed: () => setState(() => cards.add(createCard())),
                      backgroundColor: Colors.green,
                    )

                    /*RaisedButton(
                  child: Text('Add new'),
                  onPressed: () => setState(() => cards.add(createCard())),
                ),*/
                    ),
                Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: FloatingActionButton(
                      heroTag: "btn2",
                      child: Icon(Icons.remove),
                      onPressed: () => setState(() => cards.removeLast()),
                      backgroundColor: Colors.red,
                    )),
                Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: FloatingActionButton(
                      heroTag: "btn3",
                      child: Icon(Icons.save),
                      onPressed: _validateInputs),
                )
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class PersonEntry {
  final String name;
  final String age;
  final String studyJob;

  PersonEntry(this.name, this.age, this.studyJob);
  @override
  String toString() {
    return 'Person: name= $name, age= $age, study job= $studyJob';
  }
}

Size get preferredSize => Size.fromHeight(kToolbarHeight);

String validatetext(String value) {
  if (value.length < 5)
    return 'More than 5 char is required';
  else
    return null;
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Price(),
    );
  }
}
chunhunghan
  • 51,087
  • 5
  • 102
  • 120
  • Thanks for the answer. Voted Up. I will read it carefully and understand why it was not working. I am hoping that it will work when i click on plus button and not conflict with existing radios in card. – Roxx Sep 16 '20 at 05:42
  • you can reference this https://medium.com/@Nash0x7E2/stateful-widgets-be-gone-stateful-builder-a67f139725a0 and this https://medium.com/flutterdevs/can-stateful-builder-step-into-shoes-of-stateful-widget-81af58824ed0 – chunhunghan Sep 16 '20 at 05:49
  • Thanks for the help. I will work on it and wil try to check http part. I wil let you know if i face any issues during http request. – Roxx Sep 16 '20 at 07:04
  • I need one more help. Can you tell me how can i get the selected value of Radio button in http request. Current code is giving error. _selectedradio: final FruitsList = fList.firstWhere((element) => element.index == _selectedRadioIndex); – Roxx Sep 17 '20 at 02:09
1
    import 'package:flutter/material.dart';

    void main() {
      runApp(MyApp());
    }

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }

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

  

    @override
      _MyHomePageState createState() => _MyHomePageState();
    }

class _MyHomePageState extends State<MyHomePage> {

  var _selectedRadioIndex = 1;

  @override
  Widget build(BuildContext context) {

String radioItem = 'Mango';
int id = 1;

List<FruitsList> fList = [
  FruitsList(
    index: 1,
    name: "Mango",
  ),
  FruitsList(
    index: 2,
    name: "Apple",
  ),
  FruitsList(
    index: 3,
    name: "Banana",
  ),
  FruitsList(
    index: 4,
    name: "Cheery",
  ),
];
return Scaffold(
  appBar: AppBar(
    title: Text(widget.title),
  ),
  body: Center(
    child: Column(
      children:
      fList.map((data) => RadioListTile(
        title: Text("${data.name}"),
        groupValue: _selectedRadioIndex,
        value: data.index,
        onChanged: (val) {
          setState(() {
            _selectedRadioIndex = val;
            radioItem = data.name ;
            id = data.index;
            print(id);
            print(radioItem);
            print(val);

          });
        },
      )).toList(),

    ),
    ),
  
);
  }
}

class FruitsList {
  String name;
  int index;
  FruitsList({this.name, this.index});
}
werux
  • 91
  • 5
0

You put the group value to be id but in setState you are changing _selectedRadioIndex, change your code like this:


RadioListTile(
                  title: Text("${data.name}"),
                  groupValue: _selectedRadioIndex,//_selected,//id,
                  value: data.index,
                  onChanged: (val) {
                    setState(() {
                      radioItem = data.name ;
                      id = data.index;
                      print(id);
                      print(radioItem);
                      print(val);
                      _selectedRadioIndex = val;
                    });

Develocode 777
  • 1,125
  • 9
  • 14
  • thanks for the answer. i tried with groupValue: id and _selectedRadioIndex too. Both way it is not working. – Roxx Sep 16 '20 at 04:38
  • this should work, the error must be somwhere else, you need to post more code. – Develocode 777 Sep 16 '20 at 04:55
  • You can see complete code from this question https://stackoverflow.com/questions/63895921/flutter-create-dynamic-radio-buttons – Roxx Sep 16 '20 at 04:56
0

I just had the same problem and found all those solutions to complicated.

My solution is to create an empty map upfront. Use whatever key-types are unique to your radio-set, for me int was sufficient:

Map<int, int> _selectedRadioValue = {};

Then use this for the groupValue:

groupValue: (_selectedRadioValue[variant.id!] != null) ? _selectedRadioValue[variant.id!] : 1,

and this when setting the new value:

_selectedRadioValue[variant.id!] = value!;

Full code for the widget:

Wrap(
    alignment: WrapAlignment.start,
    runSpacing: 5,
    spacing: 25,
    children: <Widget>[
        for (Choice choice in variant.choices!)

        RadioListTile<int>(
            title: Text("${choice.name}"),
            value: choice.position!,
            groupValue: (_selectedRadioValue[variant.id!] != null) 
                ? _selectedRadioValue[variant.id!] : 1,
            onChanged: (int? value) {
                setState(() {
                    _selectedRadioValue[variant.id!] = value!;
                });
            },
        ),
    ],
),
Thorka Mae
  • 93
  • 1
  • 2
  • 9
0

If using radioGroup widget

int selectedWeekDay=0 ;  //declare parameter: selectedWeekDay as instance variable 
                      //in the class 
                      //monitors the changes to the radio group selection. 
                      //Can be any type. here i use int, represent 0 to 6 
                      //(weekdays)

final _radioOptionsList = [0, 1, 2, 3, 4, 5, 6];  //the list of days.

Here return the widget...

Widget weeklyRadioButton() {   
return SizedBox(
  height:50,
  width:300,
  child:  RadioGroup<int>.builder( 
            direction: Axis.horizontal,
            groupValue: selectedWeekDay, //...note default is set as selectedWeekDay
            horizontalAlignment: MainAxisAlignment.spaceAround,
            onChanged: (value) => setState(() {                  
               selectedWeekDay = value!; //... Note selectedWeekDay will now affect default group value                             
            }),
            
            items: _radioOptionsList,
            textStyle: const TextStyle(
              fontSize: 8,
              color: Colors.blue,
            ),
            itemBuilder: (item) => RadioButtonBuilder(
              item.toString(),   //...text to show on each radio button
            ),
          ),
   );         

}

Make sure to import ...

import 'package:group_radio_button/group_radio_button.dart';

And add group_radio_button to pubspec.yaml ....

dependencies:
  flutter:
  sdk: flutter      
  group_radio_button: ^1.3.0