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
- Implement an SfCalendar within a view that can be quickly navigated away from (e.g., inside a TabBarView or a GoRouter page).
- Add a custom header with buttons that call calendarController.forward() or calendarController.backward().
- 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).
- 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)
Bug description
Package:
syncfusion_flutter_calendarVersion:
31.1.19Flutter: stable
Description
When an
SfCalendaris 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:Stack trace
Root cause
_moveToNextViewWithAnimationcalls_animationController.reset()without first checkingmounted.If the
Stateis disposed (e.g. due to a concurrent widget rebuild), theAnimationControllerhas already been disposed and the call throws.Suggested fix
Add a
mountedguard in_moveToNextViewWithAnimationbefore operating on_animationController:Severity
Debug-only assertion (release builds are unaffected), but it causes repeated red-screen errors during development.
Steps to reproduce
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)