Skip to content

AnimationController.stop() called after dispose in _moveToNextViewWithAnimation when calendar widget is torn down during gesture #2529

@ttsakalos

Description

@ttsakalos

Bug description

Package: syncfusion_flutter_calendar
Version: 31.1.19
Flutter: stable

Description

When an SfCalendar is disposed mid-gesture (e.g. the user taps the forward/backward navigation arrow at the exact moment a tab switch tears down the widget tree), an assertion fires in debug mode:

AnimationController.stop() called after AnimationController.dispose()
Failed assertion: line 897 pos 7: '_ticker != null'

Stack trace

#2  AnimationController.stop (animation_controller.dart:897)
#3  AnimationController.value= (animation_controller.dart:373)
#4  AnimationController.reset (animation_controller.dart:394)
#5  _CustomCalendarScrollViewState._moveToNextViewWithAnimation (calendar_view.dart:3378)

Root cause

_moveToNextViewWithAnimation calls _animationController.reset() without first checking mounted.

If the State is disposed (e.g. due to a concurrent widget rebuild), the AnimationController has already been disposed and the call throws.

Suggested fix

Add a mounted guard in _moveToNextViewWithAnimation before operating on _animationController:

void _moveToNextViewWithAnimation() {
  if (!mounted) return;

  _animationController.reset();
  ...
}

Severity

Debug-only assertion (release builds are unaffected), but it causes repeated red-screen errors during development.

Steps to reproduce

  1. Implement an SfCalendar within a view that can be quickly navigated away from (e.g., inside a TabBarView or a GoRouter page).
  2. Add a custom header with buttons that call calendarController.forward() or calendarController.backward().
  3. Tap the navigation button (Forward/Backward) and immediately trigger a navigation/tab switch that disposes of the calendar widget (e.g., tap the arrow and then a different tab icon in rapid succession).
  4. The internal AnimationController attempt to reset() or stop() during the tear-down process, triggering the assertion.

Code sample

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';

void main() => runApp(const MaterialApp(home: ReproPage()));

class ReproPage extends StatefulWidget {
const ReproPage({super.key});

@OverRide
State createState() => _ReproPageState();
}

class _ReproPageState extends State {
final CalendarController _controller = CalendarController();
bool _showCalendar = true;

@OverRide
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
if (_showCalendar)
Expanded(
child: SfCalendar(
controller: _controller,
view: CalendarView.month,
),
),
ElevatedButton(
onPressed: () {
// Trigger navigation and immediate disposal
_controller.forward!();
setState(() => _showCalendar = false);
},
child: const Text('Navigate Forward & Dispose'),
),
],
),
);
}
}

Screenshots or Video

Not applicable.

Stack Traces

════════ Exception caught by gesture ═══════════════════════════════════════════
The following assertion was thrown while handling a gesture:
AnimationController.stop() called after AnimationController.dispose()
AnimationController methods should not be used after calling dispose.
'package:flutter/src/animation/animation_controller.dart':
Failed assertion: line 897 pos 7: '_ticker != null'

When the exception was thrown, this was the stack:
#2 AnimationController.stop (package:flutter/src/animation/animation_controller.dart:897:7)
#3 AnimationController.value= (package:flutter/src/animation/animation_controller.dart:373:5)
#4 AnimationController.reset (package:flutter/src/animation/animation_controller.dart:394:5)
#5 _CustomCalendarScrollViewState._moveToNextViewWithAnimation (package:syncfusion_flutter_calendar/src/calendar/views/calendar_view.dart:3378:28)
#6 _CalendarCustomHeaderState._goToAdjacentPeriod (package:worklife/application/calendar/components/calendar_custom_header/calendar_custom_header.dart:49:35)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#8acee

On which target platforms have you observed this bug?

Android

Flutter Doctor output

[√] Flutter (Channel stable, 3.38.7, on Microsoft Windows [Version 10.0.26200.8246])
• Flutter version 3.38.7 on channel stable
• Framework revision 3b62efc2a3 (2026-01-13)
• Dart version 3.10.7
• DevTools version 2.51.1

[√] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
• Java version OpenJDK Runtime Environment (build 21.0.7+-13880790-b1038.58)

[√] Connected device (4 available)
• sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64 • Android 16 (API 36)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcalendarCalendar componentfixedFixed and delivered updatewaiting for customer responseCannot make further progress until the customer responds.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions