A versatile Flutter widget that provides animated transitions between two widgets, offering a variety of customizable effects.
clipReveal
, slideFade
transitions.To use this widget, add it to your project and import the necessary file:
dependencies:
flutter:
sdk: flutter
custom_animated_switcher: ^1.0.0
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomeScreen(),
);
}
}
class MyHomeScreen extends StatefulWidget {
const MyHomeScreen({super.key});
@override
State<MyHomeScreen> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomeScreen> {
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Custom Animated Switcher"),
),
body: Center(
child: SizedBox(
width: 100,
height: 100,
child: CustomAnimatedSwitcher(
showPrimary: _isLoading,
animationType: AnimationType.clipReveal,
animationCurve: Curves.fastOutSlowIn,
animationDuration: const Duration(milliseconds: 500),
primaryWidget: ListView.builder(
itemCount: 20, itemBuilder: (_, index) => Text('Data: $index')),
secondaryWidget: const CircularProgressIndicator(),
clipper: const CircularRevealClipper(),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_isLoading = !_isLoading;
});
},
child: Icon(_isLoading ? Icons.visibility_off : Icons.visibility),
),
);
}
}
The CustomAnimatedSwitcher
accepts the following parameters:
showPrimary
: (required bool
) Determines whether the primaryWidget
is currently visible (true
) or if the secondaryWidget
should be displayed (false
).secondaryWidget
: (required Widget
) The widget that is displayed when showPrimary
is false
.primaryWidget
: (required Widget
) The widget that is displayed when showPrimary
is true
.animationDuration
: (Duration
, optional) The total duration of the transition animation. Defaults to 500 milliseconds
.clipper
: (CustomClipper<Path>?
, optional) An optional custom clipper that is used to clip the primaryWidget
during transitions. When not provided the widget will still be animated but without clipping effect.animationCurve
: (Curve
, optional) The animation curve used for the main scaling and clipping animation. Defaults to Curves.easeInOut
.slideCurve
: (Curve
, optional) The animation curve used for the sliding animation effect, applied when animationType
is slideFade
. Defaults to Curves.easeInOut
.fadeCurve
: (Curve
, optional) The animation curve used for the fading animation effect. Defaults to Curves.easeInOut
.horizontalSlide
: (bool
, optional) When set to true
, the slide animation (when using slideFade
) occurs horizontally. When false
, it slides vertically. Defaults to true
.animationType
: (AnimationType
, optional) Determines the type of transition animation used. It can be either clipReveal
for a scaling and clipping animation, or slideFade
for a sliding and fading animation. Defaults to clipReveal
.The animationType
parameter accepts the following values from the AnimationType
enum, which determines the transition animation:
clipReveal
:
primaryWidget
.clipper
.animationCurve
for the scaling and reveal effect.slideFade
:
primaryWidget
.slideCurve
from either the left or top based on horizontalSlide
, while scaling in, and the secondary widget fades out.fadeCurve
for the fading effect.animationCurve
for scaling animation.You can fine-tune the animation effects of the CustomAnimatedSwitcher
using the following parameters:
animationDuration
:
animationDuration
parameter (a Duration
value) to control how long the transition animation takes.animationDuration: const Duration(milliseconds: 300)
for a quick animation.animationCurve
:
animationCurve
parameter (a Curve
object) determines the speed curve for the primary scale and clip reveal animation (when using clipReveal
animationType) and the scale animation in slideFade
type animation.Curves.easeInOut
, Curves.easeIn
, Curves.bounceOut
) or create your own custom curve.animationCurve: Curves.easeOutQuad
for an animation that starts quickly and slows down.slideCurve
:
slideCurve
parameter (a Curve
object) determines the speed curve for the slide animation effect when using slideFade
animation type.Curves.easeInOut
, Curves.easeIn
, Curves.bounceOut
) or create your own custom curve.slideCurve: Curves.easeOutCubic
for a slide that starts quickly and slows down.fadeCurve
:
fadeCurve
parameter (a Curve
object) controls the speed curve for the fade effect that happens with secondaryWidget
in all the animations.Curves.easeInOut
, Curves.easeIn
, Curves.bounceOut
) or create your own custom curve.fadeCurve: Curves.easeInSine
for a fade that starts slow and gains speed.clipper
:
clipper
parameter lets you use a CustomClipper<Path>
to define the shape of the reveal effect during the clipReveal
animation or the slide-in effect in slideFade
.CircularRevealClipper
, RectangularRevealClipper
, or implement your own custom clipper for unique shapes.clipper: const CircularRevealClipper()
or clipper: MyCustomClipper()
horizontalSlide
:
horizontalSlide
parameter (a boolean) affects the slide direction when using the slideFade
animationType.
horizontalSlide
to true
causes the primaryWidget
to slide in horizontally (from right to left).horizontalSlide
to false
causes the primaryWidget
to slide in vertically (from top to bottom).horizontalSlide: false
for a vertical slide transition.By combining these customization options, you can create a wide range of transition effects.
To create a custom clipping effect you can create your own CustomClipper<Path>
. Here’s an example of a rectangular clipper that you can use as a base for more complex effects.
class RectangularRevealClipper extends CustomClipper<Path> {
const RectangularRevealClipper();
@override
Path getClip(Size size) {
return Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
This project is licensed under the MIT License. See the LICENSE file for details.
See the CHANGELOG.md file for a detailed history of changes.
Contributions are welcome! Please feel free to submit issues or pull requests.