1

Set Up the Main App Entry

Create the entry point of your Flutter app and the main widget that launches the scratch card game screen.

💡 Tip: Use the button on the right side to easily copy the code.

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

void main() {
    runApp(const ScratchCardGame());
}

class ScratchCardGame extends StatelessWidget {
    const ScratchCardGame({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Scratch to Win!',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
        primarySwatch: Colors.blue,
        fontFamily: 'Roboto',
        ),
        home: const ScratchGameScreen(),
    );
    }
}

2

Build the Scratch Game Screen Scaffold

Set up the ScratchGameScreen widget, the main screen where the game will appear. Add the layout and background.

💡 Tip: Use the button on the right side to easily copy the code.

class ScratchGameScreen extends StatefulWidget {
    const ScratchGameScreen({Key? key}) : super(key: key);
    
    @override
    _ScratchGameScreenState createState() => _ScratchGameScreenState();
    }
    
    class _ScratchGameScreenState extends State<ScratchGameScreen>
        with SingleTickerProviderStateMixin {
    late ConfettiController _confettiController;
    final List<String> prizes = ['🎁 Gift Card', '💰 Cash Prize', '🎮 Gaming Console', '🎯 Try Again'];
    String? selectedPrize;
    bool isRevealed = false;
    double scratchProgress = 0.0;
    int cardKey = 0;
    
    @override
    void initState() {
        super.initState();
        _confettiController = ConfettiController(duration: const Duration(seconds: 2));
        selectedPrize = prizes[Random().nextInt(prizes.length)];
    }
    
    @override
    void dispose() {
        _confettiController.dispose();
        super.dispose();
    }
    
    void _resetGame() {
        setState(() {
        isRevealed = false;
        scratchProgress = 0.0;
        selectedPrize = prizes[Random().nextInt(prizes.length)];
        cardKey++;
        });
    }
    }
                                  
3

Add Scratch Progress and Reveal Logic

Create functions to handle scratch card progress and prize reveal.

💡 Tip: Use the button on the right side to easily copy the code.

void _onScratchUpdate(double progress) {
    setState(() {
        scratchProgress = progress;
        if (progress &gt; 0.7 &amp;&amp; !isRevealed) {
        _revealPrize();
        }
    });
    }
    
    void _revealPrize() {
    setState(() {
        isRevealed = true;
    });
    if (selectedPrize != '🎯 Try Again') {
        _confettiController.play();
    }
    }
                                                      
4

Build the Main Game Interface

Add the layout to display instructions, the scratch card, and a reset button if the prize is revealed.

💡 Tip: Use the button on the right side to easily copy the code.

@override
Widget build(BuildContext context) {
    return Scaffold(
    backgroundColor: Colors.blue[50],
    body: Stack(
        children: [
        Center(
            child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
                const Text('Scratch to Win!', style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.blue)),
                const SizedBox(height: 20),
                ScratchCard(
                key: ValueKey(cardKey),
                prize: selectedPrize ?? '',
                onScratchUpdate: _onScratchUpdate,
                isRevealed: isRevealed,
                ),
                if (isRevealed) ElevatedButton(onPressed: _resetGame, child: const Text('Play Again')),
            ],
            ),
        ),
        ],
    ),
    );
}

5

Add Confetti Animation

Add confetti animation to celebrate when the player wins.

And add confetti: ^0.7.0 in your pubspec.yaml file.
💡 Tip: Use the button on the right side to easily copy the code.

Align(
    alignment: Alignment.topCenter,
    child: ConfettiWidget(
        confettiController: _confettiController,
        blastDirection: pi / 2,
        maxBlastForce: 5,
        minBlastForce: 2,
        emissionFrequency: 0.05,
        numberOfParticles: 50,
        gravity: 0.1,
        colors: const [Colors.green, Colors.blue, Colors.pink, Colors.orange, Colors.purple],
    ),
    );
                              
6

Create the Scratch Card Widget

Build the ScratchCard widget that the user can scratch to reveal a prize.

💡 Tip: Use the button on the right side to easily copy the code.

class ScratchCard extends StatefulWidget {
    final String prize;
    final Function(double) onScratchUpdate;
    final bool isRevealed;
    
    const ScratchCard({
        Key? key,
        required this.prize,
        required this.onScratchUpdate,
        required this.isRevealed,
    }) : super(key: key);
    
    @override
    _ScratchCardState createState() => _ScratchCardState();
    }
    

7

Define the Scratch Effect and Logic

Define how the scratch effect is implemented, handling touch events and updating scratch progress.

💡 Tip: Use the button on the right side to easily copy the code.

class _ScratchCardState extends State&lt;ScratchCard&gt; {
    final _scratchKey = GlobalKey();
    final List&lt;Offset&gt; _scratchPoints = [];
    ui.Image? _scratchImage;
    bool _isLoading = true;
    
    void _onPanUpdate(DragUpdateDetails details) {
        if (widget.isRevealed) return;
        final RenderBox renderBox = _scratchKey.currentContext!.findRenderObject() as RenderBox;
        final position = renderBox.globalToLocal(details.globalPosition);
        setState(() {
        _scratchPoints.add(position);
        });
        widget.onScratchUpdate(_scratchPoints.length / 1200);
    }
    }  
8

Customize Scratch Effect with Custom Painter

Define a ScratchPainter to apply a scratchable overlay effect on the card.

💡 Tip: Use the button on the right side to easily copy the code.

class ScratchPainter extends CustomPainter {
    final List<Offset> points;
    final ui.Image scratchImage;
    
    ScratchPainter({required this.points, required this.scratchImage});
    
    @override
    void paint(Canvas canvas, Size size) {
        final paint = Paint()..blendMode = BlendMode.clear;
        canvas.drawImage(scratchImage, Offset.zero, Paint());
    
        for (var point in points) {
        canvas.drawCircle(point, 20, paint);
        }
    }
    
    @override
    bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
    }
                                
 
Step 1 of 8