簡體   English   中英

如何刷新 Flutter 中的 AlertDialog?

[英]How to refresh an AlertDialog in Flutter?

目前,我有一個帶有AlertDialogIconButton 用戶可以點擊 IconButton,我為每次點擊設置了兩種顏色。 問題是我需要關閉 AlertDialog 並重新打開才能看到顏色圖標的狀態變化。 我想在用戶單擊 IconButton 時立即更改它的顏色。

這是代碼:

bool pressphone = false;
//....
new IconButton(
   icon: new Icon(Icons.phone),
   color: pressphone ? Colors.grey : Colors.green,
   onPressed: () => setState(() => pressphone = !pressphone),
),

使用 StatefulBuilder 在 Dialog 中使用 setState 並僅在其中更新 Widgets。

showDialog(
  context: context,
  builder: (context) {
    String contentText = "Content of Dialog";
    return StatefulBuilder(
      builder: (context, setState) {
        return AlertDialog(
          title: Text("Title of Dialog"),
          content: Text(contentText),
          actions: <Widget>[
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: Text("Cancel"),
            ),
            TextButton(
              onPressed: () {
                setState(() {
                  contentText = "Changed Content of Dialog";
                });
              },
              child: Text("Change"),
            ),
          ],
        );
      },
    );
  },
);

這是因為您需要將AlertDialog放在它自己的StatefulWidget並將所有狀態操作邏輯移到那里的顏色上。

更新:

在此處輸入圖片說明

void main() => runApp(MaterialApp(home: Home()));

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
            child: RaisedButton(
      child: Text('Open Dialog'),
      onPressed: () {
        showDialog(
            context: context,
            builder: (_) {
              return MyDialog();
            });
      },
    )));
  }
}

class MyDialog extends StatefulWidget {
  @override
  _MyDialogState createState() => new _MyDialogState();
}

class _MyDialogState extends State<MyDialog> {
  Color _c = Colors.redAccent;
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      content: Container(
        color: _c,
        height: 20.0,
        width: 20.0,
      ),
      actions: <Widget>[
        FlatButton(
            child: Text('Switch'),
            onPressed: () => setState(() {
                  _c == Colors.redAccent
                      ? _c = Colors.blueAccent
                      : _c = Colors.redAccent;
                }))
      ],
    );
  }
}

在 AlertDialog 的content部分使用StatefulBuilder 甚至StatefulBuilder 文檔實際上也有一個帶有對話框的示例。

它的作用是為您提供一個新的contextsetState函數以在需要時重建。

示例代碼:

showDialog(
  context: context,
  builder: (BuildContext context) {

    int selectedRadio = 0; // Declare your variable outside the builder
    
    return AlertDialog( 
      content: StatefulBuilder(  // You need this, notice the parameters below:
        builder: (BuildContext context, StateSetter setState) {
          return Column(  // Then, the content of your dialog.
            mainAxisSize: MainAxisSize.min,
            children: List<Widget>.generate(4, (int index) {
              return Radio<int>(
                value: index,
                groupValue: selectedRadio,
                onChanged: (int value) {
                  // Whenever you need, call setState on your variable
                  setState(() => selectedRadio = value);
                },
              );
            }),
          );
        },
      ),
    );
  },
);

正如我所提到的,這就是showDialog 文檔中所說的:

[...] 構建器返回的小部件與最初調用 showDialog 的位置不共享上下文。 如果對話框需要動態更新,請使用 StatefulBuilder 或自定義 StatefulWidget

首先你需要使用StatefulBuilder 然后我設置_setState變量,它甚至可以在StatefulBuilder之外使用,以設置新狀態。

StateSetter _setState;
String _demoText = "test";

showDialog(
  context: context,
  builder: (BuildContext context) {

    return AlertDialog( 
      content: StatefulBuilder(  // You need this, notice the parameters below:
        builder: (BuildContext context, StateSetter setState) {
          _setState = setState;
          return Text(_demoText);
        },
      ),
    );
  },
);

_setState 的使用方式與 setState 方法相同。 例如像這樣:

_setState(() {
    _demoText = "new test text";
});

如果您通過 View Models 將數據與 UI 分離,並使用帶有ChangeNotifierProvider包,則需要像這樣在調用對話框的小部件中包含當前模型:

showDialog(context: context, builder: (dialog) {
              return ChangeNotifierProvider.value(
                  value: context.read<ViewModel>(),
                child: CustomStatefulDialogWidget(),
              );
            },

請注意,可能有一種更清潔的方法可以做到這一點,但這對我有用。

關於Provider的附加信息: https ://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

showModalBottomSheet(
    context: context,
    builder: (context) {
      return StatefulBuilder(
          builder: (BuildContext context, StateSetter setState /*You can rename this!*/) {
        return Container(
          height: heightOfModalBottomSheet,
          child: RaisedButton(onPressed: () {
            setState(() {
              heightOfModalBottomSheet += 10;
            });
          }),
        );
      });
});

事實上你可以使用 statefullBuilder 但問題是當你使用這個小部件時你不能改變基本屏幕的狀態! 更喜歡導航到新屏幕以便使用 setState。

不確定這是否是最佳實踐,但在使用最佳答案將狀態添加到對話框后,我通過包裝 setState 函數解決了更新對話框狀態和內容狀態的問題:

IconButton(
  onPressed: () {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return StatefulBuilder(
          builder: (context, newSetState) { // Create a "new" state variable
          return AlertDialog(
            content: DropdownButton(
              value: listItem.type,
              items: allItems
              onChanged: (value) {
                newSetState(() {
                  setState(() {
                   // HERE SET THE STATE TWICE
                   // Once with the "new" state, once with the "old"
                  });
                });
              })
            ),
          );
        }
      );
    }
  ),

我被這個問題困住了。您必須將 setState 的名稱更改為任何其他名稱,並將此設置狀態傳遞給所有子函數。 這將按時更新您的 Dialog ui。

 return StatefulBuilder(
      builder: (context, setStateSB) {
        return AlertDialog(
          title: Text("Select Circle To Sync Data!" ,style: TextStyle(color: Colors.white),),
          content: Column(
              children: [
            Text("Select Division!" ,style: TextStyle(color: Colors.white),),
            Container(
              height: 80,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [

                  InputDecorator(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10.0)),
                      contentPadding: EdgeInsets.all(5),
                    ),
                    child: DropdownButtonHideUnderline(
                        child: DropdownButton<String>(
                          isExpanded: true,
                          value: sync_DivisionName_firstValue,
                          items: _DivisionName_list.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,style: TextStyle(color: Colors.black)),
                            );
                          }).toList(),
                          onChanged: (String? newValue) {
                            setStateSB(() {
                              sync_DivisionName_firstValue = newValue!;

                              if(sync_DivisionName_firstValue !="Select Division Name"){

                                print("sync_DivisionName_firstValue$sync_DivisionName_firstValue");
                                _getDistrictName(sync_DivisionName_firstValue,setStateSB);
                              }else{
                                refreashDivisionName(setStateSB);
                              }
                            });
                          },
                        )),
                  ),
                ],
              ),

            ),
            Text("Select District!" ,style: TextStyle(color: Colors.white),),
            Container(
              height: 80,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [

                  InputDecorator(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10.0)),
                      contentPadding: EdgeInsets.all(5),
                    ),
                    child: DropdownButtonHideUnderline(
                        child: DropdownButton<String>(
                          isExpanded: true,
                          value: sync_DistrictName_firstValue,
                          items: _DistrictName_list.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,style: TextStyle(color: Colors.black),),
                            );
                          }).toList(),
                          onChanged: (String? newValue) {
                            setStateSB(() {
                              sync_DistrictName_firstValue = newValue!;

                              if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name"){
                                print("sync_DistrictName_firstValue$sync_DistrictName_firstValue");

                                _getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,setStateSB);
                              }else{
                                refreashDistrictName(setStateSB);
                              }




                            });
                          },
                        )),
                  ),
                ],
              ),

            ),
            Text("Select Tehsil!" ,style: TextStyle(color: Colors.white),),
            Container(
              height: 80,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [

                  InputDecorator(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10.0)),
                      contentPadding: EdgeInsets.all(5),
                    ),
                    child: DropdownButtonHideUnderline(
                        child: DropdownButton<String>(
                          isExpanded: true,
                          value: sync_TehsilName_firstValue,
                          items: _TehsilName_list.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,style: TextStyle(color: Colors.black),),
                            );
                          }).toList(),
                          onChanged: (String? newValue) {
                            setStateSB(() {
                              sync_TehsilName_firstValue = newValue!;

                              if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name"  && sync_TehsilName_firstValue != "Select Tehsil Name"){
                                print("sync_TehsilName_firstValue$sync_TehsilName_firstValue");

                                _getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,setStateSB);
                              }else{
                                refreashTehsilName(setStateSB);
                              }
                            });
                          },
                        )),
                  ),
                ],
              ),

            ),
                Text("Select Rating Area Name!" ,style: TextStyle(color: Colors.white),),
            Container(
              height: 80,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [

                  InputDecorator(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10.0)),
                      contentPadding: EdgeInsets.all(5),
                    ),
                    child: DropdownButtonHideUnderline(
                        child: DropdownButton<String>(
                          isExpanded: true,
                          value: sync_RatingAreaName_firstValue,
                          items: _RatingAreaName_list.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,style: TextStyle(color: Colors.black),),
                            );
                          }).toList(),
                          onChanged: (String? newValue) {
                            setStateSB(() {
                              sync_RatingAreaName_firstValue = newValue!;

                              if(sync_DivisionName_firstValue != "Select Division Name" && sync_DistrictName_firstValue != "Select District Name"  && sync_TehsilName_firstValue != "Select Tehsil Name" && sync_RatingAreaName_firstValue != "Select Rating Area Name"){
                                print("sync_RatingAreaName_firstValue$sync_RatingAreaName_firstValue");

                                _getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue,setStateSB);
                              }else{
                                refreashWardCircleName(setStateSB);
                              }
                            });
                          },
                        )),
                  ),
                ],
              ),

            ),
                Text("Select Ward Circle Name!" ,style: TextStyle(color: Colors.white),),
            Container(
              height: 80,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [

                  InputDecorator(
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(10.0)),
                      contentPadding: EdgeInsets.all(5),
                    ),
                    child: DropdownButtonHideUnderline(
                        child: DropdownButton<String>(
                          isExpanded: true,
                          value: sync_circle_name_firstValue,
                          items: _circle_name_list.map((String value) {
                            return DropdownMenuItem<String>(
                              value: value,
                              child: Text(value,style: TextStyle(color: Colors.black),),
                            );
                          }).toList(),
                          onChanged: (String? newValue) {
                            setStateSB(() {
                              sync_circle_name_firstValue = newValue!;
                              print("sync_circle_name_firstValue$sync_circle_name_firstValue");

                              // if(sync_circle_name_firstValue != "Select Ward Circle Name"){
                              //
                              //   _getWardCircleName(sync_RatingAreaName_firstValue);
                              // }else{
                              //
                              // }
                            });
                          },
                        )),
                  ),
                ],
              ),

            ),
          ]),
          backgroundColor:Color(0xFFEC9F46),
          actions: [
            okButton,SyncButton
          ],
        );
      },
    );

其中一個Inner Funciton是這樣的。

 Future<void> refreashDivisionName( StateSetter setInnerState) async {
    final List<String> _division_name = await getDivisionNameList();
    final List<String> _district_name_list = await getDistrictName(sync_DivisionName_firstValue);
    final List<String> _tehsil_name_list = await getTehsilName(sync_DivisionName_firstValue,sync_DistrictName_firstValue);
    final List<String> _rating_area_name_list = await getRatingAreaName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue);
    final List<String> _ward_circle_name_list = await getWardCircleName(sync_DivisionName_firstValue,sync_DistrictName_firstValue,sync_TehsilName_firstValue,sync_RatingAreaName_firstValue);


    setInnerState(() {
      _division_name.insert(0, "Select Division Name");
      _DivisionName_list = _division_name;
      sync_DivisionName_firstValue = _DivisionName_list[0];

      _district_name_list.insert(0, "Select District Name");
      _DistrictName_list = _district_name_list;
      sync_DistrictName_firstValue = _DistrictName_list[0];

      _tehsil_name_list.insert(0, "Select Tehsil Name");
      _TehsilName_list = _tehsil_name_list;
      sync_TehsilName_firstValue = _TehsilName_list[0];

      _rating_area_name_list.insert(0, "Select Rating Area Name");
      _RatingAreaName_list = _rating_area_name_list;
      sync_RatingAreaName_firstValue = _RatingAreaName_list[0];

      _ward_circle_name_list.insert(0, "Select Ward Circle Name");
      _circle_name_list = _ward_circle_name_list;
      sync_circle_name_firstValue = _circle_name_list[0];
    });
  }

我希望你明白。

基於安德里斯的回答

當 dialog 與父 widget 共享相同狀態時,可以重寫父 widget 的方法 setState 來調用 StatefulBuilder 的 setState,這樣就不需要調用兩次 setState。

StateSetter? _setState;

Dialog dialog = showDialog(
  context: context,
  builder: (BuildContext context) {

    return AlertDialog( 
      content: StatefulBuilder(  // You need this, notice the parameters below:
        builder: (BuildContext context, StateSetter setState) {
          _setState = setState;
          return Text(_demoText);
        },
      ),
    );
  },
);

// set the function to null when dialo is dismiss.
dialogFuture.whenComplete(() => {_stateSetter = null});

@override
void setState(VoidCallback fn) {
   // invoke dialog setState to refresh dialog content when need
   _stateSetter?.call(fn);
   super.setState(fn);
}

目前要檢索我使用的 Dialog 的值

showDialog().then((val){
setState (() {}); 
print (val);
});

示例第一個屏幕

    onPressed: () { 
    showDialog(
       context: context,
       barrierDismissible: false,
       builder: (context) {
         return AddDespesa();
       }).then((val) {
         setState(() {});
         print(val);
        }
    );
   }

第二屏

AlertDialog(
    title: Text("Sucesso!"),
    content: Text("Gasto resgristrado com sucesso"),
    actions: <Widget>[
    FlatButton(
      child: Text("OK"),
      onPressed: () {
         Navigator.pop(context, true);
      },
     ),
   ],
 );

將打印為true,

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM