Flutter ToggleButton选中状态不显示及取值存库问题
问题根源
你当前代码的核心问题是状态变量声明位置错误:
_tempoSelezionato和_scelta被定义在build方法内部,每次调用setState触发UI重绘时,这两个变量都会被重新初始化,导致修改后的状态被覆盖,UI无法正确展示选中状态。- 调试时打印的true/false值正确,是因为
setState里的逻辑确实修改了变量,但重绘后变量又回到初始值,所以按钮颜色没变化。
修复步骤
- 将状态变量移到
_PrenotazioneState类的成员位置,确保变量在组件生命周期内保持状态 - 初始化默认选中状态,优化
onPressed内的逻辑
修改后的完整代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import '../Modelli/immProfilo.dart'; import '../services/Database.dart'; class Prenotazione extends StatefulWidget { const Prenotazione({super.key}); @override State<Prenotazione> createState() => _PrenotazioneState(); } class _PrenotazioneState extends State<Prenotazione> { // 将状态变量移到类成员位置 int? _scelta; // 使用可空类型,初始未选中时为null final List<bool> _tempoSelezionato = <bool>[true, false, false]; bool vertical = false; @override Widget build(BuildContext context) { final controlloIm = Get.put(ImagePickerController()); final String uid = auth.currentUser!.uid; return Scaffold( appBar: AppBar( title: Text('Prenotazione', style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, color: Colors.white, )), backgroundColor: const Color(0xffE5332A), iconTheme: const IconThemeData(color: Colors.white), ), body: Column( children: [ Container( height: 175, decoration: const BoxDecoration( color: Color(0xFFE5332A), borderRadius: BorderRadius.only( bottomRight: Radius.circular(20), bottomLeft: Radius.circular(20), ), ), child: InkWell( onTap: () { Navigator.pushNamed(context, '/profilo'); }, child: StreamBuilder( stream: FirebaseFirestore.instance .collection('utenti') .where('uid', isEqualTo: uid) .snapshots(), builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (!snapshot.hasData) { return const Text('Caricamento'); } else if (snapshot.hasData) { return ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, i) { var doc = snapshot.data!.docs[i]; var data = doc.data() as Map; return ListTile( leading: SizedBox( width: 50, height: 50, child: controlloIm.image.value.path == 'null' ? CircleAvatar( backgroundImage: AssetImage( 'images/utente-predefinito.jpg')) : CircleAvatar( backgroundImage: NetworkImage('${data['url']}'))), title: Text( '${data['Nome']}', style: const TextStyle( fontWeight: FontWeight.w700, fontSize: 18, color: Colors.white, ), ), subtitle: const SizedBox( width: 100, height: 20, child: Column( children: [ Row( children: [ Icon( Icons.circle, color: Colors.green, size: 10, ), SizedBox( width: 7, ), Text('Online', style: TextStyle( color: Colors.white, )), ], ), ], ), ), ); }, ); } return const Text('Qualcosa è andato storto'); }), ), ), Stack(children: [ Padding( padding: const EdgeInsets.all(20.0), child: Container( height: 420, width: 500, transform: Matrix4.translationValues(0.0, -110.0, 0.0), decoration: const BoxDecoration( borderRadius: BorderRadius.all( Radius.circular(20), ), image: DecorationImage( image: AssetImage('images/sfondo.png'), fit: BoxFit.fill, ), color: Colors.white, boxShadow: [ BoxShadow( color: Color.fromRGBO(0, 0, 0, 0.15), spreadRadius: 5, blurRadius: 7, offset: Offset(0, 3), ) ], ), child: Padding( padding: const EdgeInsets.all(25.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Prenota il tuo turno', style: TextStyle( color: Color(0xFFE5332A), fontSize: 24, fontWeight: FontWeight.w700, ), ), const SizedBox( height: 10, ), Text( 'Di quanto tempo avrai bisogno in bagno?', style: TextStyle( color: Colors.black, fontSize: 20, fontWeight: FontWeight.w700, ), ), SizedBox( height: 20, ), SingleChildScrollView( scrollDirection: Axis.horizontal, child: ToggleButtons( onPressed: (int newindex) { setState(() { // 更新选中状态 for (int i = 0; i < _tempoSelezionato.length; i++) { _tempoSelezionato[i] = i == newindex; } // 更新选中的分钟数 switch(newindex){ case 0: _scelta = 5; break; case 1: _scelta = 10; break; case 2: _scelta = 15; break; } }); }, borderRadius: const BorderRadius.all(Radius.circular(8)), selectedBorderColor: Color(0xFF0E8639), selectedColor: Colors.white, fillColor: Color(0xFF0E8639), color: Color(0xFF787878), constraints: const BoxConstraints( minHeight: 50.0, minWidth: 120.0, ), isSelected: _tempoSelezionato, children: [ Text('5 minuti'), Text('10 minuti'), Text('15 minuti'), ], ), ), SizedBox( height: 20, ), Text( '* Ricordati che una volta che avrai prenotato il bagno dovrai aspettare il tuo turno ed una volta concluso confermarlo nell’app con l’apposito pulsante di modo che possa andare la persona successiva.'), SizedBox( height: 20, ), ElevatedButton( onPressed: () { // 在这里使用_scelta变量提交到数据库 if (_scelta != null) { // 示例:提交到Firestore FirebaseFirestore.instance .collection('prenotazioni') .add({ 'uid': uid, 'tempo': _scelta, 'timestamp': Timestamp.now(), }) .then((value) { // 提交成功后的逻辑 ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Prenotazione confermata!')), ); }) .catchError((error) { // 处理错误 ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Errore: $error')), ); }); } else { // 提示用户选择时间 ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Seleziona un tempo di prenotazione')), ); } }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, minimumSize: Size(250, 50), ), child: Text('Conferma la prenotazione', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w700, fontSize: 18, ),), ), ], ), ), ), ), ]), ], ), ); } }
关键修改说明
- 将
_tempoSelezionato和_scelta移到_PrenotazioneState类的成员变量位置,避免每次build重置状态 - 将
_scelta改为可空类型int?,初始值为null,用于判断用户是否选择了时间 - 调整
onPressed内的逻辑,将状态更新和值赋值分开,代码更清晰 - 在确认按钮的
onPressed中,使用_scelta变量提交数据到Firestore,并添加了错误处理和提示
内容的提问来源于stack exchange,提问作者Francesco Ferri
相关产品推荐
相关产品推荐

