How to Implementing a Scratcher in Flutter ?

Spread the love

 

scrach card in flutter
How to Implementing a Scratcher in Flutter ?
Scratch card
How to Implementing a Scratcher in Flutter ?

Introduction

The scratcher package in Flutter allows you to create a scratch-off effect, similar to lottery tickets, where users can swipe to reveal hidden content. This guide will show you how to use this package to implement a Scratcher in your Flutter app.

Content

1.Add the scratcher dependency:

Open your pubspec.yaml file and add the scratcher dependency.

Run flutter pub get to install the package.

 

2.Import the package:

Import the scratcher package in your Dart file.


import 'package:scratcher/scratcher.dart';

 

3.Use the Scratcher widget:

Wrap the content you want to scratch off with the Scratcher widget. For example, if you want to reveal an image, you can use an Image widget as the child of the Scratcher.


Scratcher(
  brushSize: 30, // Size of the brush
  threshold: 50, // Amount of scratching needed to reveal the content
  color: Colors.grey, // Color of the scratcher surface
  onChange: (value) {
    // Callback when the user is scratching
  },
  onThreshold: () {
    // Callback when enough scratching has been done to reveal the content
  },
  child: Image.asset('assets/image_to_reveal.png'),
)

 

Customize the brushSize, threshold, and color properties according to your design and requirements. The onChange callback is called while the user is scratching, and the onThreshold callback is called when the user has scratched enough to reveal the content.

 

4.Enhance the user experience (optional):

You can enhance the user experience by adding animations, sounds, or feedback when the content is revealed. For example, you can show a congratulatory message or play a sound effect.

 

5.Test your implementation:

Run your app and test the Scratcher to ensure it behaves as expected. Try different brush sizes and scratching thresholds to find the best configuration for your app.

Sample Code


// ignore_for_file: prefer_const_literals_to_create_immutables, prefer_const_constructors

import 'package:flutter/material.dart';

import 'package:scratcher/scratcher.dart';
// import 'advanced.dart';
// import 'basic.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DefaultTabController(
        length: 2,
        child: Scaffold(
          bottomNavigationBar: SafeArea(
            child: TabBar(
              labelColor: Colors.blueAccent,
              unselectedLabelColor: Colors.blueGrey,
              indicatorColor: Colors.blueAccent,
              indicatorSize: TabBarIndicatorSize.label,
              tabs: [
                Tab(icon: Icon(Icons.looks_one)),
                Tab(icon: Icon(Icons.looks_two)),
              ],
            ),
          ),
          body: TabBarView(
            physics: const NeverScrollableScrollPhysics(),
            children: [
              AdvancedScreen(),
              BasicScreen(),
            ],
          ),
        ),
      ),
    );
  }
}

class BasicScreen extends StatefulWidget {
  @override
  _BasicScreenState createState() => _BasicScreenState();
}

class _BasicScreenState extends State<BasicScreen> {
  double brushSize = 30;
  double progress = 0;
  bool thresholdReached = false;
  bool enabled = true;
  double? size;
  final key = GlobalKey<ScratcherState>();

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(
                child: const Text('Reset'),
                onPressed: () {
                  key.currentState?.reset(
                    duration: const Duration(milliseconds: 2000),
                  );
                  setState(() => thresholdReached = false);
                },
              ),
              ElevatedButton(
                child: const Text('Change size'),
                onPressed: () {
                  setState(() {
                    if (size == null) {
                      size = 200;
                    } else if (size == 200) {
                      size = 0;
                    } else {
                      size = null;
                    }
                  });
                },
              ),
              ElevatedButton(
                child: const Text('Reveal'),
                onPressed: () {
                  key.currentState?.reveal(
                    duration: const Duration(milliseconds: 2000),
                  );
                },
              ),
            ],
          ),
          Column(
            children: [
              Text('Brush size (${brushSize.round()})'),
              Slider(
                value: brushSize,
                onChanged: (v) => setState(() => brushSize = v),
                min: 5,
                max: 100,
              ),
            ],
          ),
          CheckboxListTile(
            value: enabled,
            title: Text('Scratcher enabled'),
            onChanged: (e) => setState(() {
              enabled = e ?? false;
            }),
          ),
          Expanded(
            child: Stack(
              children: [
                SizedBox(
                  height: size,
                  width: size,
                  child: Scratcher(
                    key: key,
                    enabled: enabled,
                    brushSize: brushSize,
                    threshold: 30,
                    image: Image.network(
                        'https://c8.alamy.com/comp/2D6N8NX/gift-card-voucher-certificate-or-coupon-vector-design-template-discount-banner-layout-for-seasonal-holidays-sale-abstract-3d-multicolor-plastic-ge-2D6N8NX.jpg'),
                    onThreshold: () => setState(() => thresholdReached = true),
                    onChange: (value) {
                      setState(() {
                        progress = value;
                      });
                    },
                    onScratchStart: () {
                      print("Scratching has started");
                    },
                    onScratchUpdate: () {
                      print("Scratching in progress");
                    },
                    onScratchEnd: () {
                      print("Scratching has finished");
                    },
                    child: Container(
                      color: Colors.black,
                      alignment: Alignment.center,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          const Text(
                            'Scratch the screen!',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 36,
                              fontWeight: FontWeight.bold,
                              color: Colors.amber,
                            ),
                          ),
                          SizedBox(height: 8),
                          const Text(
                            'add here your scratch coupon code or photo',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 12,
                              fontWeight: FontWeight.bold,
                              color: Colors.amber,
                            ),
                          )
                        ],
                      ),
                    ),
                  ),
                ),
                Positioned(
                  bottom: 0,
                  right: 0,
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 8,
                      vertical: 12,
                    ),
                    color: Colors.black,
                    child: Text(
                      '${progress.floor().toString()}% '
                      '(${thresholdReached ? 'done' : 'pending'})',
                      textAlign: TextAlign.right,
                      style: const TextStyle(
                        color: Colors.white,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

const _googleIcon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Thank-you-transparent.svg/800px-Thank-you-transparent.svg.png';
const _dartIcon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Thank-you-transparent.svg/800px-Thank-you-transparent.svg.png';
const _flutterIcon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Thank-you-transparent.svg/800px-Thank-you-transparent.svg.png';

class AdvancedScreen extends StatefulWidget {
  @override
  _AdvancedScreenState createState() => _AdvancedScreenState();
}

class _AdvancedScreenState extends State<AdvancedScreen> with SingleTickerProviderStateMixin {
  double validScratches = 0;
  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void initState() {
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1200),
    )..addStatusListener(
        (listener) {
          if (listener == AnimationStatus.completed) {
            _animationController.reverse();
          }
        },
      );
    _animation = Tween(begin: 1.0, end: 1.25).animate(
      CurvedAnimation(
        parent: _animationController,
        curve: Curves.elasticIn,
      ),
    );
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const Text(
                  'Scratcher',
                  style: TextStyle(
                    fontFamily: 'The unseen',
                    color: Colors.blueAccent,
                    fontSize: 50,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const Text(
                  'scratch to win!',
                  style: TextStyle(
                    fontFamily: 'The unseen',
                    color: Colors.black,
                    fontSize: 20,
                  ),
                ),
                Container(
                  margin: const EdgeInsets.only(top: 10),
                  height: 1,
                  width: 300,
                  color: Colors.black12,
                )
              ],
            ),
            buildRow(_googleIcon, _flutterIcon, _googleIcon),
            buildRow(_dartIcon, _flutterIcon, _googleIcon),
            buildRow(_dartIcon, _flutterIcon, _dartIcon),
          ],
        ),
      ),
    );
  }

  Widget buildRow(String left, String center, String right) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ScratchBox(image: left),
        ScratchBox(
          image: center,
          animation: _animation,
          onScratch: () {
            setState(() {
              validScratches++;
              if (validScratches == 3) {
                _animationController.forward();
              }
            });
          },
        ),
        ScratchBox(image: right),
      ],
    );
  }
}

class ScratchBox extends StatefulWidget {
  ScratchBox({
    required this.image,
    this.onScratch,
    this.animation,
  });

  final String image;
  final VoidCallback? onScratch;
  final Animation<double>? animation;

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

class _ScratchBoxState extends State<ScratchBox> {
  bool isScratched = false;
  double opacity = 0.6;

  @override
  Widget build(BuildContext context) {
    var icon = AnimatedOpacity(
      opacity: opacity,
      duration: const Duration(milliseconds: 750),
      child: Image.network(
        widget.image,
        width: 115,
        height: 115,
        fit: BoxFit.cover,
      ),
    );

    return Container(
      width: 110,
      height: 110,
      margin: const EdgeInsets.symmetric(horizontal: 10),
      child: Scratcher(
        accuracy: ScratchAccuracy.high,
        color: Colors.blueGrey,
        image: Image.network('https://play-lh.googleusercontent.com/A1gsZWGEWlKhqKPbnJajuByaOlvpanDEqTBNG3gMveS65YmG5rICZN4poKBfeMtnRTI',
            fit: BoxFit.contain),
        brushSize: 15,
        threshold: 60,
        onThreshold: () {
          setState(() {
            opacity = 1;
            isScratched = true;
          });
          widget.onScratch?.call();
        },
        child: Container(
          child: widget.animation == null
              ? icon
              : ScaleTransition(
                  scale: widget.animation!,
                  child: icon,
                ),
        ),
      ),
    );
  }
}

Output

scrach card in flutter
How to Implementing a Scratcher in Flutter ?
Scratch card
How to Implementing a Scratcher in Flutter ?

Conclusion

Implementing a Scratcher in your Flutter app using the scratcher package can add an interactive and engaging element for users. By following this guide, you can easily create a scratch-off effect that allows users to reveal hidden content with a swipe gesture.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *