Home > Blockchain >  how to have two radio buttons in one row in each listtile item created by the user? in flutter
how to have two radio buttons in one row in each listtile item created by the user? in flutter

Time:01-06

I have a page that the user can add students to the list by entering their name in the listtile in the listview, i wanted to have 2 specific radio buttons for each name one green one red for their presence or absence. I have created my version of it already but when you click on radio button it changes all in that column. is there any other way that this can be done?

1 2

my code:

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

class InsideList extends StatefulWidget {
  final String name;

  InsideList(this.name);

  @override
  State<InsideList> createState() => _InsideListState();
}

class _InsideListState extends State<InsideList> {
  List<String> _students = [];

  late int selectedRadio;

  late TextEditingController _textController;

  @override
  void initState() {
    super.initState();
    _textController = TextEditingController();
    selectedRadio = 0;
  }

  SetselectedRadio(int? val) {
    setState(() {
      selectedRadio = val!;
    });
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.name),
        centerTitle: true,
        backgroundColor: const Color.fromARGB(255, 22, 37, 50),
        toolbarHeight: 65,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(
            bottom: Radius.circular(30),
          ),
        ),
      ),
      body: _students.length > 0
          ? ListView.separated(
              itemCount: _students.length,
              itemBuilder: (_, index) {
                return ListTile(
                  leading: const Icon(Icons.person),
                  trailing: FittedBox(
                    fit: BoxFit.fill,
                    child: Row(
                      children: [
                        Radio(
                            activeColor: Colors.green,
                            value: 0,
                            groupValue: selectedRadio,
                            onChanged: (val) {
                              SetselectedRadio(val);
                            }),
                        Radio(
                          activeColor: Colors.red,
                          value: 1,
                          groupValue: selectedRadio,
                          onChanged: (val) {
                            SetselectedRadio(val);
                          },
                        )
                      ],
                    ),
                  ),
                  title: Center(child: Text(_students[index])),
                  onTap: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: ((context) =>
                                InsideList(_students[index]))));
                  },
                  onLongPress: (() async {
                    await showDialog(
                        context: context,
                        builder: ((context) {
                          return AlertDialog(
                            title: const Text(
                              "Are you sure you want to delete this student?",
                              style: TextStyle(fontSize: 15),
                            ),
                            actions: [
                              TextButton(
                                  child: Text("cancel"),
                                  onPressed: (() {
                                    Navigator.pop(context);
                                  })),
                              TextButton(
                                child: Text('Delete'),
                                onPressed: () {
                                  setState(() {
                                    _students.removeAt(index);
                                    Navigator.pop(context);
                                  });
                                },
                              ),
                            ],
                          );
                        }));
                  }),
                );
              },
              separatorBuilder: (BuildContext context, int index) =>
                  const Divider(
                color: Colors.black,
              ),
            )
          : const Center(
              child: Text("You currently have no students. Add from below."),
            ),
      floatingActionButton: SpeedDial(
        animatedIcon: AnimatedIcons.menu_arrow,
        spacing: 6,
        spaceBetweenChildren: 6,
        backgroundColor: const Color.fromARGB(255, 22, 37, 50),
        foregroundColor: const Color.fromARGB(255, 255, 255, 255),
        children: [
          SpeedDialChild(
            child: const Icon(Icons.group_add),
            label: "add student",
            onTap: () async {
              final result = await showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    title: const Text('Add a new student'),
                    content: TextField(
                      controller: _textController,
                      autofocus: true,
                      decoration: const InputDecoration(
                          hintText: "Enter the name of the student."),
                    ),
                    actions: [
                      TextButton(
                        child: Text('Cancel'),
                        onPressed: () {
                          Navigator.pop(context);
                        },
                      ),
                      TextButton(
                        child: Text('Add'),
                        onPressed: () {
                          Navigator.pop(context, _textController.text);
                          _textController.clear();
                        },
                      ),
                    ],
                  );
                },
              );
              if (result != null) {
                result as String;
                setState(() {
                  _students.add(result);
                });
              }
            },
          ),
        ],
      ),
    );
  }
}

CodePudding user response:

It's because basically you are assigning same values for each Radio Button Group. There is a better way but I just have modified your code a bit to show you how to do it.

First, you assign a list for radio values along with students.

List<String> _students = [];
List<int> _selectedRadio = [];

And for assigning a value to a radio button, you need index of the radio button as well.

void _selectRadio(int index, int? val) {
    setState(() {
      _selectedRadio[index] = val ?? 0;
    });
}

Then for Radio Buttons, assign a group value with index.

Radio(
    activeColor: Colors.green,
    value: 0,
    groupValue: _selectedRadio[index],
    onChanged: (val) {
      _selectRadio(index, val);
    },
),
Radio(
    activeColor: Colors.red,
    value: 1,
    groupValue: _selectedRadio[index],
    onChanged: (val) {
       _selectRadio(index, val);
    },
)

Then finally, when you create a student, you add a radio button value to the list of radio button value.

if (result != null) {
      result as String;
      setState(() {
          _students.add(result);
          _selectedRadio.add(0);
   });
}

And below is the full working code. Hope this helps.

class InsideList extends StatefulWidget {
  final String name;

  InsideList(this.name);

  @override
  State<InsideList> createState() => _InsideListState();
}

class _InsideListState extends State<InsideList> {
  List<String> _students = [];
  List<int> _selectedRadio = [];

  late TextEditingController _textController;

  @override
  void initState() {
    super.initState();
    _textController = TextEditingController();
  }

  void _selectRadio(int index, int? val) {
    setState(() {
      _selectedRadio[index] = val ?? 0;
    });
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.name),
        centerTitle: true,
        backgroundColor: const Color.fromARGB(255, 22, 37, 50),
        toolbarHeight: 65,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(
            bottom: Radius.circular(30),
          ),
        ),
      ),
      body: _students.length > 0
          ? ListView.separated(
              itemCount: _students.length,
              itemBuilder: (_, index) {
                return ListTile(
                  leading: const Icon(Icons.person),
                  trailing: FittedBox(
                    fit: BoxFit.fill,
                    child: Row(
                      children: [
                        Radio(
                            activeColor: Colors.green,
                            value: 0,
                            groupValue: _selectedRadio[index],
                            onChanged: (val) {
                              _selectRadio(index, val);
                            }),
                        Radio(
                          activeColor: Colors.red,
                          value: 1,
                          groupValue: _selectedRadio[index],
                          onChanged: (val) {
                            _selectRadio(index, val);
                          },
                        )
                      ],
                    ),
                  ),
                  title: Center(child: Text(_students[index])),
                  onTap: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: ((context) =>
                                InsideList(_students[index]))));
                  },
                  onLongPress: (() async {
                    await showDialog(
                        context: context,
                        builder: ((context) {
                          return AlertDialog(
                            title: const Text(
                              "Are you sure you want to delete this student?",
                              style: TextStyle(fontSize: 15),
                            ),
                            actions: [
                              TextButton(
                                  child: Text("cancel"),
                                  onPressed: (() {
                                    Navigator.pop(context);
                                  })),
                              TextButton(
                                child: Text('Delete'),
                                onPressed: () {
                                  setState(() {
                                    _students.removeAt(index);
                                    _selectedRadio.removeAt(index);
                                    Navigator.pop(context);
                                  });
                                },
                              ),
                            ],
                          );
                        }));
                  }),
                );
              },
              separatorBuilder: (BuildContext context, int index) =>
                  const Divider(
                color: Colors.black,
              ),
            )
          : const Center(
              child: Text("You currently have no students. Add from below."),
            ),
      floatingActionButton: SpeedDial(
        animatedIcon: AnimatedIcons.menu_arrow,
        spacing: 6,
        spaceBetweenChildren: 6,
        backgroundColor: const Color.fromARGB(255, 22, 37, 50),
        foregroundColor: const Color.fromARGB(255, 255, 255, 255),
        children: [
          SpeedDialChild(
            child: const Icon(Icons.group_add),
            label: "add student",
            onTap: () async {
              final result = await showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    title: const Text('Add a new student'),
                    content: TextField(
                      controller: _textController,
                      autofocus: true,
                      decoration: const InputDecoration(
                          hintText: "Enter the name of the student."),
                    ),
                    actions: [
                      TextButton(
                        child: Text('Cancel'),
                        onPressed: () {
                          Navigator.pop(context);
                        },
                      ),
                      TextButton(
                        child: Text('Add'),
                        onPressed: () {
                          Navigator.pop(context, _textController.text);
                          _textController.clear();
                        },
                      ),
                    ],
                  );
                },
              );
              if (result != null) {
                result as String;
                setState(() {
                  _students.add(result);
                  _selectedRadio.add(0);
                });
              }
            },
          ),
        ],
      ),
    );
  }
}

CodePudding user response:

You have to create List < int > SelectedRadio , which will always has your students list length. Next in method SetSelectedRadio you have to change value in SelectedRadio[student_index]

CodePudding user response:

You have done it wrong you have given the radioButtons a single variable which all the radioButtons are referring to this cause them to share the same value and change accordingly(meaning all the radioButtons with corresponding values will change).

You can use various methods to pass this FOR EXAMPLE :

  1. You can generate a secondary list that will hold all the bool values for each and every list item you can use list.generate() to generate the list depending on the length of the _student list.

  2. You can create a model class where you save both name and the int value for the radio buttons (Most preferred as it gives more flexibility for future changes) I have mentioned the same below

Full code


// Here I have created the model class to create a list.
// do not make the arguments final as they will not change as we need them to change.
class student {
  String nameOfStudent;
  int isPresent;

  student({
    required this.nameOfStudent,
    required this.isPresent,
  });
}

class InsideList extends StatefulWidget {
  final String name;

  InsideList(this.name);

  @override
  State<InsideList> createState() => _InsideListState();
}

class _InsideListState extends State<InsideList> {
//   As this list is not final one can change the values dynamically.
//   You can add the items using _students.add(student(
//       nameOfStudent: "Name",
//       isPresent: 0,
//     ));
  List<student> _students = [];
  late TextEditingController _textController;

  @override
  void initState() {
    super.initState();
    _textController = TextEditingController();
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.name),
        centerTitle: true,
        backgroundColor: const Color.fromARGB(255, 22, 37, 50),
        toolbarHeight: 65,
        shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(
            bottom: Radius.circular(30),
          ),
        ),
      ),
      body: _students.length > 0
          ? ListView.separated(
              itemCount: _students.length,
              itemBuilder: (_, index) {
                return ListTile(
                  leading: const Icon(Icons.person),
                  trailing: FittedBox(
                    fit: BoxFit.fill,
                    child: Row(
                      children: [
                        Radio(
                            activeColor: Colors.green,
                            value: 0,
                            groupValue: _students[index].isPresent,
                            onChanged: (val) {
                              setState(() {
                                _students[index].isPresent = val!;
                              });
                            }),
                        Radio(
                          activeColor: Colors.red,
                          value: 1,
                          // this will go to the list with the idex and fetch the value 
                          groupValue: _students[index].isPresent,
                          onChanged: (val) {
                            // this will assign a new value to the item with the corresponding index
                            // this will give each and every item its own radioButton variable resulting in proper value change for each item in the list.
                            setState(() {
                                _students[index].isPresent = val!;
                              });
                          },
                        )
                      ],
                    ),
                  ),
                  title: Center(child: Text(_students[index].nameOfStudent)),
                  onTap: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: ((context) =>
                                InsideList(_students[index]))));
                  },
                  onLongPress: (() async {
                    await showDialog(
                        context: context,
                        builder: ((context) {
                          return AlertDialog(
                            title: const Text(
                              "Are you sure you want to delete this student?",
                              style: TextStyle(fontSize: 15),
                            ),
                            actions: [
                              TextButton(
                                  child: Text("cancel"),
                                  onPressed: (() {
                                    Navigator.pop(context);
                                  })),
                              TextButton(
                                child: Text('Delete'),
                                onPressed: () {
                                  setState(() {
                                    _students.removeAt(index);
                                    Navigator.pop(context);
                                  });
                                },
                              ),
                            ],
                          );
                        }));
                  }),
                );
              },
              separatorBuilder: (BuildContext context, int index) =>
                  const Divider(
                color: Colors.black,
              ),
            )
          : const Center(
              child: Text("You currently have no students. Add from below."),
            ),
    );
  }
}

As I have mentioned there are many more ways to do the same (using Map as well) Hope this is help full and keep in mind about making variables final as it will not change will the application is running.

  • Related