Wednesday, July 6, 2022
HomeWeb DevelopmentConstructing a procuring cart in Flutter

Constructing a procuring cart in Flutter


The world and companies are each on-line. Shoppers want comfort, and it’s as much as suppliers to provide companies on the click on of a button, which may be performed by way of a web site or a cell app.

The advantage of having an ecommerce web site or a cell app is that companies might current extra choices for guests to see, which isn’t all the time achievable in a bodily retailer as a result of house constraints. Conserving in thoughts the place the world is and the place it’s heading, companies would require an growing variety of builders to create purposes for his or her shops.

That’s the reason in the present day we’re making a easy procuring cart utility with two screens; there’s nothing fancy in regards to the UI as a result of our main focus right here is the operation and performance of a procuring cart.

We shall be utilizing SQFlite and SharedPreferences in our utility to retailer the information domestically on the gadget itself. SQFlite and SharedPreferences retailer knowledge, whereas Supplier manages the applying’s state.

As I beforehand said, we’ve two screens. The primary is a product display, which shows a listing of fruits together with pictures, the title of the fruit, and the value. Every record merchandise features a button that means that you can add it to your procuring basket.

The AppBar features a procuring cart icon with a badge that updates the merchandise depend each time a person presses the Add to Automotivet button.

The second display, the procuring cart display, shows a listing of the issues that the person added to it. If the person decides to take away it from the cart, a delete button removes the merchandise from the cart display.

The complete value is proven on the backside of the display. A button that, in the interim, shows a SnackBar confirming that the cost has been processed.

So, after somewhat introduction to the applying we’re creating, allow us to get began on programming it.

Product List

Empty Cart

Add dependencies

First allow us to step up our pubspec.yaml file by coming into all the mandatory dependencies that we’re going to use to construct our app:

shared_preferences: ^2.0.15
path_provider: ^2.0.10
sqflite: ^2.0.2+1
badges: ^2.0.2
supplier: ^6.0.3

Additionally, you will want so as to add photographs so just be sure you have uncommented the belongings and added the photographs folder below the belongings.

Arrange

Subsequent we’re going to begin off with creating our Mannequin courses named Cart and Merchandise. So, create a brand new Dart file and title it cart_model, or you can too title it per your necessities. Enter the code given under for the Mannequin class:

class Cart {
 late ultimate int? id;
 ultimate String? productId;
 ultimate String? productName;
 ultimate int? initialPrice;
 ultimate int? productPrice;
 ultimate ValueNotifier<int>? amount;
 ultimate String? unitTag;
 ultimate String? picture;

 Cart(
     {required this.id,
     required this.productId,
     required this.productName,
     required this.initialPrice,
     required this.productPrice,
     required this.amount,
     required this.unitTag,
     required this.picture});

 Cart.fromMap(Map<dynamic, dynamic> knowledge)
     : id = knowledge['id'],
       productId = knowledge['productId'],
       productName = knowledge['productName'],
       initialPrice = knowledge['initialPrice'],
       productPrice = knowledge['productPrice'],
       amount = ValueNotifier(knowledge['quantity']),
       unitTag = knowledge['unitTag'],
       picture = knowledge['image'];

 Map<String, dynamic> toMap() {
   return {
     'id': id,
     'productId': productId,
     'productName': productName,
     'initialPrice': initialPrice,
     'productPrice': productPrice,
     'amount': amount?.worth,
     'unitTag': unitTag,
     'picture': picture,
   };
 }
}

Create one other Dart file and enter item_model and the code given under:

class Merchandise {
 ultimate String title;
 ultimate String unit;
 ultimate int worth;
 ultimate String picture;

 Merchandise({required this.title, required this.unit, required this.worth, required this.picture});

 Map toJson() {
   return {
     'title': title,
     'unit': unit,
     'worth': worth,
     'picture': picture,
   };
 }
}

Add SQFlite

As I beforehand said, we shall be using SQFlite, which is basically SQLite for Flutter, and we are going to save the information domestically inside the cellphone reminiscence. We aren’t importing or retrieving knowledge from the cloud as a result of the target of this put up is to be taught the elemental operation of a cart display.

So, utilizing the SQFlite bundle, we’re developing a database class referred to as DBHelper:

import 'bundle:sqflite/sqflite.dart';
import 'bundle:path_provider/path_provider.dart';
import 'bundle:path/path.dart';
import 'dart:io' as io;
import 'bundle:shopping_cart_app/mannequin/cart_model.dart';

class DBHelper {
 static Database? _database;

 Future<Database?> get database async {
   if (_database != null) {
     return _database!;
   }
   _database = await initDatabase();
   return null;
 }

 initDatabase() async {
   io.Listing listing = await getApplicationDocumentsDirectory();
   String path = be a part of(listing.path, 'cart.db');
   var db = await openDatabase(path, model: 1, onCreate: _onCreate);
   return db;
 }
// creating database desk
 _onCreate(Database db, int model) async {
   await db.execute(
       'CREATE TABLE cart(id INTEGER PRIMARY KEY, productId VARCHAR UNIQUE, productName TEXT, initialPrice INTEGER, productPrice INTEGER, amount INTEGER, unitTag TEXT, picture TEXT)');
 }
// inserting knowledge into the desk
 Future<Cart> insert(Cart cart) async {
   var dbClient = await database;
   await dbClient!.insert('cart', cart.toMap());
   return cart;
 }
// getting all of the objects within the record from the database
 Future<Listing<Cart>> getCartList() async {
   var dbClient = await database;
   ultimate Listing<Map<String, Object?>> queryResult =
       await dbClient!.question('cart');
   return queryResult.map((end result) => Cart.fromMap(end result)).toList();
 }
Future<int> updateQuantity(Cart cart) async {
 var dbClient = await database;
 return await dbClient!.replace('cart', cart.quantityMap(),
     the place: "productId = ?", whereArgs: [cart.productId]);
}

// deleting an merchandise from the cart display
 Future<int> deleteCartItem(int id) async {
   var dbClient = await database;
   return await dbClient!.delete('cart', the place: 'id = ?', whereArgs: [id]);
 }
}

Add the Supplier class

The following step shall be to develop our Supplier class, which is able to embody all of our strategies and can separate our UI from the logic that can finally handle our whole utility.

We use SharedPreferences along with SqFlite. The rationale for utilizing SharedPreferences is as a result of it wraps platform-specific persistence to retailer easy knowledge such because the merchandise depend and whole worth, in order that even when the person exits the applying and returns to it, that info will nonetheless be obtainable.

Create a brand new class referred to as CartProvider and paste the code under into it:

class CartProvider with ChangeNotifier {
 DBHelper dbHelper = DBHelper();
 int _counter = 0;
 int _quantity = 1;
 int get counter => _counter;
 int get amount => _quantity;

 double _totalPrice = 0.0;
 double get totalPrice => _totalPrice;

 Listing<Cart> cart = [];

 Future<Listing<Cart>> getData() async {
   cart = await dbHelper.getCartList();
   notifyListeners();
   return cart;
 }

 void _setPrefsItems() async {
   SharedPreferences prefs = await SharedPreferences.getInstance();
   prefs.setInt('cart_items', _counter);
   prefs.setInt('item_quantity', _quantity);
   prefs.setDouble('total_price', _totalPrice);
   notifyListeners();
 }

 void _getPrefsItems() async {
   SharedPreferences prefs = await SharedPreferences.getInstance();
   _counter = prefs.getInt('cart_items') ?? 0;
   _quantity = prefs.getInt('item_quantity') ?? 1;
   _totalPrice = prefs.getDouble('total_price') ?? 0;
 }

 void addCounter() {
   _counter++;
   _setPrefsItems();
   notifyListeners();
 }

 void removeCounter() {
   _counter--;
   _setPrefsItems();
   notifyListeners();
 }

 int getCounter() {
   _getPrefsItems();
   return _counter;
 }

 void addQuantity(int id) {
   ultimate index = cart.indexWhere((ingredient) => ingredient.id == id);
   cart[index].amount!.worth = cart[index].amount!.worth + 1;
   _setPrefsItems();
   notifyListeners();
 }

 void deleteQuantity(int id) {
   ultimate index = cart.indexWhere((ingredient) => ingredient.id == id);
   ultimate currentQuantity = cart[index].amount!.worth;
   if (currentQuantity <= 1) {
     currentQuantity == 1;
   } else {
     cart[index].amount!.worth = currentQuantity - 1;
   }
   _setPrefsItems();
   notifyListeners();
 }

 void removeItem(int id) {
   ultimate index = cart.indexWhere((ingredient) => ingredient.id == id);
   cart.removeAt(index);
   _setPrefsItems();
   notifyListeners();
 }

 int getQuantity(int amount) {
   _getPrefsItems();
   return _quantity;
 }

 void addTotalPrice(double productPrice) {
   _totalPrice = _totalPrice + productPrice;
   _setPrefsItems();
   notifyListeners();
 }

 void removeTotalPrice(double productPrice) {
   _totalPrice = _totalPrice - productPrice;
   _setPrefsItems();
   notifyListeners();
 }

 double getTotalPrice() {
   _getPrefsItems();
   return _totalPrice;
 }
}

Now allow us to begin constructing our UI for our product record display. First we’re going to add knowledge to our Merchandise mannequin that we created. We’re including the information by making a Listing of merchandise in our Merchandise mannequin class:

Listing<Merchandise> merchandise = [
 Item(
     name: 'Apple', unit: 'Kg', price: 20, image: 'assets/images/apple.png'),
 Item(
     name: 'Mango',
     unit: 'Doz',
     price: 30,
     image: 'assets/images/mango.png'),
 Item(
     name: 'Banana',
     unit: 'Doz',
     price: 10,
     image: 'assets/images/banana.png'),
 Item(
     name: 'Grapes',
     unit: 'Kg',
     price: 8,
     image: 'assets/images/grapes.png'),
 Item(
     name: 'Water Melon',
     unit: 'Kg',
     price: 25,
     image: 'assets/images/watermelon.png'),
 Item(name: 'Kiwi', unit: 'Pc', price: 40, image: 'assets/images/kiwi.png'),
 Item(
     name: 'Orange',
     unit: 'Doz',
     price: 15,
     image: 'assets/images/orange.png'),
 Item(name: 'Peach', unit: 'Pc', price: 8, image: 'assets/images/peach.png'),
 Item(
     name: 'Strawberry',
     unit: 'Box',
     price: 12,
     image: 'assets/images/strawberry.png'),
 Item(
     name: 'Fruit Basket',
     unit: 'Kg',
     price: 55,
     image: 'assets/images/fruitBasket.png'),
];

So, ranging from the highest that’s the AppBar, we’ve added an IconButton wrapped with our Badge bundle that we added to our utility. The Icon is of a procuring cart and the badge over it reveals what number of objects have been added to our cart.

Please take a look on the picture and code under. Now we have wrapped the Textual content widget with a Shopper widget as a result of each time a person clicks on the Add to Cart button, the entire UI doesn’t have to get rebuilt when the Textual content widget has to replace the merchandise depend. And the Shopper widget does precisely that for us:

Product List Header

AppBar(
 centerTitle: true,
 title: const Textual content('Product Listing'),
 actions: [
   Badge(
     badgeContent: Consumer<CartProvider>(
       builder: (context, value, child) {
         return Text(
           value.getCounter().toString(),
           style: const TextStyle(
               color: Colors.white, fontWeight: FontWeight.bold),
         );
       },
     ),
     position: const BadgePosition(start: 30, bottom: 30),
     child: IconButton(
       onPressed: () {
         Navigator.push(
             context,
             MaterialPageRoute(
                 builder: (context) => const CartScreen()));
       },
       icon: const Icon(Icons.shopping_cart),
     ),
   ),
   const SizedBox(
     width: 20.0,
   ),
 ],
),

The Scaffold‘s physique is a ListView builder that returns a Card widget with the data from the lists we created, the title of the fruit, unit, and worth per unit, and a button so as to add that merchandise to the cart. Please see the picture and code offered under:

Apples and Mango

ListView.builder(
   padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 8.0),
   shrinkWrap: true,
   itemCount: merchandise.size,
   itemBuilder: (context, index) {
     return Card(
       colour: Colours.blueGrey.shade200,
       elevation: 5.0,
       baby: Padding(
         padding: const EdgeInsets.all(4.0),
         baby: Row(
           mainAxisAlignment: MainAxisAlignment.spaceEvenly,
           mainAxisSize: MainAxisSize.max,
           kids: [
             Image(
               height: 80,
               width: 80,
               image: AssetImage(products[index].picture.toString()),
             ),
             SizedBox(
               width: 130,
               baby: Column(
                 crossAxisAlignment: CrossAxisAlignment.begin,
                 kids: [
                   const SizedBox(
                     height: 5.0,
                   ),
                   RichText(
                     overflow: TextOverflow.ellipsis,
                     maxLines: 1,
                     text: TextSpan(
                         text: 'Name: ',
                         style: TextStyle(
                             color: Colors.blueGrey.shade800,
                             fontSize: 16.0),
                         children: [
                           TextSpan(
                               text:
                                   '${products[index].title.toString()}n',
                               fashion: const TextStyle(
                                   fontWeight: FontWeight.daring)),
                         ]),
                   ),
                   RichText(
                     maxLines: 1,
                     textual content: TextSpan(
                         textual content: 'Unit: ',
                         fashion: TextStyle(
                             colour: Colours.blueGrey.shade800,
                             fontSize: 16.0),
                         kids: [
                           TextSpan(
                               text:
                                   '${products[index].unit.toString()}n',
                               fashion: const TextStyle(
                                   fontWeight: FontWeight.daring)),
                         ]),
                   ),
                   RichText(
                     maxLines: 1,
                     textual content: TextSpan(
                         textual content: 'Value: ' r"$",
                         fashion: TextStyle(
                             colour: Colours.blueGrey.shade800,
                             fontSize: 16.0),
                         kids: [
                           TextSpan(
                               text:
                                   '${products[index].worth.toString()}n',
                               fashion: const TextStyle(
                                   fontWeight: FontWeight.daring)),
                         ]),
                   ),
                 ],
               ),
             ),
             ElevatedButton(
                 fashion: ElevatedButton.styleFrom(
                     main: Colours.blueGrey.shade900),
                 onPressed: () {
                   saveData(index);
                 },
                 baby: const Textual content('Add to Cart')),
           ],
         ),
       ),
     );
   }),

Now we have initialized our CartProvider class and created a perform that can save knowledge to the database when the Add to Cart button is clicked. It additionally updates the Textual content widget badge within the AppBar and add whole worth to the Database that can finally present up within the Cart display:

ultimate cart = Supplier.of<CartProvider>(context);
void saveData(int index) {
 dbHelper
     .insert(
   Cart(
     id: index,
     productId: index.toString(),
     productName: merchandise[index].title,
     initialPrice: merchandise[index].worth,
     productPrice: merchandise[index].worth,
     amount: ValueNotifier(1),
     unitTag: merchandise[index].unit,
     picture: merchandise[index].picture,
   ),
 )
     .then((worth) {
   cart.addTotalPrice(merchandise[index].worth.toDouble());
   cart.addCounter();
   print('Product Added to cart');
 }).onError((error, stackTrace) {
   print(error.toString());
 });
}

Make a cart display

Transferring on to the cart display, the format is just like the product record display. When the person clicks the Add to Cart button, the whole info is carried onto the cart display.

The implementation is just like what we’ve seen with different ecommerce purposes. The first distinction between the 2 layouts is that the cart display contains an increment and decrement button for growing and lowering the amount of the merchandise.

When customers click on the plus signal, the amount will increase, and after they click on the minus signal, the amount decreases. The entire worth of the cart is added or subtracted when the plus and minus buttons are pressed. The delete button deletes the merchandise from the cart record and in addition subtracts the value from the full worth. Once more we’ve wrapped our ListView builder with the Shopper widget as a result of solely elements of the UI must be rebuilt and up to date, not the entire web page.

Bananas in Cart

Bananas and Oranges in Cart

class CartScreen extends StatefulWidget { const CartScreen({
   Key? key,
 }) : tremendous(key: key);

 @override
 State<CartScreen> createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
 DBHelper? dbHelper = DBHelper();

 @override
 void initState() {
   tremendous.initState();
   context.learn<CartProvider>().getData();
 }

 @override
 Widget construct(BuildContext context) {
   ultimate cart = Supplier.of<CartProvider>(context);
   return Scaffold(
     appBar: AppBar(
       centerTitle: true,
       title: const Textual content('My Procuring Cart'),
       actions: [
         Badge(
           badgeContent: Consumer<CartProvider>(
             builder: (context, value, child) {
               return Text(
                 value.getCounter().toString(),
                 style: const TextStyle(
                     color: Colors.white, fontWeight: FontWeight.bold),
               );
             },
           ),
           position: const BadgePosition(start: 30, bottom: 30),
           child: IconButton(
             onPressed: () {},
             icon: const Icon(Icons.shopping_cart),
           ),
         ),
         const SizedBox(
           width: 20.0,
         ),
       ],
     ),
     physique: Column(
       kids: [
         Expanded(
           child: Consumer<CartProvider>(
             builder: (BuildContext context, provider, widget) {
               if (provider.cart.isEmpty) {
                 return const Center(
                     child: Text(
                   'Your Cart is Empty',
                   style:
                       TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0),
                 ));
               } else {
                 return ListView.builder(
                     shrinkWrap: true,
                     itemCount: provider.cart.length,
                     itemBuilder: (context, index) {
                       return Card(
                         color: Colors.blueGrey.shade200,
                         elevation: 5.0,
                         child: Padding(
                           padding: const EdgeInsets.all(4.0),
                           child: Row(
                             mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                             mainAxisSize: MainAxisSize.max,
                             children: [
                               Image(
                                 height: 80,
                                 width: 80,
                                 image:
                                     AssetImage(provider.cart[index].picture!),
                               ),
                               SizedBox(
                                 width: 130,
                                 baby: Column(
                                   crossAxisAlignment:
                                       CrossAxisAlignment.begin,
                                   kids: [
                                     const SizedBox(
                                       height: 5.0,
                                     ),
                                     RichText(
                                       overflow: TextOverflow.ellipsis,
                                       maxLines: 1,
                                       text: TextSpan(
                                           text: 'Name: ',
                                           style: TextStyle(
                                               color: Colors.blueGrey.shade800,
                                               fontSize: 16.0),
                                           children: [
                                             TextSpan(
                                              text:                                            '${provider.cart[index].productName!}n',
                                                 fashion: const TextStyle(
                                                     fontWeight:
                                                         FontWeight.daring)),
                                           ]),
                                     ),
                                     RichText(
                                       maxLines: 1,
                                       textual content: TextSpan(
                                           textual content: 'Unit: ',
                                           fashion: TextStyle(
                                               colour: Colours.blueGrey.shade800,
                                               fontSize: 16.0),
                                           kids: [
                                             TextSpan(
                                                 text:
                                                     '${provider.cart[index].unitTag!}n',
                                                 fashion: const TextStyle(
                                                     fontWeight:
                                                         FontWeight.daring)),
                                           ]),
                                     ),
                                     RichText(
                                       maxLines: 1,
                                       textual content: TextSpan(
                                           textual content: 'Value: ' r"$",
                                           fashion: TextStyle(
                                               colour: Colours.blueGrey.shade800,
                                               fontSize: 16.0),
                                           kids: [
                                             TextSpan(
                                                 text:
                                                     '${provider.cart[index].productPrice!}n',
                                                 fashion: const TextStyle(
                                                     fontWeight:
                                                         FontWeight.daring)),
                                           ]),
                                     ),
                                   ],
                                 ),
                               ),
                               ValueListenableBuilder<int>(
                                   valueListenable:
                                       supplier.cart[index].amount!,
                                   builder: (context, val, baby) {
                                     return PlusMinusButtons(
                                       addQuantity: () {
                                         cart.addQuantity(
                                             supplier.cart[index].id!);
                                         dbHelper!
                                             .updateQuantity(Cart(
                                                 id: index,
                                                 productId: index.toString(),
                                                 productName: supplier
                                                     .cart[index].productName,
                                                 initialPrice: supplier
                                                     .cart[index].initialPrice,
                                                 productPrice: supplier
                                                     .cart[index].productPrice,
                                                 amount: ValueNotifier(
                                                     supplier.cart[index]
                                                         .amount!.worth),
                                                 unitTag: supplier
                                                     .cart[index].unitTag,
                                                 picture: supplier
                                                     .cart[index].picture))
                                             .then((worth) {
                                           setState(() {
                                             cart.addTotalPrice(double.parse(
                                                 supplier
                                                     .cart[index].productPrice
                                                     .toString()));
                                           });
                                         });
                                       },
                                       deleteQuantity: () {
                                         cart.deleteQuantity(
                                             supplier.cart[index].id!);
                                         cart.removeTotalPrice(double.parse(
                                             supplier.cart[index].productPrice
                                                 .toString()));
                                       },
                                       textual content: val.toString(),
                                     );
                                   }),
                               IconButton(
                                   onPressed: () {
                                     dbHelper!.deleteCartItem(
                                         supplier.cart[index].id!);
                                     supplier
                                         .removeItem(supplier.cart[index].id!);
                                     supplier.removeCounter();
                                   },
                                   icon: Icon(
                                     Icons.delete,
                                     colour: Colours.crimson.shade800,
                                   )),
                             ],
                           ),
                         ),
                       );
                     });
               }
             },
           ),
         ),
         Shopper<CartProvider>(
           builder: (BuildContext context, worth, Widget? baby) {
             ultimate ValueNotifier<int?> totalPrice = ValueNotifier(null);
             for (var ingredient in worth.cart) {
               totalPrice.worth =
                   (ingredient.productPrice! * ingredient.amount!.worth) +
                       (totalPrice.worth ?? 0);
             }
             return Column(
               kids: [
                 ValueListenableBuilder<int?>(
                     valueListenable: totalPrice,
                     builder: (context, val, child) {
                       return ReusableWidget(
                           title: 'Sub-Total',
                           value: r'$' + (val?.toStringAsFixed(2) ?? '0'));
                     }),
               ],
             );
           },
         )
       ],
     ),
     bottomNavigationBar: InkWell(
       onTap: () {
         ScaffoldMessenger.of(context).showSnackBar(
           const SnackBar(
             content material: Textual content('Cost Profitable'),
             length: Period(seconds: 2),
           ),
         );
       },
       baby: Container(
         colour: Colours.yellow.shade600,
         alignment: Alignment.middle,
         peak: 50.0,
         baby: const Textual content(
           'Proceed to Pay',
           fashion: TextStyle(
             fontSize: 18.0,
             fontWeight: FontWeight.daring,
           ),
         ),
       ),
     ),
   );
 }
}

class PlusMinusButtons extends StatelessWidget {
 ultimate VoidCallback deleteQuantity;
 ultimate VoidCallback addQuantity;
 ultimate String textual content;
 const PlusMinusButtons(
     {Key? key,
     required this.addQuantity,
     required this.deleteQuantity,
     required this.textual content})
     : tremendous(key: key);

 @override
 Widget construct(BuildContext context) {
   return Row(
     kids: [
       IconButton(onPressed: deleteQuantity, icon: const Icon(Icons.remove)),
       Text(text),
       IconButton(onPressed: addQuantity, icon: const Icon(Icons.add)),
     ],
   );
 }
}

class ReusableWidget extends StatelessWidget {
 ultimate String title, worth;
 const ReusableWidget({Key? key, required this.title, required this.worth});

 @override
 Widget construct(BuildContext context) {
   return Padding(
     padding: const EdgeInsets.all(8.0),
     baby: Row(
       mainAxisAlignment: MainAxisAlignment.spaceBetween,
       kids: [
         Text(
           title,
           style: Theme.of(context).textTheme.subtitle1,
         ),
         Text(
           value.toString(),
           style: Theme.of(context).textTheme.subtitle2,
         ),
       ],
     ),
   );
 }
}

Look in the direction of the tip of the code, simply earlier than the underside navigation bar, for a Shopper widget that returns ValueNotifierBuilder from inside the Column widget. It’s accountable for updating the amount for the particular merchandise when the person clicks both the plus or minus button on the cart display. There’s a backside navigation bar with a button on the backside of the display.

The cost choice has not been established as a result of it’s past the scope of this text, however you possibly can have a look at one other article for in-app buy choices in Flutter of the Flutter Stripe SDK.

To finish the UI of the cart display, I included that button on the backside that, when pressed, brings up a SnackBar confirming that cost has been accomplished by the person. After that, we’ve two custom-made widgets for the increment and decrement button and for displaying the full worth on the backside of the display.

Right here is the working of the entire utility together with the GitHub hyperlink to the supply code.

Scrolling Through Products

Conclusion

That’s all for this text. Hope you loved studying it and realized one thing new from it too! I want to thank a pal of mine, Rohit Goswami, a Flutter developer, who helped me debug the code on this utility. Cheers to him!

Thanks! Take care and keep protected.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments