与 GetX 包一起使用 keepalive mixin 是否有任何解决方法?
Is there any workaround for using keepalive mixin alongside the GetX package?
我使用 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());
}
}
我通过保存表单并在我的自定义 FormInput
小部件上添加一个 initialValue
然后将可观察变量放到每个相关的 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
.
如果你想像StatefulWidget一样在GetX中使用AutomaticKeepAliveMixin。您可以像这样在 Get.put 中添加参数 'permanent: true'
Get.put<HomeController>(
HomeController(),
permanent: true,
);
HomeBinding 的完整代码如下
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.put<HomeController>(
HomeController(),
permanent: true,
);
}
}
我使用 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());
}
}
我通过保存表单并在我的自定义 FormInput
小部件上添加一个 initialValue
然后将可观察变量放到每个相关的 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
.
如果你想像StatefulWidget一样在GetX中使用AutomaticKeepAliveMixin。您可以像这样在 Get.put 中添加参数 'permanent: true'
Get.put<HomeController>(
HomeController(),
permanent: true,
);
HomeBinding 的完整代码如下
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.put<HomeController>(
HomeController(),
permanent: true,
);
}
}