Flutter StreamBuilder与按钮联动异常问题求助及代码修复
问题修复:按钮因Firebase实时监听自动触发的解决方法
问题根源
- 按钮
onPressed里的ref.onValue.listen()会持续监听Firebase节点的实时变化,只要数据更新,回调就会自动执行,和是否点击按钮无关。 - 全局
press变量在StreamBuilder每次重建时都会被重置为false,无法可靠控制触发逻辑。
修复方案
将持续监听的listen()替换为一次性获取当前值的once(),这样只会在点击按钮时获取一次Firebase数据并判断逻辑,不会响应后续的数据变化。同时移除冗余的press变量。
修复后的完整代码
Widget build(BuildContext context) { return FutureBuilder( future: _getColor, builder: ((context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.done: return StreamBuilder<List<MaterialStateProperty<Color>>>( stream: findColors(), builder: (context, snapshot) { if (snapshot.hasData) { return Scaffold( backgroundColor: const Color.fromARGB(200, 8, 111, 118), appBar: AppBar( title: const Text( "Home Layout", style: TextStyle(color: Colors.white), ), centerTitle: false, backgroundColor: const Color.fromARGB(200, 1, 47, 52), ), body: Center( child: Column( children: [ const Row( children: [ Flexible( child: Center( child: Container(), ), ) ], ), Expanded( child: Row( children: [ Expanded( child: Container( margin: const EdgeInsets.symmetric( horizontal: 20.0, vertical: 10.0), child: Row( children: [ Expanded( child: Column(children: [ const Text( "This is the Home Parking Layout", style: TextStyle( color: Colors.white, fontFamily: 'Oswald', fontSize: 40, ), ), const SizedBox(height: 20), Row( children: [ Container( width: 50, height: 30, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.red, ), ), const Text( ' = Parking is occupied', style: TextStyle( color: Colors.white, fontFamily: 'Montserrat', fontSize: 25, ), ) ], ), const SizedBox(height: 5), Row( children: [ Container( width: 50, height: 30, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.blue, ), ), const Text( ' = Parking is available', style: TextStyle( color: Colors.white, fontFamily: 'Montserrat', fontSize: 25, ), ) ], ), const SizedBox(height: 5), Row( children: [ Container( width: 50, height: 30, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.purple, ), ), const Text( ' = Parking is booked', style: TextStyle( color: Colors.white, fontFamily: 'Montserrat', fontSize: 25, ), ) ], ), const SizedBox(height: 20), Row( children: [ OutlinedButton( onPressed: () async { DatabaseReference ref = FirebaseDatabase.instance .ref('TimelessParking/P1'); // 一次性获取当前值,而非持续监听 DatabaseEvent event = await ref.once(); if (event.snapshot.value == 0) { Navigator.push( context, MaterialPageRoute( builder: (context) => const Parking1()), ); } else { showDialog( context: context, builder: (context) => const AlertDialog( title: Text( 'Sorry! This parking cannot be booked'), ), ); } }, style: ButtonStyle( backgroundColor: snapshot.data![0]), child: const Text( 'Parking 1', style: TextStyle( color: Colors.white, fontFamily: 'Oswald', fontWeight: FontWeight.bold, fontSize: 30, ), ), ), const SizedBox(width: 45), OutlinedButton( onPressed: () async { DatabaseReference ref = FirebaseDatabase.instance .ref('TimelessParking/P2'); DatabaseEvent event = await ref.once(); if (event.snapshot.value == 0) { Navigator.push( context, MaterialPageRoute( builder: (context) => const Parking2()), ); } }, style: ButtonStyle( backgroundColor: snapshot.data![1]), child: const Text( 'Parking 2', style: TextStyle( color: Colors.white, fontFamily: 'Oswald', fontWeight: FontWeight.bold, fontSize: 30, ), ), ), ], ), const SizedBox(height: 50), Row( children: [ OutlinedButton( onPressed: () async { DatabaseReference ref = FirebaseDatabase.instance .ref('TimelessParking/P3'); DatabaseEvent event = await ref.once(); if (event.snapshot.value == 0) { Navigator.push( context, MaterialPageRoute( builder: (context) => const Parking3()), ); } }, style: ButtonStyle( backgroundColor: snapshot.data![2]), child: const Text( 'Parking 3', style: TextStyle( color: Colors.white, fontFamily: 'Oswald', fontWeight: FontWeight.bold, fontSize: 30, ), ), ), const SizedBox(width: 45), OutlinedButton( onPressed: () async { DatabaseReference ref = FirebaseDatabase.instance .ref('TimelessParking/P4'); DatabaseEvent event = await ref.once(); if (event.snapshot.value == 0) { Navigator.push( context, MaterialPageRoute( builder: (context) => const Parking4()), ); } }, style: ButtonStyle( backgroundColor: snapshot.data![3]), child: const Text( 'Parking 4', style: TextStyle( color: Colors.white, fontFamily: 'Oswald', fontWeight: FontWeight.bold, fontSize: 30, ), ), ), ], ), ]), ) ], ), ), ) ], ), ) ], ), ), ); } else { return const Scaffold(); } }, ); default: return const Scaffold(); } }), ); }
关键改动说明
- 所有按钮的
onPressed逻辑中,将ref.onValue.listen()替换为await ref.once(),仅在点击时获取一次当前数据。 - 移除了全局的
press变量,逻辑判断直接基于点击瞬间获取的Firebase值。 - 确保每个按钮的操作仅在点击时触发一次,不会因后续Firebase数据变化自动执行。
内容的提问来源于stack exchange,提问作者Faiq Mohammed
相关产品推荐
相关产品推荐

