首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >textFormField/TextField问题在聚焦时触发不必要的重建

textFormField/TextField问题在聚焦时触发不必要的重建
EN

Stack Overflow用户
提问于 2022-10-09 06:53:16
回答 1查看 33关注 0票数 1

在焦点上,我的textFormFields或textFields触发了不必要的重建,尖峰资源耗尽,使模拟器和计算机变慢,甚至无法使用。我对此做了大量的研究。这似乎是一个相当大的问题&我一直未能解决它。

我尝试过的:

  • 使表单键成为静态的决赛 => static final GlobalKey<FormState> _signInFormKey = GlobalKey<FormState>();
  • 尺寸使用斯泽尔而不是MediaQuery
  • 使用来自ConsumerWidget、StatefulWidget、StatelessWidget和ConsumerStatefulWidget的调用字段

到目前为止,一切都不起作用。

我做错什么了吗?

或者这是热烈的状态管理问题的颤栗寿命--大量的X60 the刷新资源流失?

以下是表单字段:

代码语言:javascript
运行
复制
class CustomFormField extends StatefulWidget {
  const CustomFormField({
    Key? key,
    required TextEditingController controller,
    required bool prefixCurrencySymbol,
    required String? currencySymbol,
    required List<String> autoFillHints,
    required bool blockSystemKeyboard,
    required double width,
    required FocusNode focusNode,
    required TextInputType keyboardType,
    required TextInputAction inputAction,
    required String label,
    required TextCapitalization textCapitalization,
    required Function(String value) validator,
    required bool obscurePasswordOption,
    required bool saveAutofillData,
    required bool passwordCreationField,
    required Future<void> Function(String?)? onChanged,
  })  : _formController = controller,
        _prefixCurrencySymbol = prefixCurrencySymbol,
        _currencySymbol = currencySymbol,
        _autoFillHints = autoFillHints,
        _blockSystemKeyboard = blockSystemKeyboard,
        _width = width,
        _formFocusNode = focusNode,
        _keyboardType = keyboardType,
        _inputAction = inputAction,
        _textCapitalization = textCapitalization,
        _label = label,
        _validator = validator,
        _obscurePasswordOption = obscurePasswordOption,
        _saveAutofillData = saveAutofillData,
        _passwordCreationField = passwordCreationField,
        _onChanged = onChanged,
        super(key: key);

  final TextEditingController _formController;
  final bool _prefixCurrencySymbol;
  final String? _currencySymbol;
  final List<String> _autoFillHints;
  final bool _blockSystemKeyboard;
  final double _width;
  final FocusNode _formFocusNode;
  final TextInputType _keyboardType;
  final TextInputAction _inputAction;
  final String _label;
  final bool _obscurePasswordOption;
  final TextCapitalization _textCapitalization;
  final Function(String) _validator;
  final bool _saveAutofillData;
  final bool _passwordCreationField;
  final Future<void> Function(String?)? _onChanged;

  @override
  State<CustomFormField> createState() => _CustomFormFieldState();
}

class _CustomFormFieldState extends State<CustomFormField> {
  bool _obscurePassword = true;

  @override
  void dispose() {
    widget._formFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: widget._width,
      child: TextFormField(
        style: ThemeEndpoints.textFieldTextStyle(),
        autofillHints: widget._autoFillHints,
        enableInteractiveSelection: true,
        enableSuggestions: false,
        autocorrect: false,
        textAlignVertical: const TextAlignVertical(y: 0.1),
        readOnly: widget._blockSystemKeyboard,
        maxLines: 1,
        controller: widget._formController,
        focusNode: widget._formFocusNode,
        keyboardType: widget._keyboardType,
        obscureText: (widget._obscurePasswordOption) ? _obscurePassword : false,
        textCapitalization: widget._textCapitalization,
        textInputAction: widget._inputAction,
        validator: (text) => widget._validator(text!),
        onChanged: (text) => (widget._passwordCreationField && text.isNotEmpty)
            ? widget._onChanged!(text)
            : null,
        onEditingComplete:
            (widget._saveAutofillData) ? () => TextInput.finishAutofillContext() : null,
        toolbarOptions: const ToolbarOptions(
          copy: true,
          paste: true,
          cut: true,
          selectAll: true,
        ),
        decoration: InputDecoration(
          filled: true,
          fillColor: ThemeEndpoints.textFieldBackgroundColor(),
          alignLabelWithHint: true,
          labelText: widget._label,
          labelStyle: ThemeEndpoints.textFieldLabelStyle(),
          contentPadding: const EdgeInsets.fromLTRB(32, 24, 12, 16),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(30.0),
            borderSide: const BorderSide(
              width: 0,
              style: BorderStyle.none,
            ),
          ),
          errorStyle: ThemeEndpoints.textFieldErrorTextStyle(),
          errorBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(30.0),
            borderSide: const BorderSide(
              color: BrandColors.appLightRed,
              width: 2,
            ),
          ),
          focusedErrorBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(30.0),
            borderSide: const BorderSide(
              color: BrandColors.appRed,
              width: 2,
            ),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.circular(30.0),
            borderSide: const BorderSide(
              color: BrandColors.luckyLime,
              width: 2,
            ),
          ),
          prefixIconConstraints: (widget._prefixCurrencySymbol)
              ? const BoxConstraints(minWidth: 0, minHeight: 0)
              : null,
          prefixIcon: (widget._prefixCurrencySymbol)
              ? Container(
                  margin: const EdgeInsets.all(13),
                  child: Text(
                    widget._currencySymbol!,
                    style: ThemeEndpoints.textFieldCurrencySymbolTextStyle(),
                  ),
                )
              : null,
          suffixIcon: (widget._obscurePasswordOption)
              ? GestureDetector(
                  onTap: () {
                    setState(() {
                      _obscurePassword = !_obscurePassword;
                    });
                  },
                  child: Container(
                    margin: const EdgeInsets.all(13),
                    child: _obscurePassword
                        ? ThemeEndpoints.textFieldPasswordNotVisible()
                        : ThemeEndpoints.textFieldPasswordVisible(),
                  ),
                )
              : null,
        ),
      ),
    );
  }
}

这是我的表格:

代码语言:javascript
运行
复制
class SignInForm extends ConsumerWidget {
  final FocusNode emailFocusNode;
  final FocusNode passwordFocusNode;

  SignInForm({
    Key? key,
    required this.emailFocusNode,
    required this.passwordFocusNode,
  }) : super(key: key);

  final TextEditingController _email = TextEditingController();
  final TextEditingController _password = TextEditingController();
  static final GlobalKey<FormState> _signInFormKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return AutofillGroup(
      child: Form(
        key: _signInFormKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              TextContent.of(context).authSignInText,
              style: ThemeEndpoints.primaryHeader(),
            ),
            const SizedBox(height: 16.0),
            CustomFormField(
              controller: _email,
              onChanged: null,
              prefixCurrencySymbol: false,
              currencySymbol: null,
              autoFillHints: const [AutofillHints.email],
              blockSystemKeyboard: false,
              width: 90.w,
              focusNode: emailFocusNode,
              keyboardType: TextInputType.emailAddress,
              inputAction: TextInputAction.next,
              label: TextContent.of(context).authFormEmailText,
              textCapitalization: TextCapitalization.none,
              validator: (email) => FormValidationUtility.emailFormValidator(
                context,
                email,
              ),
              obscurePasswordOption: false,
              saveAutofillData: false,
              passwordCreationField: false,
            ),
            const SizedBox(height: 16.0),
            CustomFormField(
              controller: _password,
              onChanged: null,
              prefixCurrencySymbol: false,
              currencySymbol: null,
              autoFillHints: const [AutofillHints.password],
              blockSystemKeyboard: false,
              width: 90.w,
              focusNode: passwordFocusNode,
              keyboardType: TextInputType.visiblePassword,
              inputAction: TextInputAction.done,
              label: TextContent.of(context).authFormPasswordText,
              textCapitalization: TextCapitalization.none,
              validator: (password) => FormValidationUtility.passwordGeneralValidator(
                context,
                password,
              ),
              obscurePasswordOption: true,
              saveAutofillData: false,
              passwordCreationField: false,
            ),
            const SizedBox(height: 64.0),
            CustomButton(
              buttonText: TextContent.of(context).authSignInText,
              width: 50.w,
              onPressed: () async => _onSignInPressed(
                context,
                ref,
                [emailFocusNode, passwordFocusNode],
              ),
            ),
            const SizedBox(height: 16.0),
            const SignUpInkwell(),
            const SizedBox(height: 16.0),
            const SignInOptionsPulldown(),
          ],
        ),
      ),
    );
  }

  // On pressed button execution
  Future<void> _onSignInPressed(
    BuildContext context,
    WidgetRef ref,
    List<FocusNode> focusNodes,
  ) async {
    for (FocusNode node in focusNodes) {
      node.unfocus();
    }
    if (_signInFormKey.currentState!.validate()) {
      await ref.read(authenticationEndpoints).signIn(
            context: context,
            email: _email.text.trim(),
            password: _password.text.trim(),
          );
    }
  }
}
EN

回答 1

Stack Overflow用户

发布于 2022-10-09 07:40:02

上个星期我刚刚面对这个问题,发现在github问题上有很长时间的讨论。但是我忘了标记这个问题,所以我不能把它提交给你。

有两点我明白:

  • 每次触发焦点,键盘就会出现在屏幕上。这将触发屏幕重建。因为屏幕上的小部件的数量发生了变化
  • 有颤振的行为,当我们有一个嵌套的StatefullWidget。这里的问题与此相关:https://stackoverflow.com/a/50584602/12838877

在您的例子中,您有父状态小部件和一些CustomTextField,这是一个状态小部件。

上周我的问题解决方案:

  1. 为了避免重新生成不必要的小部件--每个焦点都更改了--我尽量减少对所有小部件使用本地方法。我选择使用新的StatelessWidgetStatefullWidget。这将不会重建整个小部件树。
  2. 然后,对于第二个问题,我的解决方案是针对扩展到StatefullWidget的每个组件分配特定的valueKey。即使当我调用setState并重新执行build方法时,由于我的valueKey是相同的,小部件也不会重新构建。

就像下面

代码语言:javascript
运行
复制
 CustomFormField(
   key: ValueKey('emailform'), 
   controller: _email,
   onChanged: null,
   ....),
CustomFormField(
   key: ValueKey('pswdForm'), 
   controller: pswd`,
   onChanged: null,
  ....)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74002668

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档