我有一个带有 StatelessWidget 子级的 StatefulWidget 父级,它返回一个 AlertDialog 框。当按下“绿色下载”按钮时,StatelessWidget 是从 StatefulWidget 中的构建器构建的。(在 AlertDialog 中确认后,完整的代码将获取并存储数据)。
在 AlertDialog 框中是一个 DropdownButtonFormField。我已经内置了自己的验证和错误消息,以确保关联的值不为空。(我无法获得 DropdownButtonFormField 的内置验证来显示整个错误消息而不会被切断)。
我不明白为什么我的 AlertDialog 没有更新以显示回调的 SetState 之后的错误消息,即使使用 StatefulBuilder(我可能没有正确使用)。我试过使用 StatefulWidget
当前输出: 当您在 AlertDialog 中按下 yes 按钮,但下拉列表值为 null 或为空时,AlertDialog 不会更新以在显示错误消息的 AlertDialog 中显示 Center 小部件。如果您弹出 AlertDialog 并重新打开它,它会显示错误消息。
所需的输出 当您按下 AlertDialog 中的 yes 按钮,但下拉列表值为 null 或为空时,AlertDialog 会更新以在显示错误消息的 AlertDialog 中显示 Center 小部件。
请问你能帮忙吗?
在下面重新创建的可用代码:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isLoading = false;
bool _downloaded = false;
File cardImage;
String _languageDropdownValue;
bool isError = false;
List<Map<String, String>> _languages = [
{'code': 'en', 'value': 'English'},
{'code': 'fr', 'value': 'French'},
];
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: _downloaded
? IconButton(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 0),
icon: Icon(
Icons.open_in_new,
size: 45.0,
color: Colors.green,
),
onPressed: () {
print('Open button pressed');
})
: _isLoading
? CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
)
: IconButton(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 0),
icon: Icon(
Icons.download_rounded,
size: 45.0,
color: Colors.green,
),
onPressed: () {
print('Download button pressed');
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return DownloadScreen(
callbackFunction: alertDialogCallback,
dropDownFunction: alertDialogDropdown,
isError: isError,
languages: _languages,
languageDropdownValue: _languageDropdownValue,
);
});
},
);
}),
),
);
}
String alertDialogDropdown(String newValue) {
setState(() {
_languageDropdownValue = newValue;
});
return newValue;
}
alertDialogCallback() {
if (_languageDropdownValue == null || _languageDropdownValue.isEmpty) {
setState(() {
isError = true;
});
} else {
setState(() {
isError = false;
startDownload();
});
}
}
void startDownload() async {
print('selected language is: $_languageDropdownValue');
Navigator.pop(context);
print('start download');
setState(() => _downloaded = true);
}
}
class DownloadScreen extends StatelessWidget {
DownloadScreen(
{@required this.callbackFunction,
@required this.dropDownFunction,
@required this.isError,
@required this.languages,
@required this.languageDropdownValue});
final Function callbackFunction;
final Function dropDownFunction;
final String languageDropdownValue;
final bool isError;
final List<Map<String, String>> languages;
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.fromLTRB(24, 24, 24, 14),
title: Text('Confirm purchase'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Please select the guide language:'),
Flexible(
child: DropdownButtonFormField(
isExpanded: false,
isDense: true,
dropdownColor: Colors.white,
value: languageDropdownValue,
hint: Text(
'Preferred Language',
style: TextStyle(color: Colors.grey),
),
items: languages.map((map) {
return DropdownMenuItem(
value: map['code'],
child: Text(
map['value'],
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: (String newValue) => dropDownFunction(newValue),
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
labelStyle: TextStyle(color: Colors.grey),
hintStyle: TextStyle(color: Colors.grey),
errorStyle: TextStyle(fontSize: 17.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
),
),
),
isError
? Center(
child: Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
'Please select a language',
style: TextStyle(
color: Colors.red,
),
),
),
)
: Container(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Text('Are you sure you want to purchase this audio guide?'),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
ElevatedButton(
onPressed: callbackFunction,
child: Text('Yes'),
),
SizedBox(
width: 40,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text('No'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
),
),
],
)
],
),
);
}
}
具有更多功能的解决方案(感谢 CbL)
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isLoading = false;
bool _downloaded = false;
File cardImage;
String _languageDropdownValue;
bool isError = false;
List<Map<String, String>> _languages = [
{'code': 'en', 'value': 'English'},
{'code': 'fr', 'value': 'French'},
];
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: _downloaded
? IconButton(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 0),
icon: Icon(
Icons.open_in_new,
size: 45.0,
color: Colors.green,
),
onPressed: () {
print('Open button pressed');
})
: _isLoading
? CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
)
: IconButton(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 0),
icon: Icon(
Icons.download_rounded,
size: 45.0,
color: Colors.green,
),
onPressed: () {
print('Download button pressed');
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setInnerState) {
return DownloadScreen(
callbackFunction: () =>
alertDialogCallback(setInnerState),
dropDownFunction: (value) =>
alertDialogDropdown(value, setInnerState),
isError: isError,
languages: _languages,
languageDropdownValue: _languageDropdownValue,
);
});
},
).then((value) => _languageDropdownValue = null);
}),
),
);
}
String alertDialogDropdown(String newValue, StateSetter setInnerState) {
setInnerState(() {
_languageDropdownValue = newValue;
isError = false;
});
return newValue;
}
alertDialogCallback(StateSetter setInnerState) {
if (_languageDropdownValue == null || _languageDropdownValue.isEmpty) {
setInnerState(() {
isError = true;
});
} else {
setInnerState(() {
isError = false;
startDownload();
});
}
}
void startDownload() async {
print('selected language is: $_languageDropdownValue');
Navigator.pop(context);
print('start download');
setState(() => _downloaded = true);
}
}
class DownloadScreen extends StatelessWidget {
DownloadScreen(
{@required this.callbackFunction,
@required this.dropDownFunction,
@required this.isError,
@required this.languages,
@required this.languageDropdownValue});
final Function callbackFunction;
final Function dropDownFunction;
final String languageDropdownValue;
final bool isError;
final List<Map<String, String>> languages;
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.fromLTRB(24, 24, 24, 14),
title: Text('Confirm purchase'),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Please select the guide language:'),
Flexible(
child: DropdownButtonFormField(
isExpanded: false,
isDense: true,
dropdownColor: Colors.white,
value: languageDropdownValue,
hint: Text(
'Preferred Language',
style: TextStyle(color: Colors.grey),
),
items: languages.map((map) {
return DropdownMenuItem(
value: map['code'],
child: Text(
map['value'],
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: (String newValue) => dropDownFunction(newValue),
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
labelStyle: TextStyle(color: Colors.grey),
hintStyle: TextStyle(color: Colors.grey),
errorStyle: TextStyle(fontSize: 17.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2),
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
),
),
),
isError
? Center(
child: Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
'Please select a language',
style: TextStyle(
color: Colors.red,
),
),
),
)
: Container(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Text('Are you sure you want to purchase this audio guide?'),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
ElevatedButton(
onPressed: callbackFunction,
child: Text('Yes'),
),
SizedBox(
width: 40,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: Text('No'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
),
),
],
)
],
),
);
}
}