如何抑制 Flutter 中的滚动到视图行为?

How to suppress scroll-into-view behavior in Flutter?

我有一个 CustomScrollView 和一个隐藏在滚动条上的 SliverAppBar

应用栏上有一个搜索按钮,按下后会在应用栏中添加一个 TextField

当字段获得焦点时,它会导致滚动视图一直滚动到顶部,并且应用栏卡在“不安全”区域:

Scaffold docs 提到当显示键盘时脚手架的插图会发生变化并重建脚手架,导致“如果焦点小部件在可滚动容器内,它将滚动到视图中”。

这似乎是我不想要的行为。我看了但无法理解该机制或如何抑制它。这样做可以吗?

图中视图的源代码是here

我还注意到,在我之前使用非条形标准小部件的实现中没有发生此问题。我怀疑这是因为应用栏不在可滚动视图中,而 SliverAppBarCustomScrollView 内部,以便它可以与主体交互。

就像文档提到的那样,尝试在脚手架小部件中将 resizeToAvoidBottomInset 参数设置为 false(默认为 true)https://api.flutter.dev/flutter/material/Scaffold/resizeToAvoidBottomInset.html

另外,我建议在脚手架之后创建 ValueListenableBuilder(作为正文中的第一个小部件)以避免重建整个脚手架而只重建正文小部件

编辑:此问题已由 this PR 修复,该问题似乎首先出现在 Flutter 1.22.0 中。

我花了一些时间进行调查,但我最终找到了这种行为的机制。

TextField 换行 EditableText。当后者获得焦点时,它将调用 _showCaretOnScreen, which includes a call to renderEditable.showOnScreen。这会冒泡并最终导致滚动行为。

如果我们向 TextField 提供一个总是 return 的黑客 ScrollController,我们可以强制 _showCaretOnScreen 提前 here return false 来自 hasClients:

class _HackScrollController extends ScrollController {
  // Causes early return from EditableText._showCaretOnScreen, preventing focus
  // gain from making the CustomScrollView jump to the top.
  @override
  bool get hasClients => false;
}

该行为似乎不是故意的,因此我将其报告为 bug #60422

注意事项

此解决方法可能不是很稳定。

我不知道 hasClients 覆盖可能会产生什么不利影响。

docs for TextFieldscrollController 用于“垂直滚动输入”。在此用例中,我们无论如何都不希望垂直滚动,因此解决方法可能不会导致任何问题。在我的简短测试中,它似乎没有引起水平(溢出)滚动的问题。