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

Issue

I have a CustomScrollView with a SliverAppBar that hides on scroll.

On the app bar is a search button that, when pressed, puts a TextField into the app bar.

When the field gets focus, it causes the scroll view to scroll all the way to the top, and the app bar gets stuck up in the "unsafe" area:

scroll on focus issue

The Scaffold docs mention that when the keyboard is shown the scaffold’s insets change and the scaffold is rebuilt, causing the "focused widget will be scrolled into view if it’s within a scrollable container".

This seems like the behavior I don’t want. I looked but couldn’t understand the mechanism or how to suppress it. Is doing so possible?

The source code for the view in the image is here.

Also I note that this problem didn’t happen in my previous implementation with non-sliver, standard widgets. I suspect this is because the app bar was not in a scrollable view, whereas SliverAppBar is inside the CustomScrollView so that it can interact with the main body.

Solution

Edit: This issue was fixed by this PR which appears to have first landed in Flutter 1.22.0.

It took some sleuthing, but I eventually found the mechanism for this behavior.

TextField wraps EditableText. When the latter gains focus, it will invoke _showCaretOnScreen, which includes a call to renderEditable.showOnScreen. This bubbles up and eventually causes the scrolling behavior.

We can force _showCaretOnScreen to return early here if we supply to the TextField a hacked ScrollController that always returns false from 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;
}

The behavior does not seem intentional, so I reported it as bug #60422.

Caveats

This workaround may not be very stable.

I don’t know what adverse effects the hasClients override might have.

The docs for TextField say that the scrollController is used "when vertically scrolling the input". In this use case we don’t want vertical scrolling anyway, so the workaround might not cause any problems. In my brief testing it didn’t seem to cause problems with horizontal (overflow) scrolling.

Answered By – Aaron

Answer Checked By – Pedro (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *