You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:45:40