0% found this document useful (0 votes)
34 views8 pages

Wallet Page

The document outlines the implementation of a WalletPage in a Flutter application, which allows users to manage in-app purchases and view their wallet balance. It includes functionalities for loading product details, processing purchases, and updating the user's wallet balance in Firestore. The page also features a user interface for displaying the wallet balance and purchase history, along with a dialog for selecting products to purchase.

Uploaded by

sarmadrehan91
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views8 pages

Wallet Page

The document outlines the implementation of a WalletPage in a Flutter application, which allows users to manage in-app purchases and view their wallet balance. It includes functionalities for loading product details, processing purchases, and updating the user's wallet balance in Firestore. The page also features a user interface for displaying the wallet balance and purchase history, along with a dialog for selecting products to purchase.

Uploaded by

sarmadrehan91
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 8

// ignore_for_file: use_build_context_synchronously

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:logger/logger.dart';
import 'package:music_train_lottery/models/wallet_model.dart';
import 'package:music_train_lottery/theme/theme_helper.dart';
import 'package:music_train_lottery/utils/constants.dart';

import '../popups/currency_popup.dart';
import '../theme/custom_button_style.dart';
import '../theme/custom_text_style.dart';
import '../utils/IAP.dart';
import '../utils/custom_widgets_builder.dart';
import '../utils/image_constant.dart';
import '../utils/shared_preferences.dart';

class WalletPage extends StatefulWidget {


const WalletPage({super.key});

@override
State<WalletPage> createState() => _WalletPageState();
}

class _WalletPageState extends State<WalletPage> {


final IAPService _iapService = IAPService();
final Logger _logger = Logger();
late StreamSubscription<List<PurchaseDetails>> _subscription;
// Toggle this to true to use dummy data instead of real IAP fetch
final bool _useDummyData = true;

List<ProductDetails> _products = [];


bool _isLoading = true;

@override
void initState() {
super.initState();
_subscription = InAppPurchase.instance.purchaseStream.listen(
_onPurchaseUpdated,
onDone: () => _subscription.cancel(),
);
_loadProducts();
}

// @override
// void initState() {
// super.initState();
// _subscription = InAppPurchase.instance.purchaseStream.listen((purchases) {
// _iapService.onPurchaseUpdated(purchases);
// }, onDone: () {
// _subscription.cancel();
// });
// _initStoreInfo();
// }

Future<void> _loadProducts() async {


setState(() => _isLoading = true);

if (_useDummyData) {
// Provide dummy list for testing
await Future.delayed(const Duration(seconds: 1)); // simulate network delay
setState(() {
_products = [
ProductDetails(
id: 'dummy_100_coins',
title: '100 Coins Pack',
description: 'Get 100 coins to use in the app',
price: '\$0.99',
rawPrice: 0.99,
currencyCode: 'USD',
),
ProductDetails(
id: 'dummy_500_coins',
title: '500 Coins Pack',
description: 'Best value: 500 coins',
price: '\$3.99',
rawPrice: 3.99,
currencyCode: 'USD',
),
ProductDetails(
id: 'dummy_1000_coins',
title: '1000 Coins Pack',
description: 'Ultimate pack: 1000 coins',
price: '\$6.99',
rawPrice: 6.99,
currencyCode: 'USD',
),
];
_isLoading = false;
});
} else {
try {
await _iapService.init();
final products = await _iapService.fetchProducts(_iapService.productIds);
setState(() {
_products = products;
_isLoading = false;
});
} catch (e) {
_logger.e('Failed to load products: $e');
setState(() => _isLoading = false);
}
}
}
void _onPurchaseUpdated(List<PurchaseDetails> purchases) async {
for (var p in purchases) {
if (p.status == PurchaseStatus.purchased) {
// Verify then deliver
await _verifyAndDeliver(p);
} else if (p.status == PurchaseStatus.error) {
_logger.e('Purchase error: ${p.error}');
}
if (p.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(p);
}
}
}

Future<void> _verifyAndDeliver(PurchaseDetails purchase) async {


// Here you would verify with your server if needed
double amount = purchase.productID.contains('1000')
? 6.99
: purchase.productID.contains('500')
? 3.99
: 0.99;

final walletRef = FirebaseFirestore.instance


.collection('users')
.doc("")
.collection('userData')
.doc('walletData');

final snapshot = await walletRef.get();


final data = snapshot.data() ?? {};
double currentBalance = double.tryParse(data['available_balance'] ?? '0') ?? 0;
List history = List.from(data['historyData'] ?? []);

double updatedBalance = currentBalance + amount;


history.add({
'balance': '+\$${amount.toStringAsFixed(2)}',
'currency': '\$',
'name': 'IAP Top-up',
'timestamp': FieldValue.serverTimestamp(),
});

await walletRef.update({
'available_balance': updatedBalance.toStringAsFixed(2),
'historyData': history,
});

_logger.i('Wallet updated: \$${updatedBalance.toStringAsFixed(2)}');


ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Purchase successful! Wallet topped up.')),
);
}

@override
void dispose() {
_subscription.cancel();
_iapService.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return FutureBuilder(
initialData: null,
future: rootBundle.loadString('assets/Common-Currency.json'),
builder: (context, currency) {
return SafeArea(
child: AnnotatedRegion(
value: const SystemUiOverlayStyle(
statusBarColor: kWhiteColor,
statusBarIconBrightness: Brightness.dark,
),
child: Scaffold(
body: FutureBuilder(
initialData: null,
future: Prefs.getString('userID'),
builder: (context, user) {
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(user.data)
.collection('userData')
.doc('walletData')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(
color: kLightAccentColor,
),
);
}

WalletData walletData =
WalletData.fromFirestore(snapshot.data!);
final balance = walletData.availableBalance.split('.');

return SingleChildScrollView(
child: Container(
width: double.maxFinite,
padding: REdgeInsets.symmetric(
horizontal: 15.w,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () => Navigator.pop(context),
child: Container(
padding: REdgeInsets.symmetric(
horizontal: 12.0,
vertical: 10.0,
),
decoration: BoxDecoration(
border: Border.all(
color: kLightAccentColor,
width: 1.0,
),
borderRadius:
BorderRadius.circular(10.0),
),
child: SvgPicture.asset(
ImageConstant.imgArrowLeftGreenA700,
height: 15.h,
width: 15.w,
),
),
),
Text(
"Wallet",
style: theme.textTheme.bodyLarge!
.copyWith(
color: kLightTextColor,
fontWeight: FontWeight.bold),
),
SvgPicture.asset(
ImageConstant.imgUser,
height: 46.h,
width: 40.w,
),
],
),
kPageItemSpacing2,
InkWell(
onTap: () {
_showPurchaseDialog();
// _showCurrencyPopup(
// context, currency.data!, user.data!);
},
child: Container(
width: double.maxFinite,
height: 200.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.r),
color: kLightPrimaryColor,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
"Main Balance",
style: CustomTextStyles
.bodySmallWhiteA700,
),
kPageItemSpacing,
RichText(
text: TextSpan(children: [
TextSpan(
text: '\$${balance.first}',
style: theme
.textTheme.displaySmall,
),
TextSpan(
text: ".${balance.last}",
style: CustomTextStyles
.bodyLargeSoraffffffff,
)
]),
textAlign: TextAlign.left),
kPageItemSpacing1,
SvgPicture.asset(
ImageConstant.imgUploadLine,
height: 16.h,
width: 16.w,
),
kPageItemSpacing1,
Text(
"Top up",
style: CustomTextStyles
.bodySmallWhiteA700,
),
],
),
),
),
kPageItemSpacing2,
Text(
"History",
style:
theme.textTheme.headlineSmall!.copyWith(
color: kLightPrimaryColor,
),
),
kPageItemSpacing1,
ListView.separated(
shrinkWrap: true,
reverse: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: walletData.history.length,
itemBuilder:
(BuildContext context, int index) {
return CustomWidgetsBuilder
.buildHistoryItemRow(
walletData.history[index],
context,
walletData.history[index]['balance'],
ImageConstant.imgPlay,
"Bought",
CustomButtonStyles.fillGreenA,
);
},
separatorBuilder:
(BuildContext context, int index) {
return CustomWidgetsBuilder
.buildLotteryItemRowDivider(
context,
appTheme.blueGray50,
0,
4.0,
0,
);
},
),
],
),
),
);
},
);
},
),
),
),
);
});
}

void _showCurrencyPopup(
BuildContext context, String jsonData, String userId) async {
showDialog(
context: context,
builder: (BuildContext context) {
return CurrencyPopup(
jsonData: jsonData,
userId: userId,
);
},
);
}
void _showPurchaseDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Available Products'),
content: SizedBox(
width: double.maxFinite,
child: _isLoading
? const Center(child: CircularProgressIndicator())
: _products.isEmpty
? const Text('No products available')
: ListView.builder(
shrinkWrap: true,
itemCount: _products.length,
itemBuilder: (context, index) {
final product = _products[index];
return ListTile(
title: Text(product.title),
subtitle: Text(product.description),
trailing: TextButton(
child: Text(product.price),
onPressed: () {
Navigator.of(context).pop();
_iapService.buy(product);
},
),
);
},
),
),
actions: [
TextButton(
child: const Text('Close'),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
}
}

You might also like