Ros*_*han 16 keyboard textfield dart flutter
我有一个屏幕,其中包含一个Form带有StreamBuilder. 当我从 加载初始数据时StreamBuilder,TextFormField按预期显示数据。
当我点击 内部时TextFormField,软件键盘会出现,这会导致小部件重建。当键盘再次按下时,同样的情况再次发生。
不幸的是,StreamBuilder再次订阅了并且文本框值被替换为初始值。
这是我的代码:
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _bloc.inputObservable(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return TextFormField(
// ...
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
);
}
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个问题?
cre*_*not 16
这是完全有道理的,预计软件键盘打开会导致重建。在幕后,MediaQuery是与更新视图插图。这些MediaQueryData.viewInsets确保您的 UI 知道键盘遮挡了它。抽象地说,键盘遮挡屏幕会导致窗口发生变化,并且大部分时间会导致您的 UI 发生变化,这需要对 UI 进行更改 - 重建。
我可以确信您Scaffold在 Flutter 应用程序中使用了 a 。像许多其他的框架部件,该Scaffold部件依赖(见InheritedWidget)上MediaQuery(从获取数据Window包含您的应用程序)使用MediaQuery.of(context)。
有关MediaQueryData更多信息,请参阅。
这一切都归结为Scaffold对视图插入的依赖。这允许它在这些视图插入更改时调整大小。基本上,当键盘打开时,视图插入更新,这允许脚手架在底部收缩,去除被遮挡的空间。
长话短说,适应调整后的视图插入的脚手架需要重新构建脚手架 UI。并且由于您的小部件必然是脚手架的子项(可能是body),因此在发生这种情况时也会重建您的小部件。
您可以使用禁用视图插入调整大小行为Scaffold.resizeToAvoidBottomInset。但是,这不一定会停止重建,因为可能仍然存在对MediaQuery. 我将在下面解释你应该如何真正思考这个问题。
你应该始终以你的build方法是幂等的方式构建你的 Flutter 小部件。
范例是构建调用可以在任何时间点发生,每秒最多 60 次(如果刷新率更高,则可能更多)。
我的意思幂等构建的呼叫是,当你的小部件的配置没有什么(在的情况下,StatelessWidget小号),或一无所知的状态(在的情况下,StatefulWidget小号)的变化,所产生的widget树应该是严格相同。因此,您不想处理任何状态build- 它的唯一职责应该是表示当前配置或状态。
导致重建的软件键盘打开只是一个很好的例子,说明为什么会这样。其他示例包括旋转设备、在 Web 上调整大小,但当您的小部件树开始变得复杂时,它实际上可以是任何东西(更多内容见下文)。
StreamBuilder 重新订阅重建回到最初的问题:在这种情况下,您的问题是您的方法StreamBuilder不正确。你应该不给它是一个流重建每个构建。
流构建器的工作方式是订阅初始流,然后在流更新时重新订阅。这意味着当两次调用之间小部件的stream属性StreamBuilder不同时build,流构建器将取消订阅第一个流并订阅第二个(新)流。
您可以在_StreamBuilderBaseState.didUpdateWidget实现中看到这一点:
if (oldWidget.stream != widget.stream) {
if (_subscription != null) {
_unsubscribe();
_summary = widget.afterDisconnected(_summary);
}
_subscribe();
}
Run Code Online (Sandbox Code Playgroud)
这里显而易见的解决方案是,当您不想重新订阅时,您将希望在不同的构建调用之间提供相同的流。这可以追溯到幂等构建调用!
StreamController例如,A将始终返回相同的流,这意味着stream: streamController.stream在您的StreamBuilder. 基本上,所有控制器、行为主体等实现都应该以这种方式运行 - 只要您不重新创建流,StreamBuilder就会妥善处理它!
因此_bloc.inputObservable(),您的案例中的错误函数是,它每次都会创建一个新流,而不是返回相同的流。
请注意,我说过构建调用可以“在任何时间点”发生。实际上,您可以(技术上)准确控制应用程序中的每个构建何时发生。然而,一个普通的应用程序会非常复杂,你可能无法控制它,因此,你会希望有幂等的构建调用。
导致重建的键盘就是一个很好的例子。
如果您从高层次考虑,这正是您想要的 - 框架及其小部件(或您创建的小部件)负责响应外部更改并在必要时进行重建。树中的叶小部件不应该关心是否发生了重建——它们应该被放置在任何环境中,并且框架通过相应地重建来负责对该环境的变化做出反应。
我希望我能够为你解决这个问题:)