Repository: flutter
Issue: AnimatedCrossFade triggers animation exception with duration of zero milliseconds
Labels: c: crash,framework,a: animation,a: error message,has reproducible steps,P2,found in release: 3.3,found in release: 3.7,team-framework,triaged-framework

## Main issue
As was also reported [elsewhere](https://github.com/flutter/flutter/issues/28434), when using the `AnimatedCrossFade` widget with a duration of `milliseconds: 0`, Flutter throws an exception:
```
════════ Exception caught by animation library ═════════════════════════════════
The following assertion was thrown while notifying listeners for AnimationController:
'package:flutter/src/rendering/object.dart': Failed assertion: line 1530 pos 12: '_debugCanPerformMutations': is not true.
```

Additionally, documentation in the error itself is severely lacking! It doesn't provide any relevant stack trace, or a thread of information about the offending code line. It is only by trial and error that a developer can figure out that this is caused by an offending `AnimatedCrossFade` widget.

<details>
<summary>Debug Console</summary>

```
════════ Exception caught by animation library ═════════════════════════════════
The following assertion was thrown while notifying listeners for AnimationController:
'package:flutter/src/rendering/object.dart': Failed assertion: line 1530 pos 12: '_debugCanPerformMutations': is not true.


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

When the exception was thrown, this was the stack
#2      RenderObject.markNeedsLayout
package:flutter/…/rendering/object.dart:1530
#3      RenderBox.markNeedsLayout
package:flutter/…/rendering/box.dart:2147
#4      new RenderAnimatedSize.<anonymous closure>
package:flutter/…/rendering/animated_size.dart:95
#5      AnimationLocalListenersMixin.notifyListeners
package:flutter/…/animation/listener_helpers.dart:136
#6      AnimationController._animateToInternal
package:flutter/…/animation/animation_controller.dart:585
...
The AnimationController notifying listeners was: AnimationController#34c52(⏮ 1.000; paused)
════════════════════════════════════════════════════════════════════════════════
```
</details>

## Sample app
When the widget is first rendered, no exceptions are thrown. It is only once you trigger a cross-fade that the exceptions are thrown on each consecutive call.

One can prevent this exception by increasing the duration, but the amount depends on system resources and complexity of code. For example, in the sample app below, setting the duration to `millisecond: 1` is enough. However, in a more complex app, even `milliseconds: 60` might not be enough.

<details>
<summary>main.dart</summary>

```dart
// main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  CrossFadeState _crossFadeState = CrossFadeState.showFirst;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            RaisedButton(
              child: Text('Toggle'),
              onPressed: _crossFadeNext,
            ),
            AnimatedCrossFade(
              firstChild: Text('First'),
              secondChild: Text('Second'),
              crossFadeState: _crossFadeState,
              duration: Duration(milliseconds: 0),
            ),
          ],
        ),
      ),
    );
  }

  void _crossFadeNext() {
    setState(() => _crossFadeState =
        (_crossFadeState == CrossFadeState.showFirst)
            ? CrossFadeState.showSecond
            : CrossFadeState.showFirst);
  }
}
```
</details>

### Flutter Doctor
<details>
<summary>Blissfully ignorant, as always. 😐</summary>

```
[✓] Flutter (Channel stable, 1.22.4, on macOS 11.0.1 20B29 darwin-x64, locale en-US)
    • Flutter version 1.22.4 at /Users/benPesso/Projects/flutter
    • Framework revision 1aafb3a8b9 (12 days ago), 2020-11-13 09:59:28 -0800
    • Engine revision 2c956a31c0
    • Dart version 2.10.4

 
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at /Users/benPesso/Library/Android/sdk
    • Platform android-30, build-tools 30.0.2
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

 
[✓] Xcode - develop for iOS and macOS (Xcode 12.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.2, Build version 12B45b
    • CocoaPods version 1.9.3

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 50.0.1
    • Dart plugin version 193.7547
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] VS Code (version 1.51.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.16.0

 
[✓] Connected device (1 available)            
    • iPhone SE (2nd generation) (mobile) • ED880130-98FA-4356-8F56-B89E9888798E • ios •
      com.apple.CoreSimulator.SimRuntime.iOS-14-2 (simulator)

• No issues found!
```
</details>