image

11 May 2026

9K

35K

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects

Flutter's declarative UI framework, combined with its powerful animation capabilities, allows developers to craft highly engaging and intuitive user experiences. Smooth transitions and subtle animations not only delight users but also provide crucial visual feedback, guiding them through the application. This article delves into implementing various "slide and scale" animations within Flutter, covering common scenarios such as card tap interactions, modal transitions, page transitions, and notification animations.

1. Slide & Scale Animation on Card Tap

Animating a card upon tap provides immediate visual feedback, indicating interactivity. A common effect is to slightly scale the card up or down and potentially add a subtle slide effect.

This can be achieved using an AnimatedContainer for simpler effects or a combination of GestureDetector, AnimationController, and Transform.scale for more fine-grained control.

Example: Simple Scale on Card Tap using AnimatedContainer


import 'package:flutter/material.dart';

class TappableAnimatedCard extends StatefulWidget {
  @override
  _TappableAnimatedCardState createState() => _TappableAnimatedCardState();
}

class _TappableAnimatedCardState extends State {
  bool _isTapped = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _isTapped = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _isTapped = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _isTapped = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: _handleTapDown,
      onTapUp: _handleTapUp,
      onTapCancel: _handleTapCancel,
      onTap: () {
        print("Card tapped!");
        // Perform actual card action here
      },
      child: AnimatedContainer(
        duration: Duration(milliseconds: 150),
        curve: Curves.easeOut,
        width: _isTapped ? 150.0 * 0.95 : 150.0,
        height: _isTapped ? 100.0 * 0.95 : 100.0,
        decoration: BoxDecoration(
          color: Colors.blueAccent,
          borderRadius: BorderRadius.circular(15),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.2),
              blurRadius: 8,
              offset: Offset(0, 4),
            ),
          ],
        ),
        alignment: Alignment.center,
        child: Text(
          'Tap Me!',
          style: TextStyle(color: Colors.white, fontSize: 20),
        ),
      ),
    );
  }
}

2. Modal Transition Animation

Modals, such as dialogs or bottom sheets, often benefit from engaging entry and exit animations. A common pattern is for a modal to slide in from the bottom or center, possibly with a fade or scale effect, creating a more natural and less jarring user experience.

Flutter's showGeneralDialog combined with a custom PageRouteBuilder or directly animating an OverlayEntry offers robust control over modal transitions.

Example: Scale & Fade Modal Transition using showGeneralDialog


import 'package:flutter/material.dart';

class ModalAnimationDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {
          _showAnimatedDialog(context);
        },
        child: Text('Show Animated Modal'),
      ),
    );
  }

  void _showAnimatedDialog(BuildContext context) {
    showGeneralDialog(
      context: context,
      barrierColor: Colors.black.withOpacity(0.5), // Background color
      transitionDuration: Duration(milliseconds: 300), // Animation duration
      pageBuilder: (context, anim1, anim2) {
        return Center(
          child: Material(
            color: Colors.transparent, // Required for custom dialog
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: MediaQuery.of(context).size.height * 0.3,
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(20),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Hello from Animated Modal!', textAlign: TextAlign.center),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: () => Navigator.of(context).pop(),
                    child: Text('Close'),
                  ),
                ],
              ),
            ),
          ),
        );
      },
      transitionBuilder: (context, anim1, anim2, child) {
        // Scale and fade animation
        return Transform.scale(
          scale: anim1.value,
          child: Opacity(
            opacity: anim1.value,
            child: child,
          ),
        );
      },
    );
  }
}

3. Page Transition Animation

Navigating between pages is a fundamental aspect of any application, and custom page transitions can significantly enhance the user experience. Instead of abrupt jumps, a smooth animation can guide the user's eye and provide a sense of continuity.

PageRouteBuilder is the go-to widget for creating bespoke page transitions in Flutter, allowing you to define how the new page enters and the old page exits.

Example: Slide & Fade Page Transition using PageRouteBuilder


import 'package:flutter/material.dart';

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First Page')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).push(
              _createRoute(),
            );
          },
          child: Text('Go to Second Page'),
        ),
      ),
    );
  }

  Route _createRoute() {
    return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => SecondPage(),
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        // Slide from right
        var begin = Offset(1.0, 0.0);
        var end = Offset.zero;
        var curve = Curves.ease;

        var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

        // Fade in
        var fadeTween = Tween(begin: 0.0, end: 1.0).chain(CurveTween(curve: curve));

        return SlideTransition(
          position: animation.drive(tween),
          child: FadeTransition(
            opacity: animation.drive(fadeTween),
            child: child,
          ),
        );
      },
      transitionDuration: Duration(milliseconds: 500),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second Page')),
      body: Center(
        child: Text('Welcome to the Second Page!'),
      ),
    );
  }
}

4. Notification Animation

Transient notifications, like custom "toasts" or "snackbars," can be made more elegant and less intrusive with subtle animations. A common pattern is for them to slide in from the top or bottom and then slide out after a short duration.

Implementing custom notification animations often involves using OverlayEntry to display widgets on top of the entire application, combined with an AnimationController to manage their entry and exit.

Example: Slide-In/Slide-Out Notification Animation


import 'package:flutter/material.dart';

class NotificationAnimationDemo extends StatefulWidget {
  @override
  _NotificationAnimationDemoState createState() => _NotificationAnimationDemoState();
}

class _NotificationAnimationDemoState extends State with SingleTickerProviderStateMixin {
  OverlayEntry? _overlayEntry;
  AnimationController? _animationController;
  Animation? _slideAnimation;
  Animation? _fadeAnimation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 300),
      reverseDuration: Duration(milliseconds: 200),
    );

    _slideAnimation = Tween(
      begin: Offset(0, -1), // Start off-screen above
      end: Offset(0, 0.0), // End at top of screen (relative to overlay)
    ).animate(CurvedAnimation(
      parent: _animationController!,
      curve: Curves.easeOut,
      reverseCurve: Curves.easeIn,
    ));

    _fadeAnimation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(
      parent: _animationController!,
      curve: Curves.easeOut,
      reverseCurve: Curves.easeIn,
    ));
  }

  @override
  void dispose() {
    _animationController?.dispose();
    super.dispose();
  }

  void _showNotification(String message) {
    _overlayEntry?.remove(); // Remove existing if any
    _overlayEntry = _createOverlayEntry(message);
    Overlay.of(context).insert(_overlayEntry!);

    _animationController!.forward().then((_) {
      Future.delayed(Duration(seconds: 3), () {
        _hideNotification();
      });
    });
  }

  void _hideNotification() {
    _animationController!.reverse().then((_) {
      _overlayEntry?.remove();
      _overlayEntry = null;
    });
  }

  OverlayEntry _createOverlayEntry(String message) {
    return OverlayEntry(
      builder: (context) => Positioned(
        top: 0,
        width: MediaQuery.of(context).size.width,
        child: SlideTransition(
          position: _slideAnimation!,
          child: FadeTransition(
            opacity: _fadeAnimation!,
            child: Material(
              color: Colors.transparent, // Make sure Material doesn't block hits
              child: SafeArea(
                child: Container(
                  padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15),
                  color: Colors.green.withOpacity(0.8),
                  child: Text(
                    message,
                    textAlign: TextAlign.center,
                    style: TextStyle(color: Colors.white, fontSize: 16),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () => _showNotification("Item added to cart successfully!"),
        child: Text('Show Notification'),
      ),
    );
  }
}

Conclusion

Flutter's animation system provides a rich and flexible toolkit for bringing user interfaces to life. By leveraging widgets like AnimatedContainer, Transform.scale, SlideTransition, FadeTransition, and powerful constructs such as PageRouteBuilder and OverlayEntry, developers can implement sophisticated slide and scale effects across various UI components. Mastering these techniques not only enhances the aesthetic appeal of an application but also significantly improves its usability and overall user satisfaction, creating truly dynamic and memorable experiences.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is