这就是我想要实现的目标:
表单以下拉菜单(DropdownButtonFormField)开始,根据用户的选择,会出现一组新的字段。 每个新字段本身可以有条件地触发更多字段。 所有字段(显示和隐藏)的状态都需要保留,直到提交表单。 我已经开始使用 Bloc 进行简单的实现,但我面临着在不重建整个表单的情况下被动更新状态的问题,这会导致输入丢失和糟糕的用户体验。
我首先使用 Bloc 状态管理方法来实现表单。我的目标是根据前面字段的用户输入来管理各种表单字段的可见性和验证状态。具体来说,我使用 Bloc 对状态变化做出反应,例如下拉菜单中的选择。
我预计,当用户从下拉列表中选择一个选项时,只有表单的相关部分会更新、添加或删除字段,而不会影响表单其他部分中已输入的数据。我们的想法是提供无缝的用户体验,其中字段可以根据输入动态出现和消失,而不会对其他表单输入造成干扰。
class FormBloc extends Bloc<FormEvent, FormState> {
FormBloc() : super(InitialFormState());
@override
Stream<FormState> mapEventToState(FormEvent event) async* {
if (event is FormEventSelectOption) {
yield FormStateWithSelectedOption(event.selectedOption);
} else if (event is FormEventAddField) {
yield FormStateWithAddedField(event.field);
}
}
}
在此示例中,FormBloc 响应两种类型的事件:FormEventSelectOption
和
FormEventAddField
。当用户从下拉菜单中选择一个选项时,会触发
FormEventSelectOption
事件;当向表单添加新字段时,会触发
FormEventAddField
事件。这是表单小部件的示例实现:
class FormWidget extends StatefulWidget {
@override
_FormWidgetState createState() => _FormWidgetState();
}
class _FormWidgetState extends State<FormWidget> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: BlocBuilder<FormBloc, FormState>(
builder: (context, state) {
return Column(
children: [
DropdownButtonFormField<String>(
onChanged: (value) {
context.read<FormBloc>().add(FormEventSelectOption(selectedOption: value));
},
items: [
DropdownMenuItem(
child: Text('Option 1'),
value: 'option1',
),
DropdownMenuItem(
child: Text('Option 2'),
value: 'option2',
),
],
),
if (state is FormStateWithSelectedOption)
// Display fields based on the selected option
Column(
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
// Add more fields as needed
],
),
if (state is FormStateWithAddedField)
// Display the added field
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
// Add more fields as needed
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// Form is valid, submit form data
}
},
child: Text('Submit'),
),
],
);
},
),
);
}
}
在此示例中,FormWidget 使用 BlocBuilder
根据 FormBloc 的当前状态构建表单。当用户从下拉菜单中选择一个选项时,
FormBloc
将其状态更新为
FormStateWithSelectedOption
,这会触发
BlocBuilder
重建表单并根据所选选项显示字段。要在用户浏览表单时保留表单的状态,您可以使用
InheritedWidget
来存储表单的当前状态。这是一个示例实现:
class FormProvider extends InheritedWidget {
final FormBloc formBloc;
const FormProvider({
Key? key,
required this.formBloc,
required Widget child,
}) : super(key: key, child: child);
static FormProvider of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FormProvider>()!;
}
@override
bool updateShouldNotify(FormProvider oldWidget) {
return formBloc != oldWidget.formBloc;
}
}
在此示例中,FormProvider
小部件存储
FormBloc
的当前状态并将其提供给小部件树的其余部分。您可以使用
of
方法从小部件树中的任何位置访问
FormBloc
的当前状态。通过使用
Bloc
和
InheritedWidget
的组合,您可以创建根据用户输入无缝更新的动态表单,而不会干扰其他表单输入。