Flutter:每个ListTile独立双单选按钮(出勤/缺勤)问题解决
问题原因
你当前所有单选按钮都绑定了同一个selectedRadio全局变量,导致任何一个Radio被选中时,所有条目的Radio都会同步更新状态,自然无法实现每个学生独立的出勤/缺勤标记。
解决方案
给每个学生单独维护出勤状态,具体实现步骤如下:
- 创建学生数据模型
定义Student类,包含姓名和出勤状态字段,让每个学生拥有独立的状态:
class Student { final String name; int attendanceStatus; // 0=出勤(绿色), 1=缺勤(红色) Student(this.name, {this.attendanceStatus = 0}); }
修改列表类型
把原来的List<String> _students替换为List<Student> _students,用对象存储每个学生的姓名和状态。调整添加学生逻辑
添加新学生时,创建Student实例并默认设置出勤状态为0:
if (result != null) { setState(() { _students.add(Student(result)); }); }
- 更新单选按钮绑定逻辑
在ListView的itemBuilder中,让每个Radio绑定对应Student的attendanceStatus,点击时仅修改当前学生的状态:
Radio( activeColor: Colors.green, value: 0, groupValue: _students[index].attendanceStatus, onChanged: (val) { setState(() { _students[index].attendanceStatus = val!; }); }, ), Radio( activeColor: Colors.red, value: 1, groupValue: _students[index].attendanceStatus, onChanged: (val) { setState(() { _students[index].attendanceStatus = val!; }); }, )
- 适配其他关联逻辑
比如ListTile标题改为显示学生姓名Text(_students[index].name),删除逻辑直接移除对应Student对象即可。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; class Student { final String name; int attendanceStatus; // 0=出勤(绿色), 1=缺勤(红色) Student(this.name, {this.attendanceStatus = 0}); } class InsideList extends StatefulWidget { final String name; InsideList(this.name); @override State<InsideList> createState() => _InsideListState(); } class _InsideListState extends State<InsideList> { 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].attendanceStatus, onChanged: (val) { setState(() { _students[index].attendanceStatus = val!; }); }), Radio( activeColor: Colors.red, value: 1, groupValue: _students[index].attendanceStatus, onChanged: (val) { setState(() { _students[index].attendanceStatus = val!; }); }, ) ], ), ), title: Center(child: Text(_students[index].name)), onTap: () { Navigator.push( context, MaterialPageRoute( builder: ((context) => InsideList(_students[index].name)))); }, 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) { setState(() { _students.add(Student(result)); }); } }, ), ], ), ); } }
关键说明
- 移除了全局的
selectedRadio变量,改为每个Student实例维护自己的attendanceStatus - 每个条目里的Radio组件仅绑定对应学生的状态,点击时仅更新当前学生的状态
- 数据模型的设计让状态管理更清晰,后续扩展更多学生属性也更方便
内容的提问来源于stack exchange,提问作者Killertouch
相关产品推荐
相关产品推荐

