Flutter点击ListTile无法更新ModalBottomSheet按钮文本问题求助
Flutter模态框内ListTile点击无法更新按钮文本问题
问题描述
点击ChooseRideWidget中的Continue按钮会弹出底部车辆选择模态框,点击对应车辆ListTile时,期望更新模态框底部的Book ${vehicle}按钮文本,但调用setState后按钮文本始终未变化。
相关代码(select_vehicle.dart)
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:ura_taxi/resources/strings.dart'; import 'package:ura_taxi/widgets/home/driver_info_widget.dart'; class ChooseRideWidget extends StatefulWidget { const ChooseRideWidget({Key? key}) : super(key: key); @override State<ChooseRideWidget> createState() => _ChooseRideWidgetState(); } class _ChooseRideWidgetState extends State<ChooseRideWidget> { String vehicle = "Any"; bool isClciked = false; void bb(String v){ print('Clicked Clicked'); if(mounted){ setState(() { vehicle = v; }); } } void showBottomVehicleSheet(){ showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(10.0), topLeft: Radius.circular(10.0)), ), context: context, builder: (context){ return Container( padding: EdgeInsets.symmetric(vertical: 10,horizontal: 10), child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ ListTile( leading: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Image(image: AssetImage('assets/images/car.png'),width: 40,height: 40,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("Book any",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("Mini,Prime Sedan",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { vehicle = "any"; bb(vehicle); }, trailing: Text("\u{20B9}351-\u{20B9}400"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Column( children: [ Image(image: AssetImage('assets/images/rickshaw.png'),width: 30,height: 30,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("Auto",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("Get autos at your door step",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { vehicle = "Auto"; bb(vehicle); print("ListTile" + vehicle); }, trailing: Text("\u{20B9}151"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Column( children: [ Image(image: AssetImage('assets/images/suv.png'),width: 30,height: 30,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("suv",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("comfy economical cars",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { vehicle = "suv"; print("ListTile" + vehicle); }, trailing: Text("\u{20B9}151"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Image(image: AssetImage('assets/images/coupon_icon.png'),width: 30,height: 30,), title: Text("Apply Coupon Code",style: TextStyle(fontWeight: FontWeight.bold),), onTap: () {}, trailing: Icon( Icons.arrow_forward_ios, size: 15, ), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Image(image: AssetImage('assets/images/rupee_icon.png'),width: 30,height: 30,), title: Text("cash",style: TextStyle(fontWeight: FontWeight.bold),), onTap: () {}, trailing: Icon( Icons.arrow_forward_ios, size: 15, ), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), MaterialButton( child: Text('Book ${vehicle}'), height: 50, color: Colors.purple, textColor: Colors.white, minWidth: 280, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), onPressed: () { Navigator.pop(context); showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(10.0), topLeft: Radius.circular(10.0)), ), context: context, builder: (context) { return DriverInfoWidget(); }); }, ) ], ), ), ); }); } @override Widget build(BuildContext context) { return Wrap( children:[ Padding( padding: EdgeInsets.symmetric(vertical: 10,horizontal: 20), child: Column( children: [ Text(Strings.someone_take_ride_title,style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),), SizedBox(height: 10,), Text(Strings.someone_take_ride_subtitle,style: TextStyle(color: Colors.grey,fontSize: 15),textAlign: TextAlign.center,), SizedBox(height: 10,), Row( children: [ Radio( value: "myself", groupValue: "myself", onChanged: (value){ setState(() { // gender = value.toString(); }); }), Icon( Icons.person, color: Colors.blue, ), SizedBox(width: 5,), Text("Myself",style: TextStyle(fontSize: 15),) ], ), const Divider( thickness: 1, // thickness of the line indent: 45, // empty space to the leading edge of divider. endIndent: 5, // empty space to the trailing edge of the divider. color: Colors.grey, // The color to use when painting the line. height: 20, // The divider's height extent. ), Row( children: [ SizedBox(width: 45,), Icon( Icons.contact_page, color: Colors.blue, ), SizedBox(width: 5,), Text(Strings.someone_take_ride_contact,style: TextStyle(fontSize: 15,color: Colors.blue),), SizedBox(width: 25,), Spacer(), Icon( Icons.arrow_forward_ios, color: Colors.blue, size: 15, ), ], ), SizedBox(height: 20,), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MaterialButton( height: 50.0, minWidth: 150.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0)), color: Colors.grey[200], textColor: Colors.black, child: Text("skip"), onPressed: () => {}, splashColor: Colors.redAccent, ), MaterialButton( height: 50.0, minWidth: 150.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0)), color: Colors.black, textColor: Colors.white, child: Text("Continue"), onPressed: () { Navigator.pop(context); showBottomVehicleSheet(); } ) ], ) ], ), ), ] ); } }
问题原因
showModalBottomSheet创建的模态框是独立上下文,UI在弹出时就已构建完成,父组件的setState无法触发模态框内部的UI刷新,因此即便更新了vehicle变量,按钮文本也不会变化。
解决方案
方案一:使用StatefulBuilder包裹模态框内容
StatefulBuilder提供局部setState方法,可直接更新模态框内部UI。修改showBottomVehicleSheet方法如下:
void showBottomVehicleSheet(){ showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(10.0), topLeft: Radius.circular(10.0)), ), context: context, builder: (context){ return StatefulBuilder( builder: (context, setStateSheet) { return Container( padding: EdgeInsets.symmetric(vertical: 10,horizontal: 10), child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ ListTile( leading: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Image(image: AssetImage('assets/images/car.png'),width: 40,height: 40,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("Book any",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("Mini,Prime Sedan",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { setStateSheet(() { vehicle = "any"; }); }, trailing: Text("\u{20B9}351-\u{20B9}400"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Column( children: [ Image(image: AssetImage('assets/images/rickshaw.png'),width: 30,height: 30,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("Auto",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("Get autos at your door step",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { setStateSheet(() { vehicle = "Auto"; }); print("ListTile" + vehicle); }, trailing: Text("\u{20B9}151"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Column( children: [ Image(image: AssetImage('assets/images/suv.png'),width: 30,height: 30,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("suv",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("comfy economical cars",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { setStateSheet(() { vehicle = "suv"; }); print("ListTile" + vehicle); }, trailing: Text("\u{20B9}151"), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Image(image: AssetImage('assets/images/coupon_icon.png'),width: 30,height: 30,), title: Text("Apply Coupon Code",style: TextStyle(fontWeight: FontWeight.bold),), onTap: () {}, trailing: Icon( Icons.arrow_forward_ios, size: 15, ), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), ListTile( leading: Image(image: AssetImage('assets/images/rupee_icon.png'),width: 30,height: 30,), title: Text("cash",style: TextStyle(fontWeight: FontWeight.bold),), onTap: () {}, trailing: Icon( Icons.arrow_forward_ios, size: 15, ), dense:true, visualDensity: VisualDensity(vertical: 3), ), SizedBox(height: 5,), MaterialButton( child: Text('Book ${vehicle}'), height: 50, color: Colors.purple, textColor: Colors.white, minWidth: 280, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), onPressed: () { Navigator.pop(context); showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(10.0), topLeft: Radius.circular(10.0)), ), context: context, builder: (context) { return DriverInfoWidget(); }); }, ) ], ), ), ); }, ); }); }
方案二:将模态框内容抽为独立StatefulWidget
若模态框逻辑复杂,建议抽成单独组件,自行管理选中状态,提升代码可维护性:
// 独立的车辆选择模态框组件 class VehicleBottomSheet extends StatefulWidget { const VehicleBottomSheet({Key? key}) : super(key: key); @override State<VehicleBottomSheet> createState() => _VehicleBottomSheetState(); } class _VehicleBottomSheetState extends State<VehicleBottomSheet> { String vehicle = "Any"; @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.symmetric(vertical: 10,horizontal: 10), child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ ListTile( leading: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Image(image: AssetImage('assets/images/car.png'),width: 40,height: 40,), Expanded(child: Text("3 min",style: TextStyle(fontSize: 10))) ], ), title: Text("Book any",style: TextStyle(fontWeight: FontWeight.bold),), subtitle: Text("Mini,Prime Sedan",style: TextStyle(fontSize: 10,color: Colors.grey)), onTap: () { setState(() { vehicle = "any"; }); }, trailing: Text("\u{20B9}351-\u{20B9}400"), dense:true, visualDensity: VisualDensity(vertical: 3), ), // 其余ListTile和按钮逻辑同理,直接使用setState更新 MaterialButton( child: Text('Book ${vehicle}'), height: 50, color: Colors.purple, textColor: Colors.white, minWidth: 280, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), onPressed: () { Navigator.pop(context); showModalBottomSheet( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(10.0), topLeft: Radius.circular(10.0)), ), context: context, builder: (context) { return DriverInfoWidget(); }); }, ) ], ), ), ); } }
修改`show
相关产品推荐
相关产品推荐

