From 09cee9931bd36ebf49c4eb44ce35e1e407fa5637 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sun, 12 Apr 2026 13:08:36 +0530 Subject: [PATCH] refactor: migrate Stripe checkout to external browser with lifecycle-based subscription refresh --- .../presentation/widgets/billing_tab.dart | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/features/profile/presentation/widgets/billing_tab.dart b/lib/features/profile/presentation/widgets/billing_tab.dart index 77637bd..1c281e7 100644 --- a/lib/features/profile/presentation/widgets/billing_tab.dart +++ b/lib/features/profile/presentation/widgets/billing_tab.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:real_estate_mobile/core/constants/app_colors.dart'; import 'package:real_estate_mobile/features/profile/presentation/providers/profile_provider.dart'; -import 'package:real_estate_mobile/features/profile/presentation/screens/stripe_checkout_screen.dart'; class BillingTab extends ConsumerStatefulWidget { const BillingTab({super.key}); @@ -15,16 +13,33 @@ class BillingTab extends ConsumerStatefulWidget { ConsumerState createState() => _BillingTabState(); } -class _BillingTabState extends ConsumerState { +class _BillingTabState extends ConsumerState + with WidgetsBindingObserver { Map? _subscription; bool _isLoadingSubscription = false; + bool _openedExternalCheckout = false; @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); _loadSubscription(); } + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed && _openedExternalCheckout) { + _openedExternalCheckout = false; + _loadSubscription(); + } + } + Future _loadSubscription() async { if (_isLoadingSubscription) return; setState(() => _isLoadingSubscription = true); @@ -51,7 +66,10 @@ class _BillingTabState extends ConsumerState { _subscription!['status'] == 'ACTIVE'; final plan = _subscription?['plan'] as Map?; - return ListView( + return RefreshIndicator( + color: AppColors.accentOrange, + onRefresh: _loadSubscription, + child: ListView( padding: const EdgeInsets.fromLTRB(24, 10, 24, 30), children: [ const Center( @@ -109,6 +127,7 @@ class _BillingTabState extends ConsumerState { const SizedBox(height: 24), _buildNeedHelpSection(), ], + ), ); } @@ -371,6 +390,15 @@ class _BillingTabState extends ConsumerState { ), ], ), + const SizedBox(height: 12), + Text( + 'By clicking this button you\'ll be taken to our website to complete your purchase.', + style: TextStyle( + fontFamily: 'SourceSerif4', + fontSize: 12, + color: AppColors.primaryDark.withValues(alpha: 0.5), + ), + ), const SizedBox(height: 16), const Text( 'Include Features', @@ -555,18 +583,13 @@ class _BillingTabState extends ConsumerState { final repo = ref.read(profileRepositoryProvider); final url = await repo.createPortalSession(); if (!mounted) return; - await Navigator.of(context, rootNavigator: true).push?>( - MaterialPageRoute( - builder: (_) => StripeCheckoutScreen( - url: url, - title: 'Manage Subscription', - ), - ), + _openedExternalCheckout = true; + await launchUrl( + Uri.parse(url), + mode: LaunchMode.externalApplication, ); - if (mounted) { - _loadSubscription(); - } } catch (e) { + _openedExternalCheckout = false; if (mounted) _showSnackBar('Failed to open billing portal: $e', isError: true); } } @@ -582,22 +605,13 @@ class _BillingTabState extends ConsumerState { final planId = plans.first['id'] as String; final url = await repo.createCheckoutSession(planId); if (!mounted) return; - final result = await Navigator.of(context, rootNavigator: true).push?>( - MaterialPageRoute( - builder: (_) => StripeCheckoutScreen( - url: url, - title: 'Checkout', - successUrlPattern: 'confirmation', - ), - ), + _openedExternalCheckout = true; + await launchUrl( + Uri.parse(url), + mode: LaunchMode.externalApplication, ); - if (result != null && result['success'] == true && mounted) { - final sessionId = result['sessionId'] as String?; - context.go('/payment-success${sessionId != null ? '?session_id=$sessionId' : ''}'); - } else if (mounted) { - _loadSubscription(); - } } catch (e) { + _openedExternalCheckout = false; if (mounted) _showSnackBar('Failed to start checkout: $e', isError: true); } }