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
-
- Object
- DiagnosticableTree
- Widget
- StatefulWidget
- AnimatedBetween
- 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.centerexpands from the center,Alignment.topLeftexpands down-right).final - child → Widget?
-
The child currently shown. Changing it triggers a transition to
the new value. A
nullchild 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 topno 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 toAnimate(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
Constants
- defaultFadeCurve → const Cubic
- defaultFadeDuration → const Duration
- defaultFadeDurationShowHide → const Duration
- defaultSizeCurve → const Cubic
- defaultSizeDurationFactor → const double
- defaultSizeDurationFactorShowHide → const double