Skip to content

DraggableScrollableSheet throws an exception when disposed while being animated by DraggableScrollableController.animateTo #102483

@bleroux

Description

@bleroux

Steps to Reproduce

  1. Execute flutter run on the code sample (see "Code sample" section below)
  2. Tap on the ‘Animate’ button first
  3. Tap on the ‘Hide’ button soon after (before the animation terminates)

Expected results: No exception

Actual results: Get exception ScrollableState … was disposed with an active Ticker.

Code sample
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DraggableScrollableSheet'),
      ),
      body: const Align(
        alignment: Alignment.bottomCenter,
        child: _DemoSheet(),
      ),
    );
  }
}

class _DemoSheet extends StatefulWidget {
  const _DemoSheet({Key? key}) : super(key: key);

  @override
  _DemoSheetState createState() => _DemoSheetState();
}

class _DemoSheetState extends State<_DemoSheet> {
  bool _show = true;
  DraggableScrollableController? _controller;

  @override
  void initState() {
    super.initState();
    _controller = DraggableScrollableController();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ElevatedButton(
          child: const Text('Animate'),
          onPressed: () {
            if(_show) {
              _controller!.reset();
              _controller!.animateTo(
                0.0,
                duration: const Duration(milliseconds: 1000),
                curve: Curves.linear,
              );
            }
          },
        ),
        ElevatedButton(
          child: Text(_show ? 'Hide' : 'Show'),
          onPressed: () {
            setState(() => _show = !_show);
          },
        ),
        Expanded(
          child: Visibility(
            visible: _show,
            child: DraggableScrollableSheet(
              controller: _controller,
              initialChildSize: 0.8,
              minChildSize: 0.2,
              maxChildSize: 0.9,
              expand: false,
              builder: (context, scrollController) => ListView.separated(
                physics: const BouncingScrollPhysics(),
                controller: scrollController,
                separatorBuilder: (context, index) => const Divider(),
                itemCount: 100,
                itemBuilder: (context, index) => SizedBox(
                  height: 100,
                  child: ColoredBox(
                    color: Colors.primaries[index % Colors.primaries.length],
                  ),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

Logs
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown while finalizing the widget tree:
ScrollableState#166a8(tickers: tracking 1 ticker, position: _DraggableScrollableSheetScrollPosition#d5ac2(offset: 0.0, range: -1.0..12236.6, viewport: 235.1, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics, null, ScrollDirection.idle), effective physics: ClampingScrollPhysics -> RangeMaintainingScrollPhysics) was disposed with an active Ticker.

ScrollableState created a Ticker via its TickerProviderStateMixin, but at the time dispose() was called on the mixin, that Ticker was still active. All Tickers must be disposed before calling super.dispose().

Tickers used by AnimationControllers should be disposed by calling dispose() on the AnimationController itself. Otherwise, the ticker will leak.

The offending ticker was: _WidgetTicker(created by ScrollableState#166a8)
The stack trace when the _WidgetTicker was actually created was:
#0      new Ticker.<anonymous closure>
#1      new Ticker
#2      new _WidgetTicker
#3      TickerProviderStateMixin.createTicker
#4      new AnimationController.unbounded
#5      DraggableScrollableController.animateTo
#6      _DemoSheetState.build.<anonymous closure>
#7      _InkResponseState._handleTap
#8      GestureRecognizer.invokeCallback
#9      TapGestureRecognizer.handleTapUp
#10     BaseTapGestureRecognizer._checkUp
#11     BaseTapGestureRecognizer.handlePrimaryPointer
#12     PrimaryPointerGestureRecognizer.handleEvent
#13     PointerRouter._dispatch
#14     PointerRouter._dispatchEventToRoutes.<anonymous closure>
#15     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:539:8)
#16     PointerRouter._dispatchEventToRoutes
#17     PointerRouter.route
#18     GestureBinding.handleEvent
#19     GestureBinding.dispatchEvent
#20     RendererBinding.dispatchEvent
#21     GestureBinding._handlePointerEventImmediately
#22     GestureBinding.handlePointerEvent
#23     GestureBinding._flushPointerEventQueue
#24     GestureBinding._handlePointerDataPacket
#28     _invoke1 (dart:ui/hooks.dart:170:10)
#29     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7)
#30     _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
(elided 3 frames from dart:async)

When the exception was thrown, this was the stack
#0      TickerProviderStateMixin.dispose.<anonymous closure>
#1      TickerProviderStateMixin.dispose
#2      RestorationMixin.dispose
#3      ScrollableState.dispose
#4      StatefulElement.unmount
#5      _InactiveElements._unmount
#6      _InactiveElements._unmount.<anonymous closure>
#7      ComponentElement.visitChildren
#8      _InactiveElements._unmount
#9      _InactiveElements._unmount.<anonymous closure>
#10     SingleChildRenderObjectElement.visitChildren
#11     _InactiveElements._unmount
#12     _InactiveElements._unmount.<anonymous closure>
#13     _LayoutBuilderElement.visitChildren
#14     _InactiveElements._unmount
#15     _InactiveElements._unmount.<anonymous closure>
#16     ComponentElement.visitChildren
#17     _InactiveElements._unmount
#18     ListIterable.forEach (dart:_internal/iterable.dart:39:13)
#19     _InactiveElements._unmountAll
#20     BuildOwner.lockState
#21     BuildOwner.finalizeTree
#22     WidgetsBinding.drawFrame
#23     RendererBinding._handlePersistentFrameCallback
#24     SchedulerBinding._invokeFrameCallback
#25     SchedulerBinding.handleDrawFrame
#26     SchedulerBinding._handleDrawFrame
#30     _invoke (dart:ui/hooks.dart:151:10)
#31     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#32     _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)

stable and master flutter doctor -v :

[✓] Flutter (Channel stable, 2.10.4, on Ubuntu 20.04.4 LTS 5.4.0-109-generic, locale fr_FR.UTF-8)
    • Flutter version 2.10.4 at /home/bruno/fvm/versions/2.10.4
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision c860cba910 (il y a 4 semaines), 2022-03-25 00:23:12 -0500
    • Engine revision 57d3bac3dd
    • Dart version 2.16.2
    • DevTools version 2.9.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /home/bruno/Android/Sdk
    • Platform android-31, build-tools 30.0.3
    • Java binary at: /home/bruno/Produits/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • clang version 10.0.0-4ubuntu1
    • cmake version 3.16.3
    • ninja version 1.8.2
    • pkg-config version 0.29.1

[✓] Android Studio (version 2020.3)
    • Android Studio at /home/bruno/Produits/android-studio
    • Flutter plugin version 60.1.2
    • Dart plugin version 203.8292
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)

[✓] VS Code (version 1.66.2)
    • VS Code at /usr/share/code
    • Flutter extension version 3.38.1

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Ubuntu 20.04.4 LTS 5.4.0-109-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 100.0.4896.127

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!


[✓] Flutter (Channel master, 2.13.0-0.0.pre.710, on Ubuntu 20.04.4 LTS 5.4.0-109-generic, locale fr_FR.UTF-8)
    • Flutter version 2.13.0-0.0.pre.710 at /home/bruno/fvm/versions/master
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 4fec2ee0ed (il y a 3 heures), 2022-04-24 23:29:06 -0400
    • Engine revision 20b5b8604c
    • Dart version 2.18.0 (build 2.18.0-44.0.dev)
    • DevTools version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /home/bruno/Android/Sdk
    • Platform android-31, build-tools 30.0.3
    • Java binary at: /home/bruno/Produits/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop
    • clang version 10.0.0-4ubuntu1
    • cmake version 3.16.3
    • ninja version 1.8.2
    • pkg-config version 0.29.1

[✓] Android Studio (version 2020.3)
    • Android Studio at /home/bruno/Produits/android-studio
    • Flutter plugin version 60.1.2
    • Dart plugin version 203.8292
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)

[✓] VS Code (version 1.66.2)
    • VS Code at /usr/share/code
    • Flutter extension version 3.38.1

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Ubuntu 20.04.4 LTS 5.4.0-109-generic
    • Chrome (web)    • chrome • web-javascript • Google Chrome 100.0.4896.127

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

Looks similar to #101061 but it is not the same bug (DraggableScrollableController.animateTo and _DraggableScrollableSheetScrollPosition.goBallistic create AnimationController in the same way that might leads to leaked tickers).

Metadata

Metadata

Assignees

Labels

a: error messageError messages from the Flutter frameworkc: crashStack traces logged to the consolef: material designflutter/packages/flutter/material repository.f: scrollingViewports, list views, slivers, etc.found in release: 2.10Found to occur in 2.10found in release: 2.13Found to occur in 2.13frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work on

Type

No type

Projects

Status

Done (PR merged)

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions