如何使 "Flutter Form Builder" 从 RFlutter Alert 中的问题中识别提交的表单数据?
How to make "Flutter Form Builder" recognize submitted form data from questions in an RFlutter Alert?
我正在使用 "Flutter Form Builder" package 4.0.2 构建表单。
大多数表单问题都在屏幕上,但最后两个问题在使用 RFlutter Alert package 1.1.0
创建的连续*弹出窗口中
* “连续”是指主窗体屏幕底部有一个“Post”按钮(请参见下面的第一张图片)。按下时,会出现一个 RFlutter 警报弹出窗口(参见下面的第二个屏幕截图),以及倒数第二个 FormBuilderRadioGroup 问题(“q11”)。当按下“下一步”按钮时,会出现另一个 RFlutter 警报弹出窗口,其中会出现最后一个 FormBuilderRadioGroup 问题(“q12”)。当按下此处的“完成 Posting”按钮时,整个 Flutter Form Builder 表单将提交。
用户界面运行良好,但问题与警报弹出窗口(Q11 和 Q12)中的问题无关(请参阅下面的控制台快照 - q1 , q2 和 q3 也有问题,但这不是我在这里问的问题)。
这里是代码(跳过不相关的部分,为了长度):
// This is the stateful widget that the main application instantiates, per https://api.flutter.dev/flutter/widgets/Form-class.html
class SandboxWriteReviewScreen extends StatefulWidget {
// BEGIN code from material_tag_editor
final String title = 'Material Tag Editor Demo';
// END code from material_tag_editor
@override
_SandboxWriteReviewScreenState createState() =>
_SandboxWriteReviewScreenState();
}
// This is the private State class that goes with WriteReviewScreen
class _SandboxWriteReviewScreenState extends State<SandboxWriteReviewScreen> {
var data;
AutovalidateMode autovalidateMode = AutovalidateMode.always;
bool readOnly = false;
bool showSegmentedControl = true;
//final _newFormbuilderKey = GlobalKey<FormState>();
final _newnewFormbuilderKey = GlobalKey<FormBuilderState>();
// above "GlobalKey" lets us generate a unique, app-wide ID that we can associate with our form, per https://fluttercrashcourse.com/blog/realistic-forms-part1
final ValueChanged _onChanged = (val) => print(val);
// BEGIN related to FormBuilderTextField in form below
final _ageController = TextEditingController(text: '45');
bool _ageHasError = false;
// END related to FormBuilderTextField in form below
String qEleven;
String qTwelve;
// BEGIN code from material_tag_editor
List<String> qOne = [];
final FocusNode _focusNode = FocusNode();
onDelete(index) {
setState(() {
qOne.removeAt(index);
});
}
// below = reiteration for cons
List<String> qThree = [];
//final FocusNode _focusNode = FocusNode();
uponDelete(index) {
// NOTE: "uponDelete" for cons vs. "onDelete" for pros
setState(() {
qThree.removeAt(index);
});
}
// END code from material_tag_editor
//final _user = User();
List<bool> isSelected;
int starIconColor =
0xffFFB900; // was 0xffFFB900; 0xffD49428 is from this image: https://images.liveauctioneers.com/houses/logos/lg/bartonsauction550_large.jpg?auto=webp&format=pjpg&width=140
@override
void initState() {
//isSelected = [true, false];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
续(跳过 AppBar):
body: SingleChildScrollView(
child: Container(
child: Builder(
builder: (context) => FormBuilder(
// was "builder: (context) => Form("
key: _newnewFormbuilderKey,
initialValue: {
'date': DateTime.now(),
},
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 12.0,
),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q1 via TagEditor', // was 'What are 3 good or positive things about the house, property or neighborhood?', // [ 1 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
// BEGIN code from material_tag_editor
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: TagEditor(
length: qOne.length,
delimiters: [
','
], // was delimiters: [',', ' '], Also tried "return" ('\u2386',) and '\u{2386}'
hasAddButton: true,
textInputAction: TextInputAction
.next, // moves user from one field to the next!!!!
autofocus: false,
maxLines: 1,
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.lightBlue),
// borderRadius: BorderRadius.circular(20.0),
// ),
inputDecoration: const InputDecoration(
// below was "border: InputBorder.none,"
isDense: true,
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlue),
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
// above is per https://github.com/flutter/flutter/issues/5191
),
labelText: 'separate, with, commas',
labelStyle: TextStyle(
fontStyle: FontStyle.italic,
backgroundColor:
Color(0x65dffd02), // was Color(0xffDDFDFC),
color: Colors.black87, // was Color(0xffD82E6D),
fontSize: 14,
),
),
onTagChanged: (value) {
setState(() {
qOne.add(value);
});
},
tagBuilder: (context, index) => _Chip(
index: index,
label: qOne[index],
onDeleted: onDelete,
),
),
),
// END code from material_tag_editor
SuperDivider(),
续(跳过 Q2 - Q9):
SuperDivider(),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q10 - via FormBuilder\'s FormBuilderTextField', // [ 9 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
GavTextField(
maxCharLength: 1200,
fieldAttribute: 'qTen',
fieldLabelText:
'Be honest & kind.', // was 'Be honest, but kind.',
),
SuperDivider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: EdgeInsets.symmetric(
horizontal: 50, vertical: 20),
textStyle: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
onPressed: () {
Alert(
context: context,
style: alertStyle,
title: 'Two quick things...',
desc: ' ',
content: Column(
children: <Widget>[
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q11 - via FormBuilder\'s FormBuilderRadioGroup', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
],
),
),
FormBuilderRadioGroup(
name: 'qEleven',
decoration: const InputDecoration(
border: InputBorder.none,
),
orientation: OptionsOrientation.vertical,
onChanged: _onChanged,
options: [
FormBuilderFieldOption(
value: 'N',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'No', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
FormBuilderFieldOption(
value: 'YesSometimes',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text:
'Yes, sometimes', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
FormBuilderFieldOption(
value: 'YesDaily',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'Yes, daily', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
],
),
],
),
buttons: [
DialogButton(
onPressed: () {
Alert(
context: context,
style: alertStyle,
title: 'And lastly:',
desc: '',
content: Column(
children: <Widget>[
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
' Q12 - via FormBuilder\'s', // [ 10 ]
最后(跳过 Q12):
buttons: [
DialogButton(
// BEGIN submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M
onPressed: () {
_newnewFormbuilderKey
.currentState
.save();
if (_newnewFormbuilderKey
.currentState
.validate()) {
print(_newnewFormbuilderKey
.currentState.value);
print(
' >>> Q1\'s value via separate print: {$qOne}',
);
print(
' >>> Q3\'s value via separate print: {$qThree}',
);
print(
' >>> Q11\'s value via separate print: {$qEleven}',
);
print(
' >>> Q12\'s value via separate print: {$qTwelve}',
);
//print(_newFormbuilderKey.currentState.privacyChoice.value);
} else {
print("validation failed");
}
},
// END submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M
child: Text(
"Finish Posting",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
)
]).show();
},
child: Text(
"Next >",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
)
],
).show();
},
child: Text(
'Post',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
],
),
),
谢谢!
flutter 表单生成器包自动获取祖先表单以在它们中添加字段值。但是由于您的表单字段现在位于对话框中,因此不能保证它们位于调用对话框的父布局内。
参考 flutter 文档 (Link)
This function takes a builder which typically builds a Dialog widget.
Content below the dialog is dimmed with a ModalBarrier. The widget returned by the
builder does not share a context with the location that showDialog is originally
called from. Use a StatefulBuilder or a custom StatefulWidget if the dialog needs
to update dynamically.
您可以改为将这些字段的值保存在有状态小部件的状态中,它们稍后会合并所有值以用于将来的操作。由于您已经将它们保存在变量 qEleven
和 qTwelve
中,您可以稍后使用它。
这是为 qEleven 执行此操作的代码片段(与上述问题中的这部分代码相比):
FormBuilderRadioGroup(
name: 'qEleven',
decoration: const InputDecoration(
border: InputBorder.none,
),
orientation: OptionsOrientation.vertical,
onChanged: (val) {
print(val);
qEleven = val;
},
options: [
FormBuilderFieldOption(
value: 'N',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'No', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
这是一张截图,突出显示了问题中的代码(左)和基于答案的代码(右)之间的区别:
可以做的另一件事是修改表单生成器包以也接受状态,但如果您正在访问未安装的状态,它可能会中断。
我正在使用 "Flutter Form Builder" package 4.0.2 构建表单。
大多数表单问题都在屏幕上,但最后两个问题在使用 RFlutter Alert package 1.1.0
创建的连续*弹出窗口中* “连续”是指主窗体屏幕底部有一个“Post”按钮(请参见下面的第一张图片)。按下时,会出现一个 RFlutter 警报弹出窗口(参见下面的第二个屏幕截图),以及倒数第二个 FormBuilderRadioGroup 问题(“q11”)。当按下“下一步”按钮时,会出现另一个 RFlutter 警报弹出窗口,其中会出现最后一个 FormBuilderRadioGroup 问题(“q12”)。当按下此处的“完成 Posting”按钮时,整个 Flutter Form Builder 表单将提交。
用户界面运行良好,但问题与警报弹出窗口(Q11 和 Q12)中的问题无关(请参阅下面的控制台快照 - q1 , q2 和 q3 也有问题,但这不是我在这里问的问题)。
这里是代码(跳过不相关的部分,为了长度):
// This is the stateful widget that the main application instantiates, per https://api.flutter.dev/flutter/widgets/Form-class.html
class SandboxWriteReviewScreen extends StatefulWidget {
// BEGIN code from material_tag_editor
final String title = 'Material Tag Editor Demo';
// END code from material_tag_editor
@override
_SandboxWriteReviewScreenState createState() =>
_SandboxWriteReviewScreenState();
}
// This is the private State class that goes with WriteReviewScreen
class _SandboxWriteReviewScreenState extends State<SandboxWriteReviewScreen> {
var data;
AutovalidateMode autovalidateMode = AutovalidateMode.always;
bool readOnly = false;
bool showSegmentedControl = true;
//final _newFormbuilderKey = GlobalKey<FormState>();
final _newnewFormbuilderKey = GlobalKey<FormBuilderState>();
// above "GlobalKey" lets us generate a unique, app-wide ID that we can associate with our form, per https://fluttercrashcourse.com/blog/realistic-forms-part1
final ValueChanged _onChanged = (val) => print(val);
// BEGIN related to FormBuilderTextField in form below
final _ageController = TextEditingController(text: '45');
bool _ageHasError = false;
// END related to FormBuilderTextField in form below
String qEleven;
String qTwelve;
// BEGIN code from material_tag_editor
List<String> qOne = [];
final FocusNode _focusNode = FocusNode();
onDelete(index) {
setState(() {
qOne.removeAt(index);
});
}
// below = reiteration for cons
List<String> qThree = [];
//final FocusNode _focusNode = FocusNode();
uponDelete(index) {
// NOTE: "uponDelete" for cons vs. "onDelete" for pros
setState(() {
qThree.removeAt(index);
});
}
// END code from material_tag_editor
//final _user = User();
List<bool> isSelected;
int starIconColor =
0xffFFB900; // was 0xffFFB900; 0xffD49428 is from this image: https://images.liveauctioneers.com/houses/logos/lg/bartonsauction550_large.jpg?auto=webp&format=pjpg&width=140
@override
void initState() {
//isSelected = [true, false];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
续(跳过 AppBar):
body: SingleChildScrollView(
child: Container(
child: Builder(
builder: (context) => FormBuilder(
// was "builder: (context) => Form("
key: _newnewFormbuilderKey,
initialValue: {
'date': DateTime.now(),
},
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 12.0,
),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q1 via TagEditor', // was 'What are 3 good or positive things about the house, property or neighborhood?', // [ 1 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
// BEGIN code from material_tag_editor
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: TagEditor(
length: qOne.length,
delimiters: [
','
], // was delimiters: [',', ' '], Also tried "return" ('\u2386',) and '\u{2386}'
hasAddButton: true,
textInputAction: TextInputAction
.next, // moves user from one field to the next!!!!
autofocus: false,
maxLines: 1,
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.lightBlue),
// borderRadius: BorderRadius.circular(20.0),
// ),
inputDecoration: const InputDecoration(
// below was "border: InputBorder.none,"
isDense: true,
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlue),
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
// above is per https://github.com/flutter/flutter/issues/5191
),
labelText: 'separate, with, commas',
labelStyle: TextStyle(
fontStyle: FontStyle.italic,
backgroundColor:
Color(0x65dffd02), // was Color(0xffDDFDFC),
color: Colors.black87, // was Color(0xffD82E6D),
fontSize: 14,
),
),
onTagChanged: (value) {
setState(() {
qOne.add(value);
});
},
tagBuilder: (context, index) => _Chip(
index: index,
label: qOne[index],
onDeleted: onDelete,
),
),
),
// END code from material_tag_editor
SuperDivider(),
续(跳过 Q2 - Q9):
SuperDivider(),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q10 - via FormBuilder\'s FormBuilderTextField', // [ 9 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
GavTextField(
maxCharLength: 1200,
fieldAttribute: 'qTen',
fieldLabelText:
'Be honest & kind.', // was 'Be honest, but kind.',
),
SuperDivider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: EdgeInsets.symmetric(
horizontal: 50, vertical: 20),
textStyle: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
onPressed: () {
Alert(
context: context,
style: alertStyle,
title: 'Two quick things...',
desc: ' ',
content: Column(
children: <Widget>[
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q11 - via FormBuilder\'s FormBuilderRadioGroup', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
],
),
),
FormBuilderRadioGroup(
name: 'qEleven',
decoration: const InputDecoration(
border: InputBorder.none,
),
orientation: OptionsOrientation.vertical,
onChanged: _onChanged,
options: [
FormBuilderFieldOption(
value: 'N',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'No', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
FormBuilderFieldOption(
value: 'YesSometimes',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text:
'Yes, sometimes', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
FormBuilderFieldOption(
value: 'YesDaily',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'Yes, daily', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
],
),
],
),
buttons: [
DialogButton(
onPressed: () {
Alert(
context: context,
style: alertStyle,
title: 'And lastly:',
desc: '',
content: Column(
children: <Widget>[
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
' Q12 - via FormBuilder\'s', // [ 10 ]
最后(跳过 Q12):
buttons: [
DialogButton(
// BEGIN submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M
onPressed: () {
_newnewFormbuilderKey
.currentState
.save();
if (_newnewFormbuilderKey
.currentState
.validate()) {
print(_newnewFormbuilderKey
.currentState.value);
print(
' >>> Q1\'s value via separate print: {$qOne}',
);
print(
' >>> Q3\'s value via separate print: {$qThree}',
);
print(
' >>> Q11\'s value via separate print: {$qEleven}',
);
print(
' >>> Q12\'s value via separate print: {$qTwelve}',
);
//print(_newFormbuilderKey.currentState.privacyChoice.value);
} else {
print("validation failed");
}
},
// END submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M
child: Text(
"Finish Posting",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
)
]).show();
},
child: Text(
"Next >",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
)
],
).show();
},
child: Text(
'Post',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
],
),
),
谢谢!
flutter 表单生成器包自动获取祖先表单以在它们中添加字段值。但是由于您的表单字段现在位于对话框中,因此不能保证它们位于调用对话框的父布局内。
参考 flutter 文档 (Link)
This function takes a builder which typically builds a Dialog widget.
Content below the dialog is dimmed with a ModalBarrier. The widget returned by the
builder does not share a context with the location that showDialog is originally
called from. Use a StatefulBuilder or a custom StatefulWidget if the dialog needs
to update dynamically.
您可以改为将这些字段的值保存在有状态小部件的状态中,它们稍后会合并所有值以用于将来的操作。由于您已经将它们保存在变量 qEleven
和 qTwelve
中,您可以稍后使用它。
这是为 qEleven 执行此操作的代码片段(与上述问题中的这部分代码相比):
FormBuilderRadioGroup(
name: 'qEleven',
decoration: const InputDecoration(
border: InputBorder.none,
),
orientation: OptionsOrientation.vertical,
onChanged: (val) {
print(val);
qEleven = val;
},
options: [
FormBuilderFieldOption(
value: 'N',
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.black,
),
children: <TextSpan>[
TextSpan(
text: 'No', // [ 10 ]
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
),
],
),
),
),
这是一张截图,突出显示了问题中的代码(左)和基于答案的代码(右)之间的区别:
可以做的另一件事是修改表单生成器包以也接受状态,但如果您正在访问未安装的状态,它可能会中断。