1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
| | import 'package:flutter/material.dart';
| | import 'package:flutter_riverpod/flutter_riverpod.dart';
| |
| | import '../providers/providers.dart';
| | import '../services/purchase_service.dart';
| | import '../theme/app_theme.dart';
| |
| | /// Dismissible banner shown at the top of the chat screen when a free-tier
| | /// limit has been reached. Tapping "Upgrade" initiates the IAP flow.
| | class PaywallBanner extends ConsumerStatefulWidget {
| | const PaywallBanner({super.key});
| |
| | @override
| | ConsumerState<PaywallBanner> createState() => _PaywallBannerState();
| | }
| |
| | class _PaywallBannerState extends ConsumerState<PaywallBanner> {
| | bool _dismissed = false;
| |
| | @override
| | Widget build(BuildContext context) {
| | final isPro = ref.watch(isProProvider);
| | if (isPro || _dismissed) return const SizedBox.shrink();
| |
| | final sessions = ref.watch(sessionsProvider);
| | if (sessions.length <= kFreeTierMaxSessions) return const SizedBox.shrink();
| |
| | return Material(
| | color: Colors.transparent,
| | child: Container(
| | margin: const EdgeInsets.fromLTRB(8, 4, 8, 0),
| | decoration: BoxDecoration(
| | color: AppColors.accent.withAlpha(230),
| | borderRadius: BorderRadius.circular(10),
| | ),
| | child: Padding(
| | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
| | child: Row(
| | children: [
| | const Icon(Icons.lock_outline, color: Colors.white, size: 18),
| | const SizedBox(width: 8),
| | Expanded(
| | child: Column(
| | crossAxisAlignment: CrossAxisAlignment.start,
| | mainAxisSize: MainAxisSize.min,
| | children: [
| | const Text(
| | 'PAILot Pro',
| | style: TextStyle(
| | color: Colors.white,
| | fontWeight: FontWeight.bold,
| | fontSize: 13,
| | ),
| | ),
| | const Text(
| | 'Unlimited sessions & persistent messages',
| | style: TextStyle(color: Colors.white70, fontSize: 11),
| | ),
| | ],
| | ),
| | ),
| | const SizedBox(width: 8),
| | TextButton(
| | onPressed: _handleRestore,
| | style: TextButton.styleFrom(
| | foregroundColor: Colors.white70,
| | padding: const EdgeInsets.symmetric(horizontal: 8),
| | minimumSize: Size.zero,
| | tapTargetSize: MaterialTapTargetSize.shrinkWrap,
| | ),
| | child: const Text('Restore', style: TextStyle(fontSize: 11)),
| | ),
| | ElevatedButton(
| | onPressed: _handleUpgrade,
| | style: ElevatedButton.styleFrom(
| | backgroundColor: Colors.white,
| | foregroundColor: AppColors.accent,
| | padding:
| | const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
| | minimumSize: Size.zero,
| | tapTargetSize: MaterialTapTargetSize.shrinkWrap,
| | textStyle: const TextStyle(
| | fontSize: 12,
| | fontWeight: FontWeight.bold,
| | ),
| | ),
| | child: const Text('Upgrade \$4.99'),
| | ),
| | const SizedBox(width: 4),
| | GestureDetector(
| | onTap: () => setState(() => _dismissed = true),
| | child: const Icon(Icons.close, color: Colors.white70, size: 16),
| | ),
| | ],
| | ),
| | ),
| | ),
| | );
| | }
| |
| | Future<void> _handleUpgrade() async {
| | await PurchaseService.instance.purchaseFullAccess();
| | }
| |
| | Future<void> _handleRestore() async {
| | await PurchaseService.instance.restorePurchases();
| | if (mounted) {
| | ScaffoldMessenger.of(context).showSnackBar(
| | const SnackBar(
| | content: Text('Checking for previous purchases...'),
| | duration: Duration(seconds: 2),
| | ),
| | );
| | }
| | }
| | }
|
|