AnimatedBetween class

Animates smoothly between two children, cross-fading their content while resizing the enclosing box. The old and new children can differ in both width and height; their sizes do not need to be known in advance — they are measured as they lay out.

Transition model

Each transition runs two animations concurrently:

  • a cross-fade between the old child (fading out) and the new child (fading in), running for fadeDuration,
  • a size change from the old box size to the new one, running for a derived duration that scales with the size difference.

The size duration is computed from fadeDuration and sizeDurationFactor. Let P be the proportional area difference between the two children (larger area over smaller area — so P ≥ 1). Then:

R = P ^ (1 / sizeDurationFactor)
sizeDuration = fadeDuration * R

Because P ≥ 1 and sizeDurationFactor ≥ 1, we always have R ≥ 1, so the size animation is never shorter than the fade. Larger sizeDurationFactor dampens the effect of P, pulling the size duration closer to the fade duration. At sizeDurationFactor = 1, the size duration scales linearly with P. Example: if one child has 4× the area of the other (P = 4), then with sizeDurationFactor = 2 the size animation is √4 = 2× the fade, and with sizeDurationFactor = 3 it is ∛4 ≈ 1.59× the fade.

When the width and height directions disagree (e.g. the new child is wider but shorter), grow vs. shrink is decided by overall area, so the transition commits to a single direction rather than mixing.

The fade and size animations are coordinated so that they always end together on grow, and always start together on shrink:

  • Growing (new child larger by area): the size animation starts first; the fade starts later, so the fade finishes at the same moment the size animation does. The box opens to make room, and the new child reveals into that room right as it settles.
  • Shrinking (new child smaller by area): both animations start together. The fade finishes first (since it is the shorter of the two), so the new, smaller child is already visible while the box settles around it.

The fade and size animations use independent curves (fadeCurve and sizeCurve), so each can be tuned separately.

The visual effect

The container change and the content change happen in the order the eye expects:

  • On grow, space opens and then fills — the new (larger) child never looks cramped inside a still-small box.
  • On shrink, new content arrives and then the frame tightens around it — the box never looks like it's cutting off the new child.

The default curves (fadeCurve = Curves.easeInOut, sizeCurve = a custom cubic) reinforce this: both animations settle toward the end, so the two directions read as a single coordinated gesture rather than two separate animations running side by side.

Widget identity caveat

If the new child is the same widget type as the old one and has the same key, Flutter treats them as the same widget and updates the existing element in place, with no transition. To force a transition, give each visually distinct child its own Key, usually a ValueKey:

AnimatedBetween(
  child: toggle
    ? const Text('A', key: ValueKey('a'))
    : const Text('B', key: ValueKey('b')),
)

To animate a single widget in and out (null ↔ widget), use AnimatedBetween.showHide.

Inheritance
Available extensions

Constructors

AnimatedBetween({Key? key, required Widget? child, Duration fadeDuration = defaultFadeDuration, double sizeDurationFactor = defaultSizeDurationFactor, Curve fadeCurve = defaultFadeCurve, Curve sizeCurve = defaultSizeCurve, Alignment alignment = Alignment.center, Clip clipBehavior = Clip.none, AnimatedBetweenMode modeShorterChild = AnimatedBetweenMode.resize, AnimatedBetweenMode modeLargerChild = AnimatedBetweenMode.fit, bool printDebug = false})
Creates an AnimatedBetween.
const
AnimatedBetween.showHide({Key? key, required bool show, required Widget child, Duration fadeDuration = defaultFadeDurationShowHide, double sizeDurationFactor = defaultSizeDurationFactorShowHide, Curve fadeCurve = Curves.easeInOut, Curve sizeCurve = defaultSizeCurve, Alignment alignment = Alignment.topCenter, Clip clipBehavior = Clip.none, AnimatedBetweenMode mode = AnimatedBetweenMode.fit, bool printDebug = true})
Convenience constructor that animates a single widget in and out.
const

Properties

alignment Alignment
Where each child is placed inside the animated box. Also determines the anchor point from which the box appears to grow or shrink (e.g. Alignment.center expands from the center, Alignment.topLeft expands down-right).
final
child Widget?
The child currently shown. Changing it triggers a transition to the new value. A null child is valid and animates the box to zero size.
final
clipBehavior Clip
How content that exceeds the current animated box size is clipped during the transition.
final
fadeCurve Curve
Curve applied to the cross-fade animation, in both directions. Defaults to Curves.easeInOut, which reads naturally for growing and shrinking alike.
final
fadeDuration Duration
Duration of the cross-fade between the old child (fading out) and the new child (fading in).
final
hashCode int
The hash code for this object.
no setterinherited
key Key?
Controls how one widget replaces another widget in the tree.
finalinherited
makeRefreshable Widget

Available on Widget?, provided by the WidgetExtension extension

Make your any widget refreshable with RefreshIndicator on top
no setter
modeLargerChild AnimatedBetweenMode
How the child with the larger natural area fills the animated box during a transition. See AnimatedBetweenMode for the available modes.
final
modeShorterChild AnimatedBetweenMode
How the child with the smaller natural area fills the animated box during a transition. See AnimatedBetweenMode for the available modes (AnimatedBetweenMode.overflow, AnimatedBetweenMode.fit, AnimatedBetweenMode.resize).
final
printDebug bool
When true, prints the computed fade and size durations (in milliseconds) at the start of every transition, and whenever the size target changes mid-transition. Intended for debugging the effect of fadeDuration and sizeDurationFactor. Defaults to false.
final
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
sizeCurve Curve
Curve applied to the size animation, in both directions. Defaults to a custom cubic (.29, .65, .35, .97) that accelerates a bit into the change and settles gently at the end.
final
sizeDurationFactor double
Controls how much longer the size animation is, relative to the fade, as a function of the size ratio between the two children. Must be >= 1.
final

Methods

addMaterialWidget() Material

Available on Widget, provided by the GenericExtensions extension

addTooltipWidget(String toolTip) Tooltip

Available on Widget, provided by the GenericExtensions extension

animate({Key? key, List<Effect>? effects, AnimateCallback? onInit, AnimateCallback? onPlay, AnimateCallback? onComplete, bool? autoPlay, Duration? delay, AnimationController? controller, Adapter? adapter, double? target, double? value}) Animate

Available on Widget, provided by the AnimateWidgetExtensions extension

Wraps the target Widget in an Animate instance, and returns the instance for chaining calls. Ex. myWidget.animate() is equivalent to Animate(child: myWidget).
borderRadius([BorderRadiusGeometry? borderRadius]) Widget

Available on Widget, provided by the GenericExtensions extension

boxDecoration([BoxDecoration? boxDecoration]) Widget

Available on Widget, provided by the GenericExtensions extension

center({double? heightFactor, double? widthFactor}) Widget

Available on Widget?, provided by the WidgetExtension extension

set parent widget in center
colorFilter([ColorFilter? colorFilter]) Widget

Available on Widget, provided by the GenericExtensions extension

set parent widget in center
cornerRadiusWithClipRRect(double radius) ClipRRect

Available on Widget?, provided by the WidgetExtension extension

add corner radius
cornerRadiusWithClipRRectOnly({int bottomLeft = 0, int bottomRight = 0, int topLeft = 0, int topRight = 0}) ClipRRect

Available on Widget?, provided by the WidgetExtension extension

add custom corner radius each side
createElement() StatefulElement
Creates a StatefulElement to manage this widget's location in the tree.
inherited
createState() State<AnimatedBetween>
Creates the mutable state for this widget at a given location in the tree.
override
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
expand({int flex = 1}) Widget

Available on Widget?, provided by the WidgetExtension extension

add Expanded to parent widget
fit({BoxFit? fit, AlignmentGeometry? alignment}) Widget

Available on Widget?, provided by the WidgetExtension extension

add FittedBox to parent widget
flexible({int flex = 1, FlexFit? fit}) Widget

Available on Widget?, provided by the WidgetExtension extension

add Flexible to parent widget
launch<T>(BuildContext context, {bool isNewTask = false, PageRouteAnimation? pageRouteAnimation, Duration? duration, String? routeName, Object? routeArguments}) Future<T?>

Available on Widget?, provided by the WidgetExtension extension

Launch a new screen
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onTap(Function? function, {BorderRadius? borderRadius, Color? splashColor, Color? hoverColor, Color? highlightColor, Color? focusColor, WidgetStateProperty<Color?>? overlayColor}) Widget

Available on Widget?, provided by the WidgetExtension extension

add tap to parent widget
opacity({required double opacity, int durationInSecond = 1, Duration? duration}) Widget

Available on Widget?, provided by the WidgetExtension extension

add opacity to parent widget
paddingAll(double padding) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding all
paddingBottom(double bottom) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding bottom
paddingDirectional({double start = 0.0, double top = 0.0, double end = 0.0, double bottom = 0.0}) Widget

Available on Widget?, provided by the WidgetExtension extension

paddingLeft(double left) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding left
paddingOnly({double top = 0.0, double left = 0.0, double bottom = 0.0, double right = 0.0}) Padding

Available on Widget?, provided by the WidgetExtension extension

return custom padding from each side
paddingRight(double right) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding right
paddingSymmetric({double vertical = 0.0, double horizontal = 0.0}) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding symmetric
paddingTop(double top) Padding

Available on Widget?, provided by the WidgetExtension extension

return padding top
rotate({required double angle, bool transformHitTests = true, Offset? origin}) Widget

Available on Widget?, provided by the WidgetExtension extension

add rotation to parent widget
scale({required double scale, Offset? origin, AlignmentGeometry? alignment, bool transformHitTests = true}) Widget

Available on Widget?, provided by the WidgetExtension extension

add scaling to parent widget
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
tooltip({required String msg}) Widget

Available on Widget?, provided by the WidgetExtension extension

toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object.
inherited
toStringShort() String
A short, textual description of this widget.
inherited
translate({required Offset offset, bool transformHitTests = true, Key? key}) Widget

Available on Widget?, provided by the WidgetExtension extension

add translate to parent widget
validate({Widget value = const SizedBox()}) Widget

Available on Widget?, provided by the WidgetExtension extension

Validate given widget is not null and returns given value if null.
visible(bool visible, {Widget? defaultWidget}) Widget

Available on Widget?, provided by the WidgetExtension extension

set visibility
withHeight(double height) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom height
withRoundedCorners({Color backgroundColor = whiteColor, BorderRadius borderRadius = const BorderRadius.all(Radius.circular(8.0)), LinearGradient? gradient, BoxBorder? border, List<BoxShadow>? boxShadow, DecorationImage? decorationImage, BoxShape boxShape = BoxShape.rectangle}) Container

Available on Widget?, provided by the WidgetExtension extension

withScroll({ScrollPhysics? physics, EdgeInsetsGeometry? padding, Axis scrollDirection = Axis.vertical, ScrollController? controller, DragStartBehavior dragStartBehavior = DragStartBehavior.start, bool? primary, required bool reverse}) Widget

Available on Widget?, provided by the WidgetExtension extension

withShaderMask(List<Color> colors, {BlendMode blendMode = BlendMode.srcATop}) Widget

Available on Widget?, provided by the WidgetExtension extension

Wrap with ShaderMask widget
withShaderMaskGradient(Gradient gradient, {BlendMode blendMode = BlendMode.srcATop}) Widget

Available on Widget?, provided by the WidgetExtension extension

Wrap with ShaderMask widget Gradient
withShadow({Color bgColor = whiteColor, Color shadowColor = Colors.black12, dynamic blurRadius = 10.0, dynamic spreadRadius = 0.0, Offset offset = const Offset(0.0, 0.0), LinearGradient? gradient, BoxBorder? border, DecorationImage? decorationImage, BoxShape boxShape = BoxShape.rectangle}) Container

Available on Widget?, provided by the WidgetExtension extension

withSize({double width = 0.0, double height = 0.0}) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom height and width
withTooltip({required String msg}) Widget

Available on Widget?, provided by the WidgetExtension extension

Validate given widget is not null and returns given value if null.
withVisibility(bool visible, {Widget? replacement, bool maintainAnimation = false, bool maintainState = false, bool maintainSize = false, bool maintainSemantics = false, bool maintainInteractivity = false}) Visibility

Available on Widget?, provided by the WidgetExtension extension

set widget visibility
withWidth(double width) SizedBox

Available on Widget?, provided by the WidgetExtension extension

With custom width

Operators

operator ==(Object other) bool
The equality operator.
inherited