如何在 Flutter 中使用条件逻辑高效管理动态表单的状态?

问题描述 投票:0回答:1
我正在构建一个 Flutter 应用程序,我需要创建根据用户输入而变化的动态表单。例如,根据先前字段中的选择,某些字段应该变得可见或必需。我研究了各种状态管理解决方案,例如 Provider、Bloc 和 Riverpod,但我不确定如何有效地实现这一点,尤其是需要保留表单状态并动态验证时。

这就是我想要实现的目标:

表单以下拉菜单(DropdownButtonFormField)开始,根据用户的选择,会出现一组新的字段。 每个新字段本身可以有条件地触发更多字段。 所有字段(显示和隐藏)的状态都需要保留,直到提交表单。 我已经开始使用 Bloc 进行简单的实现,但我面临着在不重建整个表单的情况下被动更新状态的问题,这会导致输入丢失和糟糕的用户体验。

我首先使用 Bloc 状态管理方法来实现表单。我的目标是根据前面字段的用户输入来管理各种表单字段的可见性和验证状态。具体来说,我使用 Bloc 对状态变化做出反应,例如下拉菜单中的选择。

我预计,当用户从下拉列表中选择一个选项时,只有表单的相关部分会更新、添加或删除字段,而不会影响表单其他部分中已输入的数据。我们的想法是提供无缝的用户体验,其中字段可以根据输入动态出现和消失,而不会对其他表单输入造成干扰。

flutter flutter-dependencies hybrid-mobile-app bloc mobile-application
1个回答
0
投票
为了实现您所描述的动态表单行为,我建议结合使用 Bloc 进行状态管理和 InheritedWidget 来保留表单状态。您可以采取以下一般方法:

  1. 创建一个FormBloc,管理整个表单的状态,包括每个表单字段的可见性和验证状态。
  2. 使用 InheritedWidget 在用户浏览表单时保留表单的状态。这将使您避免在更新单个字段时重建整个表单。
  3. 使用 BlocBuilder 对 FormBloc 中的状态更改做出反应,并更新每个表单字段的可见性和验证状态。
  4. 结合使用 BlocListener 和 BlocBuilder 来处理表单验证和提交。 BlocListener 可以监听验证状态的变化并根据需要显示错误消息,而 BlocBuilder 可以根据验证状态更新每个表单字段的可见性状态。
  5. 使用 Form 小部件来处理表单提交和验证。这将使您可以在用户提交表单时轻松验证所有表单字段。
这是 FormBloc 的示例实现:

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
 的组合,您可以创建根据用户输入无缝更新的动态表单,而不会干扰其他表单输入。

© www.soinside.com 2019 - 2024. All rights reserved.