Как обновить алертилдиалог в флаттере

В настоящее время я открываю AlertDialog с помощью IconButton. Пользователь может нажать на эту иконку, у меня есть два цвета для каждого клика. Проблема в том, что мне нужно закрыть AlertDialog и открыть снова, чтобы увидеть изменение состояния значка цвета. Я хочу просто открыть AlertDialog, нажать на IconButton и изменить его цвет.

Вот код:

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

11 ответов

Используйте StatefulBuilder, чтобы использовать setState внутри Dialog и обновлять виджеты только внутри него.

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>[
            FlatButton(
              onPressed: () => Navigator.pop(context),
              child: Text("Cancel"),
            ),
            FlatButton(
              onPressed: () {
                setState(() {
                  contentText = "Changed Content of Dialog";
                });
              },
              child: Text("Change"),
            ),
          ],
        );
      },
    );
  },
);

В документации предлагается использовать StatefulBuilder в contentраздел AlertDialog. Даже в документации StatefulBuilder есть пример с диалогом.

Что он делает, так это предоставляет вам новый контекст и функцию setState для перестройки при необходимости.

Пример кода из документации:

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, если диалоговое окно необходимо обновлять динамически.

Это потому, что вам нужно поставить 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;
                }))
      ],
    );
  }
}

Сначала вам нужно использовать 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 и используете пакет с ChangeNotifier, вам нужно будет включить вашу текущую модель, например, в виджет, вызывающий диалог:

      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

Не уверен, что это лучшая практика, но я решил проблему обновления как состояния диалога, так и состояния содержимого, обернув функции 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"
                  });
                });
              })
            ),
          );
        }
      );
    }
  ),

showModalBottomSheet(context: context,builder: (context) {return StatefulBuilder(builder: (BuildContext context, StateSetter setState /Вы можете переименовать это!/) {return Container(height: heightOfModalBottomSheet,child: RaisedButton(onPressed: () { setState( () {heightOfModalBottomSheet += 10;});}), ); });});

Я застрял с этой проблемой. Вы должны изменить имя setState на любое другое имя и передать это состояние набора всем подфункциям. Это своевременно обновит ваш диалоговый интерфейс.

       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
          ],
        );
      },
    );

Одна из внутренних функций подобна этой.

       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];
    });
  }

Я надеюсь, что вы под стендом.

На самом деле, вы можете использоватьStatefullBuilderно проблема в том, что при использовании этого виджета вы не можете изменить состояние основного экрана! Предпочитаю перейти на новый экран, чтобы использоватьsetState.

на основе ответа Андриса .

когда диалоговое окно использует одно и то же состояние с родительским виджетом, вы можете переопределить метод setState родительского виджета, чтобы вызвать setState StatefulBuilder, поэтому вам не нужно вызывать 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);
});

Пример 1-го экрана

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

2-й экран

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

Будет напечатано правда,

Другие вопросы по тегам