#forms #flutter #popup #alert #flutter-form-builder
#формы #flutter #всплывающее окно #предупреждение #flutter-form-builder
Вопрос:
Я создаю форму, используя пакет «Конструктор форм Flutter» 4.0.2.
Большинство вопросов формы отображаются на экране, но последние два отображаются в последовательных * всплывающих окнах, созданных с помощью пакета предупреждений RFlutter 1.1.0
* Под «последовательным» я подразумеваю, что в нижней части главного экрана формы есть кнопка «Опубликовать» (см. Первое изображение сразу ниже). При нажатии появляется всплывающее окно с предупреждением RFlutter (см. 2-й снимок экрана ниже) с предпоследним вопросом FormBuilderRadioGroup («q11»). При нажатии кнопки «Далее» появляется еще одно всплывающее окно предупреждения RFlutter, в котором отображается последний вопрос FormBuilderRadioGroup («q12»). При нажатии кнопки «Завершить публикацию» отправляется вся форма конструктора форм Flutter.
Пользовательский интерфейс работает хорошо, но проблема в том, что ничего из вопросов во всплывающих окнах предупреждений (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=webpamp;format=pjpgamp;width=140
@override
void initState() {
//isSelected = [true, false];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
Продолжение (пропущенная панель приложений):
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 amp; 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,
),
),
),
],
),
),
Спасибо!
Ответ №1:
Пакет flutter form Builder автоматически извлекает форму-предок, чтобы добавить в них значения полей. Но поскольку поля вашей формы теперь находятся в диалоговом окне, они не гарантированно находятся внутри родительского макета, из которого был вызван диалог.
Ссылка из документации flutter (ссылка)
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,
),
),
],
),
),
),
А вот скриншот, на котором показаны различия между кодом to в вопросе (слева) и кодом, основанным на ответе (справа):
Еще одна вещь, которую можно сделать, — это изменить пакет form builder, чтобы он также принимал состояние, но он может сломаться, если вы обращаетесь к размонтированному состоянию.