Skip to content

ScrollController doesn't broadcast change notification after it updates due to shrinking content #121419

@marcianx

Description

@marcianx

Steps to Reproduce

Steps

  1. Render a scrollable container with ListView and a ScrollController.
  2. Listen to events on ScrollController (e.g. via addListener).
  3. Scroll the content all the way to the end. Observe that all events are fired.
  4. Shrink the content so that the ScrollController is updated to the reduced maxScrollExtent.

Expected results: ScrollController should notify that it was changed.

Actual results: It doesn't.

Here's a unit test that demonstrates it -- the last expectation fails.

I ran this as a flutter-web integration test.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

const _viewportWidth = 300.0;

class FooApp extends StatelessWidget {
  final ScrollController scrollController;
  final ValueNotifier<double> contentWidth;

  FooApp(this.scrollController, this.contentWidth);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Material(
        child: Wrap(
          children: [
            Container(
              height: 40,
              width: _viewportWidth,
              child: ListView(
                controller: scrollController,
                scrollDirection: Axis.horizontal,
                shrinkWrap: true,
                children: [
                  ValueListenableBuilder(
                    valueListenable: contentWidth,
                    builder: (context, contentWidth, _) =>
                        SizedBox(height: 40, width: contentWidth),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

void main() {
  group('Scroll issues', () {
    testWidgets(
        'No scroll notification when scroll offset changes due to content shrinking',
        (tester) async {
      final contentWidth = ValueNotifier(500.0);
      final scrollController = ScrollController();

      // Listen to [scrollController] update notifications.
      var scrollOffset = 0.0;
      scrollController
          .addListener(() => scrollOffset = scrollController.offset);

      await tester.pumpWidget(FooApp(scrollController, contentWidth));

      // When moving the scroll controller all the way to the end.
      scrollController.jumpTo(scrollController.position.maxScrollExtent);
      var expectedScrollPosition = contentWidth.value - _viewportWidth;
      await tester.pumpAndSettle();
      expect(scrollController.offset, expectedScrollPosition,
          reason: 'Expect scrollController to update to scroll to the end.');
      expect(scrollOffset, expectedScrollPosition,
          reason: 'Expect scrollController to have broadcast notifications.');

      // When shrinking the content so that the maxScrollExtent decreases.
      contentWidth.value -= 100;
      expectedScrollPosition = contentWidth.value - _viewportWidth;
      // Wait as long as needed for everything to settle.
      await tester.pumpAndSettle();
      await tester.pumpAndSettle();
      await tester.pumpAndSettle();
      expect(scrollController.offset, expectedScrollPosition,
          reason: 'Expect scrollController to update to the new end.');
      // >>>>>>>>>> FAILS HERE: <<<<<<<<<<
      expect(scrollOffset, expectedScrollPosition,
          reason: 'Expect scrollController to have broadcast a notification.');
    });
  });
}

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listcustomer: googleVarious Google teamsd: api docsIssues with https://api.flutter.dev/f: scrollingViewports, list views, slivers, etc.frameworkflutter/packages/flutter repository. See also f: labels.

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions