【问题标题】:Flutter Bloc does not change TextFormField initialValueFlutter Bloc 不会改变 TextFormField 的初始值
【发布时间】:2020-12-01 16:29:48
【问题描述】:

我正在使用 Bloc 库,并注意到在产生新状态后我的 TextFormField initialValue 没有改变。

我的应用比这更复杂,但我做了一个最小的例子。还跟踪它在推送事件后正在更改的状态。

Bloc 应该正确地重建整个小部件。我错过了什么吗?

import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:developer' as developer;

void main() {
  runApp(MyApp());
}

enum Event { first }

class ExampleBloc extends Bloc<Event, int> {
  ExampleBloc() : super(0);
  @override
  Stream<int> mapEventToState(Event event) async* {
    yield state + 1;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (_) => ExampleBloc(),
        child: Builder(
          builder: (contex) => SafeArea(
            child: BlocConsumer<ExampleBloc, int>(
                listener: (context, state) {},
                builder: (context, int state) {
                  developer.log(state.toString());
                  return Scaffold(
                    body: Form(
                      child: Column(
                        children: [
                          TextFormField(
                            autocorrect: false,
                            initialValue: state.toString(),
                          ),
                          RaisedButton(
                            child: Text('Press'),
                            onPressed: () {
                              context.bloc<ExampleBloc>().add(Event.first);
                            },
                          )
                        ],
                      ),
                    ),
                  );
                }),
          ),
        ),
      ),
    );
  }
}

pubspec.yaml

name: form
description: A new Flutter project.
version: 1.0.0+1
environment:
  sdk: ">=2.7.0 <3.0.0"
dependencies:
  flutter:
    sdk: flutter
  bloc: ^6.0.0
  flutter_bloc: ^6.0.0

编辑
正如@chunhunghan 所指出的,添加一个 UniqueKey 可以解决这个问题。我也应该提到我的情况。该应用程序从两个TextFormFieldonChanged 方法发出事件。这会导致窗体重置并删除键盘。自动对焦不起作用,因为有两个 TextFormField wgich 发出事件。

【问题讨论】:

  • 只是快速提醒一下,钥匙很贵。当只需要更改状态时,您应该小心不要重建整个小部件。

标签: flutter bloc flutter-bloc


【解决方案1】:

您可以在下面复制粘贴运行完整代码 1 和 2
您可以将UniqueKey() 提供给ScaffoldTextFormField 以强制重新创建
详情可以参考https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d

如果 Element 的 key 与对应的 Widget 的 key 不匹配。这会导致 Flutter 停用这些元素并删除对元素树中元素的引用

解决方案 1:

return Scaffold(
        key: UniqueKey(),
        body: Form(

解决方案 2:

TextFormField(
               key: UniqueKey(),

工作演示

完整代码 1 ScaffoldUniqueKey

import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:developer' as developer;

void main() {
  runApp(MyApp());
}

enum Event { first }

class ExampleBloc extends Bloc<Event, int> {
  ExampleBloc() : super(0);
  @override
  Stream<int> mapEventToState(Event event) async* {
    yield state + 1;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("build");
    return MaterialApp(
      home: BlocProvider(
        create: (_) => ExampleBloc(),
        child: Builder(
          builder: (contex) => SafeArea(
            child: BlocConsumer<ExampleBloc, int>(
                listener: (context, state) {},
                builder: (context, int state) {
                  print("state ${state.toString()}");
                  developer.log(state.toString());
                  return Scaffold(
                    key: UniqueKey(),
                    body: Form(
                      child: Column(
                        children: [
                          TextFormField(
                            autocorrect: false,
                            initialValue: state.toString(),
                          ),
                          RaisedButton(
                            child: Text('Press'),
                            onPressed: () {
                              context.bloc<ExampleBloc>().add(Event.first);
                            },
                          )
                        ],
                      ),
                    ),
                  );
                }),
          ),
        ),
      ),
    );
  }
}

完整代码 2 TextFormFieldUniqueKey

import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:developer' as developer;

void main() {
  runApp(MyApp());
}

enum Event { first }

class ExampleBloc extends Bloc<Event, int> {
  ExampleBloc() : super(0);
  @override
  Stream<int> mapEventToState(Event event) async* {
    yield state + 1;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print("build");
    return MaterialApp(
      home: BlocProvider(
        create: (_) => ExampleBloc(),
        child: Builder(
          builder: (contex) => SafeArea(
            child: BlocConsumer<ExampleBloc, int>(
                listener: (context, state) {},
                builder: (context, int state) {
                  print("state ${state.toString()}");
                  developer.log(state.toString());
                  return Scaffold(
                    body: Form(
                      child: Column(
                        children: [
                          TextFormField(
                            key: UniqueKey(),
                            autocorrect: false,
                            initialValue: state.toString(),
                          ),
                          RaisedButton(
                            child: Text('Press'),
                            onPressed: () {
                              context.bloc<ExampleBloc>().add(Event.first);
                            },
                          )
                        ],
                      ),
                    ),
                  );
                }),
          ),
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案2】:

    您不应该仅仅因为要更新TextFormField 的值而重建整个Form,请尝试使用TextEditingController 并更新侦听器上的值。

    TextEditingController _controller = TextEditingController();
    BlocProvider(
        create: (_) => ExampleBloc(),
        child: Builder(
          builder: (contex) => SafeArea(
            child: BlocListener<ExampleBloc, int>(
                listener: (context, state) {
                    _controller.text = state.toString();
                },
                child: Scaffold(
                    body: Form(
                      child: Column(
                        children: [
                          TextFormField(
                            controller: _controller,
                            autocorrect: false,
                          ),
                          RaisedButton(
                            child: Text('Press'),
                            onPressed: () {
                              context.bloc<ExampleBloc>().add(Event.first);
                            },
                          )
                        ],
                      ),
                    ),
                  );
                }),
    

    【讨论】:

    • 这段代码有效吗?它对我来说失败了,因为你不能有初始值和控制器。
    • 我不认为这段代码会起作用,因为在颤振中我们不能同时给出初始值和控制器。
    【解决方案3】:

    我也遇到了同样的问题。在添加Unique Key 时,颤动会不断构建小部件,并且我的键盘每次都失焦。我解决它的方法是在TextField的onChanged事件中添加去抖动。

    class InputTextWidget extends StatelessWidget {
    final Function(String) onChanged;
    Timer _debounce;
    
    
    void _onSearchChanged(String value) {
        if (_debounce?.isActive ?? false) _debounce.cancel();
        _debounce = Timer(const Duration(milliseconds: 2000), () {
          onChanged(value);
        });
      }
    
    @override
      Widget build(BuildContext context) {
             return TextFormField(
              controller: TextEditingController(text: value)
                ..selection = TextSelection.fromPosition(
                  TextPosition(offset: value.length),
                ),
              onChanged: _onSearchChanged,
              onEditingComplete: onEditingCompleted,
            );
           }
         }
    

    希望这对使用表单、块和更新表单的人有所帮助。

    编辑:虽然添加了去抖动帮助显示了什么。我已将代码更改为更健壮。这是变化。

    InputTextWidget(已更改)

    class InputTextWidget extends StatelessWidget {
    final Function(String) onChanged;
    final TextEditingController controller;
    
    
    void _onSearchChanged(String value) {
        if (_debounce?.isActive ?? false) _debounce.cancel();
        _debounce = Timer(const Duration(milliseconds: 2000), () {
          onChanged(value);
        });
      }
    
    @override
      Widget build(BuildContext context) {
             return TextFormField(
              controller: controller,
              onChanged: _onSearchChanged,
              onEditingComplete: onEditingCompleted,
            );
           }
         }
    

    在我的演讲结束时

    class _NameField extends StatelessWidget {
      const _NameField({
        Key key,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final TextEditingController _controller = TextEditingController();
        return BlocConsumer<SomeBloc,
            SomeState>(
          listenWhen: (previous, current) =>
              previous.name != current.name,
          listener: (context, state) {
            final TextSelection previousSelection = _controller.selection;
            _controller.text = state.name;
            _controller.selection = previousSelection;
          },
          buildWhen: (previous, current) =>
              previous.name != current.name,
          builder: (context, state) => FormFieldDecoration(
            title: "Name",
            child: InputTextWidget(
              hintText: "AWS Certification",
              textInputType: TextInputType.name,
              controller: _controller,
              onChanged: (value) => context
                  .read< SomeBloc >()
                  .add(SomeEvent(
                      value)),
            ),
          ),
        );
      }
    }
    

    此编辑运行良好。

    最终编辑:

    我在我的 bloc 状态上添加了一个 key? key 并将此密钥传递给小部件。如果我需要再次重绘表单,我将事件中的密钥更改为UniqueKey。这是迄今为止我一起实现 bloc 和 form 的最简单的方法。如果需要解释,请在这里评论,我稍后会补充。

    【讨论】:

      猜你喜欢
      • 2020-06-14
      • 1970-01-01
      • 2020-02-21
      • 2020-10-18
      • 2021-06-19
      • 2023-01-17
      • 2021-10-13
      • 1970-01-01
      • 2021-02-10
      相关资源
      最近更新 更多