Site Juggler
  • React
  • Flutter
No Result
View All Result
Site Juggler
  • React
  • Flutter
No Result
View All Result
Site Juggler
No Result
View All Result
Home Flutter

How to Make Reward Screen UI in Flutter Like Google Pay Complete Guide

Hemant Singh by Hemant Singh
May 16, 2023
in Flutter
0 0
0
Reward Screen UI in Flutter

Reward Screen UI in Flutter

0
SHARES
43
VIEWS
Share on FacebookShare on Twitter

When building a mobile application, creating a rewards screen is an essential component of keeping users engaged. A rewards screen shows users their progress towards earning rewards and motivates them to keep using the app. In this context, Flutter provides a powerful framework for building a rewards screen(Reward Screen UI in Flutter) that is both aesthetically pleasing and highly functional.

In this project, we will be exploring how to build a user interface for a rewards screen that is similar to the Google Pay Rewards screen using Flutter widgets. By the end of this project, you will learn how to create an adaptive UI that includes a custom SliverAppBar, and a button widget with text and an icon. This tutorial assumes that you have a basic understanding of Flutter widgets and the Dart programming language.

Table of Contents

  • Understanding the Rewards UI of Google Pay
  • Implementing the Rewards Screen UI in Flutter
  • Creating the SilverAppBar
    • Solving the Overlapping by Adding Scroll Controller
  • Coming towards our body part
    • Widget Function buildRewardsbody
    • Stateless Class UpcomingRewardsButton
    • Widget Function buildRewardsContainer
  • Output
  • Full Source Code
  • Conclusion

Understanding the Rewards UI of Google Pay

Let’s break it down. When you open Google Pay and go to the rewards screen, you will see a grid view of cards. Some cards may be scratched off, and you can also see your past rewards.

If you click on any card, it will take you to a new screen where you can see the card details. This screen has a transparent background and a draggable sheet at the bottom. As you drag the sheet up, an animated container gets smaller. At the top, there are menu and cross buttons on the right and left, respectively.

So, we need to create two screens. The first screen will show the grid view of rewards, and the second screen will show the details of a selected reward.

but in this article, we will learn the flutter implementation of the first screen to keep this article short and simple you can check the next article for the reward detail screen

Implementing the Rewards Screen UI in Flutter

To create a custom app bar similar to the one in the Google Pay reward screen, we first need to use the silver app bar. We can do this by creating a stateful class and using the scaffold body to provide a nested scroll view.

Essentially, we want to create an app bar that is similar to the one in Google Pay rewards. To do this, we’ll use a silver app bar. This requires creating a stateful class, which allows us to create a bar that can change over time.

Then, we’ll use a scaffold body to create a nested scroll view. This will allow us to create a scrolling bar that works well with the rest of the page.

NestedScrollView(
  headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[
      SliverAppBar(
        pinned: true,
        // add other properties here
        flexibleSpace: FlexibleSpaceBar(
          // add flexibleSpace content here
        ),
      ),
    ];
  },
  body: CustomScrollView(
    // add body content here
  ),
)

In this code, we are creating a NestedScrollView widget, which is used to create a scrollable view with multiple layers of widgets. The NestedScrollView takes a headerSliverBuilder argument, which is a callback function that returns a list of slivers that should be placed above the main scrollable area.

Creating the SilverAppBar

 SliverAppBar(
                elevation: 0,
                backgroundColor:_isShrink? Colors.white:Colors.transparent,
                pinned: true,
                expandedHeight: height,

                leading: _isShrink
                    ? const BackButton(
                  color: Colors.black, // <-- SEE HERE
                )
                : null,

                title: _isShrink
                    ? const Text(
                        "₹62 total rewards",
                        style: TextStyle(color: Colors.black,fontWeight: FontWeight.normal,fontSize: 18),
                      )
                    : null,
                actions: [
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(
                      Icons.more_vert,
                      color: Colors.black,
                    ),
                  ),
                ],
                flexibleSpace: FlexibleSpaceBar(
                  collapseMode: CollapseMode.parallax,
                  background: SafeArea(
                    child: Stack(
                      children: [
                        Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            alignment: Alignment.topCenter,
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: [
                                const Text(
                                  "₹62",style: 
                                          TextStyle(color: Colors.black,
                                          fontWeight: FontWeight.normal,
                                          fontSize: 24),
                                ),
                                const Text(
                                  "Total rewards",style: TextStyle(color: Colors.black),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),

To create a collapsible app bar in Flutter like Google Pay, you can use the SliverAppBar widget. Set the elevation property to 0 for no shadow effect and set the backgroundColor to transparent when fully expanded and white when collapsed. Use pinned to keep the app bar visible as the user scrolls and set the expandedHeight to determine the size when fully expanded. Use the leading property to specify a back button widget, title to specify a text widget that appears in the center, and actions to specify an IconButton widget with the Icons.more_vert icon to open a menu. Finally, use the flexibleSpace property to create the background of the app bar, which can display user information or other content.

Overlapped Text when its expanded
Overlapped Text when its expanded

Solving the Overlapping by Adding Scroll Controller

As you can see, a potential problem is that when the app bar is expanded, the text in the app bar can overlap or appear merged together, which can make it difficult to read. However, the text appears properly when the app bar is collapsed

To fix the issue of overlapping or merged text in the SliverAppBar widget when it’s expanded, you can add a _scrollController to your code. This controller will keep track of the user’s scrolling behavior and can be used to make the app bar disappear as the user scrolls up, which frees up space on the screen and prevents the text from overlapping. This approach can help ensure that the app bar remains useful and readable, even when it’s expanded.

First, we define a _scrollController variable and set it to null. We also define a lastStatus boolean variable and set it to true.

  ScrollController? _scrollController;
  bool lastStatus = true;

Next, we define a _scrollListener function that will be called whenever the user scrolls on the screen. This function checks to see if the _isShrink boolean has changed since the last time it was called. If _isShrink has changed, we update the lastStatus variable to reflect this change.

 void _scrollListener() {
    if (_isShrink != lastStatus) {
      setState(() {
        lastStatus = _isShrink;
      });
    }
  }

The _isShrink boolean is a getter function that determines whether the user has scrolled past a certain threshold on the screen. In this case, the threshold is defined as the difference between the height of the app bar and the kToolbarHeight constant, which represents the standard height of a toolbar.

  bool get _isShrink {
    return _scrollController != null &&
        _scrollController!.hasClients &&
        _scrollController!.offset > (height - kToolbarHeight);
  }

an initState function that is called when the widget is first created. This function initializes the _scrollController and adds a listener to it that will call the _scrollListener function whenever the user scrolls on the screen.


We also define a dispose function that is called when the widget is destroyed. This function removes the _scrollListener from the _scrollController and disposes of the _scrollController.

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    _scrollController?.removeListener(_scrollListener);
    _scrollController?.dispose();
    super.dispose();
  }

.now using the _isShrink boolean in our code to hide the title and lead back button

             
                //back button will hide when we will scroll up
                leading: _isShrink     // <-- SEE HERE
                    ? const BackButton(
                        color: Colors.black, 
                      )
                    : null,
                //title will hide when we will scroll up
                title: _isShrink       // <-- SEE HERE
                    ? const Text(
                        "₹62 total rewards",
                        style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.normal,
                            fontSize: 18),
                      )
                    : null,                                                                                   

now after adding the image our app bar is done

   SliverAppBar(
                elevation: 0,
                backgroundColor: _isShrink ? Colors.white : Colors.transparent,
                pinned: true,
                expandedHeight: height,
                //back button will hide when we will scroll up
                leading: _isShrink // <-- SEE HERE
                    ? const BackButton(
                        color: Colors.black,
                      )
                    : null,
                //title will hide when we will scroll up
                title: _isShrink // <-- SEE HERE
                    ? const Text(
                        "₹62 total rewards",
                        style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.normal,
                            fontSize: 18),
                      )
                    : null,
                actions: [
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(
                      Icons.more_vert,
                      color: Colors.black,
                    ),
                  ),
                ],
                flexibleSpace: FlexibleSpaceBar(
                  collapseMode: CollapseMode.parallax,
                  background: SafeArea(
                    child: Stack(
                      children: [
                        Container(
                          width: double.infinity,
                          child: SingleChildScrollView(scrollDirection: Axis.horizontal,child: Image.asset("assets/background_image.jpg")),
                          //in future we will add image
                        ),
                        Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            alignment: Alignment.topCenter,
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: const [
                                Text(
                                  "₹62",
                                  style: TextStyle(
                                      color: Colors.black,
                                      fontWeight: FontWeight.normal,
                                      fontSize: 24),
                                ),
                                Text(
                                  "Total rewards",
                                  style: TextStyle(color: Colors.black),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
Flutter Rewards App Bar
Flutter Rewards App Bar

Coming towards our body part

In the Google Pay rewards screen, there’s a long-width rectangular button that says “Upcoming rewards on your path” When you tap on it, it takes you to a new screen that shows rewards that you can earn in the future.

Back on the main rewards screen, there’s another section with the heading “My rewards.” This section displays all the rewards that you’ve earned so far in a grid view. The grid view has two rewards displayed side by side. As you scroll down, more rewards are loaded and displayed using an infinite scrolling feature, which means that you can keep scrolling and more rewards will keep appearing.

in the body part

 body: CustomScrollView(slivers: [buildRewardsbody()]),

This is creating a CustomScrollView widget with one child sliver called buildRewardsbody(). A CustomScrollView is a widget that allows you to create a scrollable view with a flexible header and footer, along with any number of sliver elements in between. Sliver elements are widgets that can be scrolled and laid out in a scrolling list.

In this case, the buildRewardsbody() function returns a widget that creates a sliver element for displaying the content of the reward. The slivers property of CustomScrollView takes an array of sliver widgets, and we’re passing an array with just one element (the sliver widget returned by buildRewardsbody()).

By using a CustomScrollView with a sliver element, we can create a scrolling view with a flexible header that can expand or contract as the user scrolls. This allows for a more dynamic and interactive user experience, especially when combined with other scrolling widgets like ListView or GridView.

Widget Function buildRewardsbody

 Widget buildRewardsbody() => SliverToBoxAdapter(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: UpcomingRewardsButton(
                onTap: () {
                  // Add your navigation code here
                },
              ),
            ),
            const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text("My Rewards",style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.normal,
                  fontSize: 20),),
            ),
            GridView.builder(
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  crossAxisSpacing: 0.0,
                  mainAxisSpacing: 0.0,
                ),
              primary: false,
              shrinkWrap: true,
              itemCount: 20,

              itemBuilder: (context, index) => Padding(
                padding: const EdgeInsets.all(8.0),
                child: buildRewardsContainer(),
              )
            ),
          ],
        ),
      );

function buildRewardsbody() that returns a sliver element for displaying the content of the reward. The sliver element contains a column with three child widgets:

  1. The UpcomingRewardsButton widget: This is a custom button widget that shows the text “Upcoming rewards on your path” and a “>” icon on the right. When the user taps on this button, the onTap function is called, and you can add your navigation code to this function.
  2. The Text widget: This widget shows the heading “My Rewards” in black color with a normal font weight and a font size of 20.
  3. The GridView.builder widget: This widget creates a grid view with two columns and an infinite number of rows. It uses the SliverGridDelegateWithFixedCrossAxisCount delegate to specify the number of columns and the spacing between them. The itemCount parameter specifies the total number of items in the grid view.

For each item in the grid view, the itemBuilder callback function is called, which creates a padding widget with the buildRewardsContainer() function as its child. The buildRewardsContainer() function returns a container widget with a reward image, a reward title, and a reward description.

Stateless Class UpcomingRewardsButton

class UpcomingRewardsButton extends StatelessWidget {
  final VoidCallback onTap;

  const UpcomingRewardsButton({Key? key, required this.onTap}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: 60,
        decoration: BoxDecoration(
          color: Colors.blue.withOpacity(0.06),
          borderRadius: BorderRadius.circular(20),
        ),
        child: Row(
          children: const [
            Padding(
              padding: EdgeInsets.symmetric(horizontal: 16),
              child: Text(
                'Upcoming rewards on your path',
                style: TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.normal,
                ),
              ),
            ),
            Spacer(),
            Icon(Icons.arrow_forward),
            SizedBox(width: 16),
          ],
        ),
      ),
    );
  }
}

Widget Function buildRewardsContainer

Rewards Container
Rewards Container
Widget buildRewardsContainer() => Container(
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(20),
    border: Border.all(color: Colors.grey)
  ),
  child: SizedBox(
    height: 100,
    width: 100,
    child: Stack(
      children:[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: const [
                      Text("Flat ₹5000",style: TextStyle(
                          fontWeight: FontWeight.normal,
                          fontSize: 18
                      ),),
                      Expanded(child: Text('''Bonus cash on1st deposit on My11Circle''',maxLines: 2,)),
                    ]
                ),
              ),
              const CircleAvatar(
                radius: 100*0.11,
                backgroundColor: Colors.teal,
                child: CircleAvatar(
                  // backgroundImage: AssetImage('assets/appdev.png'),
                  backgroundColor: Colors.white,
                  radius: 100*0.1,
                ),
              ),
            ],
          ),
        ),
      ],
    ),
  ),
);

Output

Full Source Code

import 'package:flutter/material.dart';

class GPayRewardScreen extends StatefulWidget {
  @override
  State<GPayRewardScreen> createState() => _GPayRewardScreenState();
}

class _GPayRewardScreenState extends State<GPayRewardScreen> {
  ScrollController? _scrollController;
  bool lastStatus = true;
  double height = 200;

  void _scrollListener() {
    if (_isShrink != lastStatus) {
      setState(() {
        lastStatus = _isShrink;
      });
    }
  }

  bool get _isShrink {
    return _scrollController != null &&
        _scrollController!.hasClients &&
        _scrollController!.offset > (height - kToolbarHeight);
  }

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    _scrollController?.removeListener(_scrollListener);
    _scrollController?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        body: NestedScrollView(
          controller: _scrollController,
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverAppBar(
                elevation: 0,
                backgroundColor: _isShrink ? Colors.white : Colors.transparent,
                pinned: true,
                expandedHeight: height,
                leading:const BackButton(
                        color: Colors.black,
                      ),
                //title will hide when we will scroll up
                title: _isShrink // <-- SEE HERE
                    ? const Text(
                        "₹62 total rewards",
                        style: TextStyle(
                            color: Colors.black,
                            fontWeight: FontWeight.normal,
                            fontSize: 18),
                      )
                    : null,
                actions: [
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(
                      Icons.more_vert,
                      color: Colors.black,
                    ),
                  ),
                ],
                flexibleSpace: FlexibleSpaceBar(
                  collapseMode: CollapseMode.parallax,
                  background: SafeArea(
                    child: Stack(
                      children: [
                        Container(
                          width: double.infinity,
                          child: SingleChildScrollView(scrollDirection: Axis.horizontal,child: Image.asset("assets/background_image.jpg")),
                          //in future we will add image
                        ),
                        Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            alignment: Alignment.topCenter,
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.start,
                              children: const [
                                Text(
                                  "₹62",
                                  style: TextStyle(
                                      color: Colors.black,
                                      fontWeight: FontWeight.normal,
                                      fontSize: 24),
                                ),
                                Text(
                                  "Total rewards",
                                  style: TextStyle(color: Colors.black),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ];
          },
          body: CustomScrollView(slivers: [buildRewardsbody()]),
        ),
      );
}


Widget buildRewardsbody() => SliverToBoxAdapter(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: UpcomingRewardsButton(
          onTap: () {
            // Add your navigation code here
          },
        ),
      ),
      const Padding(
        padding: EdgeInsets.all(8.0),
        child: Text("My Rewards",style: TextStyle(
            color: Colors.black,
            fontWeight: FontWeight.normal,
            fontSize: 20),),
      ),
      GridView.builder(
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 0.0,
            mainAxisSpacing: 0.0,
          ),
          primary: false,
          shrinkWrap: true,
          itemCount: 20,

          itemBuilder: (context, index) => Padding(
            padding: const EdgeInsets.all(8.0),
            child: buildRewardsContainer(),
          )
      ),
    ],
  ),
);

class UpcomingRewardsButton extends StatelessWidget {
  final VoidCallback onTap;

  const UpcomingRewardsButton({Key? key, required this.onTap}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: 60,
        decoration: BoxDecoration(
          color: Colors.blue.withOpacity(0.06),
          borderRadius: BorderRadius.circular(20),
        ),
        child: Row(
          children: const [
            Padding(
              padding: EdgeInsets.symmetric(horizontal: 16),
              child: Text(
                'Upcoming rewards on your path',
                style: TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.normal,
                ),
              ),
            ),
            Spacer(),
            Icon(Icons.arrow_forward),
            SizedBox(width: 16),
          ],
        ),
      ),
    );
  }
}

Widget buildRewardsContainer() => Container(
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(20),
    border: Border.all(color: Colors.grey)
  ),
  child: SizedBox(
    height: 100,
    width: 100,
    child: Stack(
      children:[
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: const [
                      Text("Flat ₹5000",style: TextStyle(
                          fontWeight: FontWeight.normal,
                          fontSize: 18
                      ),),
                      Expanded(child: Text('''Bonus cash on1st deposit on My11Circle''',maxLines: 2,)),
                    ]
                ),
              ),
              const CircleAvatar(
                radius: 100*0.11,
                backgroundColor: Colors.teal,
                child: CircleAvatar(
                  // backgroundImage: AssetImage('assets/appdev.png'),
                  backgroundColor: Colors.white,
                  radius: 100*0.1,
                ),
              ),
            ],
          ),
        ),
      ],
    ),
  ),
);

Conclusion

we have learned how to create a user interface similar to the Google Pay Rewards screen using Flutter. We started by creating a custom SliverAppBar with a flexible space bar and parallax effect. Then, we added a button widget with text and an icon using a Container and InkWell widget. After that, we created a GridView.builder widget with a fixed cross-axis count and infinite scrolling functionality. Finally, we wrapped all these widgets inside a CustomScrollView and a SliverToBoxAdapter to create a responsive layout.

With these widgets and techniques, we can build a UI with a similar look and feel as the Google Pay Rewards screen. This UI is not only aesthetically pleasing but also highly functional and can be used in a variety of applications. By understanding these concepts, developers can create a wide range of visually appealing interfaces in their Flutter applications.

How to Implement the 60-30-10 Rule in Flutter UI Design: Complete Guide

How to Implement the 60-30-10 Rule in Flutter UI Design: Complete Guide
Previous Post

Discover the Top 10 Essential npm Scripts for React Development

Next Post

How to Make Reward Detail Screen UI in Flutter Like Google Pay Complete Guide

Hemant Singh

Hemant Singh

Software Developer | Content Writer | Singer | Artist

Next Post
rewards detail screen ui in flutter

How to Make Reward Detail Screen UI in Flutter Like Google Pay Complete Guide

Leave a Reply Cancel reply

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

You might also like

Debugging Flutter widgets

Debugging Flutter Widgets Like a Pro Complete Guide

May 14, 2023
rewards detail screen ui in flutter

How to Make Reward Detail Screen UI in Flutter Like Google Pay Complete Guide

May 14, 2023
Reward Screen UI in Flutter

How to Make Reward Screen UI in Flutter Like Google Pay Complete Guide

May 16, 2023
Discover the Top 10 Essential npm Scripts for React Development

Discover the Top 10 Essential npm Scripts for React Development

May 13, 2023
how to connect domain or subdomain with linux server using Cloudflare

how to connect domain or subdomain with linux server using Cloudflare

May 13, 2023
The 60-30-10 Rule in Flutter UI design

How to Implement the 60-30-10 Rule in Flutter UI Design Complete Guide

May 15, 2023
  • Privacy Policy
  • Terms of Use
  • About Us
  • Advertisement
  • Disclaimer
  • Contact Us

© 2023 Site Jugglers - Premium tutorial website for coding geeks.

No Result
View All Result
  • React
  • Flutter

© 2023 Site Jugglers - Premium tutorial website for coding geeks.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In