问题描述
我使用 TabBarView
创建了一个基于滑块的步进表单,它在切换前验证输入。它有效,但是当我回去时,状态已重置。当我尝试收集选项卡末尾的数据时,这种行为导致我看到一个空表单。
我在谷歌上搜索了几个小时,并尝试将当前的 GetView<MyController>
切换到经典的 StatefulWidget with AutomaticKeepAliveMixin
,但没有成功,所以我将其还原。
我有点卡住了,我想知道是否还有其他方法可以实现这一点,如果可能的话,GetX 方法。
visual explanation
create_account_form_slider.dart
class CreateAccountFormSlider extends GetView<CreateAccountController> {
@override
Widget build(BuildContext context) {
return Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),controller: controller.tabController,children: [
_buildEmailForm(),_buildNameForm(),_buildPasswordForm(),],),);
}
Widget _buildEmailForm() {
return Form(
key: controller.emailFormKey,child: Column(
children: [
Spacer(),// Necessary to push the input to the bottom constraint,Align class doesn't work.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),child: FormInput(
focusNode: controller.emailFocusNode,margin: EdgeInsets.zero,label: 'create_account_form_email'.tr,hintText: 'janedoe@example.com',textInputAction: TextInputAction.next,keyboardType: TextInputType.emailAddress,validator: controller.emailValidator,onFieldSubmitted: (_) => controller.next(),);
}
... each form has similar structure (almost identical),so i will not include it here
create_account_controller.dart
class CreateAccountController extends GetxController
with SingleGetTickerProviderMixin {
final tabIndex = 0.obs;
final emailFormKey = GlobalKey<FormState>();
FormState get emailForm => emailFormKey.currentState;
final emailFocusNode = FocusNode();
final email = ''.obs;
TabController tabController;
@override
void onInit() {
_initTabController();
super.onInit();
}
@override
void onClose() {
_disposeFocusNodes();
_disposeTabController();
super.onClose();
}
/// Initialize tab controller and add a listener.
void _initTabController() {
tabController = TabController(vsync: this,length: 3);
tabController.addListener(_tabListener);
}
/// Listen on tab change and update `tabIndex`
void _tabListener() => tabIndex(tabController.index);
/// Dispose tab controller and remove its listener.
void _disposeTabController() {
tabController.removeListener(_tabListener);
tabController.dispose();
}
/// Dispose all the focus nodes.
void _disposeFocusNodes() {
emailFocusNode.dispose();
}
/// Animate to the next slide.
void _nextSlide() => tabController.animateTo(tabIndex() + 1);
/// Animate to the next slide or submit if current tab is the last tab.
void next() {
if (tabIndex().isEqual(0) && emailForm.validate()) {
_nextSlide();
return focusScope.requestFocus(nameFocusNode);
}
...
}
/// A function that checks the validity of the given value.
///
/// When the email is empty,show required error message and when the email
/// is invalid,show the invalid message.
String emailValidator(String val) {
if (val.isEmpty) return 'create_account_form_error_email_required'.tr;
if (!val.isEmail) return 'create_account_form_error_email_invalid'.tr;
return null;
}
/// Submit data to the server.
void _submit() {
print('TODO: implement submit');
print(email());
}
}
解决方法
我通过保存表单并在我的自定义 initialValue
小部件上添加一个 FormInput
然后将可观察变量放到每个相关的 FormInput
上来实现。无需使用 keepalive mixin。
create_account_controller.dart
/// Animate to the next slide or submit if current tab is the last tab.
void next() {
if (tabIndex().isEqual(0) && emailForm.validate()) {
// save the form so the value persisted into the .obs variable
emailForm.save();
// slide to next form
_nextSlide();
// TODO: wouldn't it be nice if we use autofocus since we only have one input each form?
return focusScope.requestFocus(nameFocusNode);
}
...
}
create_account_form_slider.dart
Obx( // wrap the input inside an Obx to rebuild with the new value
() => Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),child: FormInput(
focusNode: controller.emailFocusNode,label: 'create_account_form_email'.tr,hintText: 'janedoe@example.com',textInputAction: TextInputAction.next,keyboardType: TextInputType.emailAddress,validator: controller.emailValidator,onFieldSubmitted: (_) => controller.next(),initialValue: controller.email(),// use initial value to keep current value when user go back from the next slide
onSaved: controller.email,// persist current value into the .obs variable
),),
仅供参考:FormInput
只是一个普通的 TextInput
,只有 decoration
被修改。这应该适用于常规颤振 TextInput
。