image

26 Jan 2026

9K

35K

Creating a Flip Animation Card Widget for a Quiz App in Flutter

In the realm of mobile application development, user experience often hinges on intuitive and engaging visual feedback. For quiz applications, presenting questions and answers in an interactive way can significantly enhance user engagement. A flip animation card widget is an excellent design choice for this, allowing users to tap a card to reveal the answer or additional information with a smooth, satisfying 3D flip effect.

This article will guide you through the process of building a reusable flip animation card widget in Flutter. We'll cover the core concepts of Flutter animations, including AnimationController, Tween, and Transform widgets, to achieve a realistic 3D card flip.

Understanding the Core Principles

Before diving into the code, let's briefly touch upon the key Flutter animation concepts we'll be utilizing:

  • AnimationController: This is the primary driver of an animation. It generates a new value whenever the animation "ticks." We'll use it to control the duration and playback (forward, reverse) of our flip animation.
  • Tween: A Tween defines a range of values over which an animation should operate (e.g., from 0.0 to 1.0 for a rotation). It calculates interpolated values between its begin and end points.
  • CurvedAnimation: This wraps another Animation and applies a non-linear curve to its value. We'll use it to make the flip animation feel more natural.
  • AnimatedBuilder: This widget listens to an Animation and rebuilds its widget tree whenever the animation value changes. This is more efficient than calling setState directly within the animation listener.
  • Transform with Matrix4: To create a 3D flip effect, we'll use the Transform widget with a Matrix4 to apply a perspective transformation and a Y-axis rotation. The Matrix4.identity()..setEntry(3, 2, 0.001) part is crucial for adding the perspective effect, making the card appear to recede into space as it rotates.

Step-by-Step Implementation

1. Creating the FlipCard Widget Structure

We'll start by defining a StatefulWidget called FlipCard, as animations typically require managing state over time. We'll also need a TickerProvider for our AnimationController, which is usually provided by mixing in SingleTickerProviderStateMixin.


import 'dart:math' as math;
import 'package:flutter/material.dart';

class FlipCard extends StatefulWidget {
  final Widget front;
  final Widget back;
  final bool initiallyFlipped;
  final Duration animationDuration;
  final AxisDirection flipDirection;

  const FlipCard({
    Key? key,
    required this.front,
    required this.back,
    this.initiallyFlipped = false,
    this.animationDuration = const Duration(milliseconds: 500),
    this.flipDirection = AxisDirection.left, // Can be left or right
  }) : super(key: key);

  @override
  _FlipCardState createState() => _FlipCardState();
}

class _FlipCardState extends State
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation _animation;
  bool _isFront = true;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: widget.animationDuration,
    );

    _animation = Tween(begin: 0, end: 1).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Curves.easeInOut,
      ),
    );

    if (widget.initiallyFlipped) {
      _controller.value = 1.0;
      _isFront = false;
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _flipCard() {
    if (_isFront) {
      _controller.forward();
    } else {
      _controller.reverse();
    }
    setState(() {
      _isFront = !_isFront;
    });
  }

  @override
  Widget build(BuildContext context) {
    // We'll fill this in the next step
    return Container(); 
  }
}

2. Implementing the Animation and UI

Now, let's implement the build method using AnimatedBuilder and Transform. We'll also use GestureDetector to trigger the _flipCard method.


import 'dart:math' as math; // Make sure this import is present
import 'package:flutter/material.dart';

// ... (FlipCard and _FlipCardState definition from previous step) ...

class _FlipCardState extends State
    with SingleTickerProviderStateMixin {
  // ... (initState, dispose, _flipCard methods from previous step) ...

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _flipCard,
      child: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          final double rotationValue = _animation.value * math.pi; // 0 to pi
          final double transformX = (widget.flipDirection == AxisDirection.left) ? -1.0 : 1.0;

          // Determine which side is visible
          final bool isFrontVisible = _animation.value <= 0.5;
          final Widget currentChild = isFrontVisible ? widget.front : widget.back;
          
          // Apply rotation only after 0.5 for the back side
          // and mirror the back side content if rotating beyond 90 degrees
          final double effectiveRotation = isFrontVisible 
              ? rotationValue 
              : rotationValue + math.pi; // Adjust for back side to prevent mirror effect

          return Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001) // Add perspective
              ..rotateY(effectiveRotation * transformX), // Rotate around Y-axis
            child: Container(
              // Optional: Add some decoration to the card itself
              decoration: BoxDecoration(
                color: Theme.of(context).cardColor,
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.1),
                    spreadRadius: 2,
                    blurRadius: 5,
                    offset: Offset(0, 3),
                  ),
                ],
              ),
              child: Center(
                // Ensure content of the back side isn't mirrored
                child: isFrontVisible 
                    ? widget.front 
                    : Transform(
                        alignment: Alignment.center,
                        transform: Matrix4.identity()..rotateY(math.pi), // Mirror back content
                        child: widget.back,
                      ),
              ),
            ),
          );
        },
      ),
    );
  }
}

Correction for back side rendering: The previous implementation of the AnimatedBuilder might cause the back side content to appear mirrored during the rotation. To fix this, we need to apply an additional Y-axis rotation to the back widget itself, essentially mirroring its content, so that it appears correctly oriented when the card is fully flipped. This ensures text and images on the back are readable.

Let's refine the AnimatedBuilder's child logic:


// ... (imports and _FlipCardState definition) ...

class _FlipCardState extends State
    with SingleTickerProviderStateMixin {
  // ... (initState, dispose, _flipCard methods) ...

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _flipCard,
      child: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          final double rotationValue = _animation.value * math.pi; // 0 to pi (180 degrees)

          // Determine which side is currently visible based on rotation progress
          final bool isFront = rotationValue <= math.pi / 2; // Front visible for first 90 degrees

          return Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity()
              ..setEntry(3, 2, 0.001) // Add perspective
              ..rotateY(rotationValue), // Rotate around Y-axis
            child: Container(
              // Optional: decoration for the card itself
              decoration: BoxDecoration(
                color: Theme.of(context).cardColor,
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.1),
                    spreadRadius: 2,
                    blurRadius: 5,
                    offset: Offset(0, 3),
                  ),
                ],
              ),
              child: Center(
                // Only show the relevant side.
                // If it's the back side, apply an additional 180-degree Y rotation
                // to make its content appear correctly oriented.
                child: isFront
                    ? widget.front
                    : Transform(
                        alignment: Alignment.center,
                        transform: Matrix4.identity()..rotateY(math.pi), // Mirror the back content
                        child: widget.back,
                      ),
              ),
            ),
          );
        },
      ),
    );
  }
}

The flipDirection property was removed for simplicity in the refined code, as a basic Y-axis rotation from 0 to pi already handles the visual flip. If more complex left/right flipping behavior is desired (e.g., rotating from the left edge vs. right edge), that would require more intricate `Matrix4` manipulation or `Transform.rotate` with specific `origin` points, but for a standard card flip, the current approach is clean and effective.

3. Integrating into a Quiz Screen

Now that our FlipCard widget is ready, let's see how we can integrate it into a simple quiz application screen. You can use it within a ListView.builder for multiple quiz cards, or just display a single card at a time.


import 'package:flutter/material.dart';
// Assuming your FlipCard widget is in 'widgets/flip_card.dart'
// import 'package:your_app_name/widgets/flip_card.dart'; 

class QuizScreen extends StatefulWidget {
  const QuizScreen({Key? key}) : super(key: key);

  @override
  State createState() => _QuizScreenState();
}

class _QuizScreenState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Quiz App'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: SizedBox(
            width: 300,
            height: 200,
            child: FlipCard(
              front: Container(
                alignment: Alignment.center,
                padding: const EdgeInsets.all(20),
                child: const Text(
                  "What is the capital of France?",
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 22,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
                decoration: BoxDecoration(
                  color: Colors.blueAccent,
                  borderRadius: BorderRadius.circular(12),
                ),
              ),
              back: Container(
                alignment: Alignment.center,
                padding: const EdgeInsets.all(20),
                child: const Text(
                  "Paris",
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 28,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
                decoration: BoxDecoration(
                  color: Colors.green,
                  borderRadius: BorderRadius.circular(12),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

// To run this app:
// void main() {
//   runApp(MaterialApp(
//     home: QuizScreen(),
//   ));
// }

Conclusion

You have successfully created a reusable flip animation card widget for your Flutter quiz application! This widget not only adds a visually appealing interaction but also serves as a robust foundation for various card-based UIs. By leveraging Flutter's powerful animation framework, specifically AnimationController, Tween, and Transform with Matrix4, we achieved a smooth and realistic 3D flip effect.

This widget can be further enhanced with features like:

  • Swipe gestures to flip.
  • Callbacks for when the card finishes flipping.
  • Programmatic control to flip the card from outside the widget.
  • Support for different flip axes (X-axis for vertical flips).

By incorporating such interactive elements, you can significantly elevate the user experience of your Flutter applications, making them more dynamic and enjoyable.

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