added yearly subscription

This commit is contained in:
Vadim 2025-08-04 00:34:20 +02:00
parent ae3915b73c
commit 490aebccd0
6 changed files with 40 additions and 14 deletions

View file

@ -179,8 +179,10 @@
"noPhotos": "Keine Fotos", "noPhotos": "Keine Fotos",
"continuePurchase": "Weiter", "continuePurchase": "Weiter",
"monthly": "Monatlich", "monthly": "Monatlich",
"yearly": "Jährlich",
"lifetime": "Für immer", "lifetime": "Für immer",
"pricePerMonth": "{price}/Monat", "pricePerMonth": "{price}/Monat",
"pricePerYear": "{price}/Jahr",
"@pricePerMonth": { "@pricePerMonth": {
"placeholders": { "placeholders": {
"price": { "price": {

View file

@ -179,8 +179,10 @@
"noPhotos": "No photos", "noPhotos": "No photos",
"continuePurchase": "Continue", "continuePurchase": "Continue",
"monthly": "Monthly", "monthly": "Monthly",
"yearly": "Yearly",
"lifetime": "Lifetime", "lifetime": "Lifetime",
"pricePerMonth": "{price}/month", "pricePerMonth": "{price}/month",
"pricePerYear": "{price}/year",
"@pricePerMonth": { "@pricePerMonth": {
"placeholders": { "placeholders": {
"price": { "price": {

View file

@ -170,8 +170,10 @@
"noPhotos": "Aucune photo", "noPhotos": "Aucune photo",
"continuePurchase": "Continuer", "continuePurchase": "Continuer",
"monthly": "Mensuel", "monthly": "Mensuel",
"yearly": "Annuel",
"lifetime": "Pour toujours", "lifetime": "Pour toujours",
"pricePerMonth": "{price}/mois", "pricePerMonth": "{price}/mois",
"pricePerYear": "{price}/an",
"@pricePerMonth": { "@pricePerMonth": {
"placeholders": { "placeholders": {
"price": { "price": {

View file

@ -169,8 +169,10 @@
"noPhotos": "Нет фотографий", "noPhotos": "Нет фотографий",
"continuePurchase": "Продолжить", "continuePurchase": "Продолжить",
"monthly": "Ежемесячно", "monthly": "Ежемесячно",
"yearly": "Ежегодно",
"lifetime": "Навсегда", "lifetime": "Навсегда",
"pricePerMonth": "{price}/месяц", "pricePerMonth": "{price}/месяц",
"pricePerYear": "{price}/год",
"@pricePerMonth": { "@pricePerMonth": {
"placeholders": { "placeholders": {
"price": { "price": {

View file

@ -167,8 +167,10 @@
"noPhotos": "没有照片", "noPhotos": "没有照片",
"continuePurchase": "继续", "continuePurchase": "继续",
"monthly": "月付", "monthly": "月付",
"yearly": "年付",
"lifetime": "永久", "lifetime": "永久",
"pricePerMonth": "{price}/月", "pricePerMonth": "{price}/月",
"pricePerYear": "{price}/年",
"@pricePerMonth": { "@pricePerMonth": {
"placeholders": { "placeholders": {
"price": { "price": {

View file

@ -17,6 +17,7 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
late final Future<List<IAPProduct>> productsFuture; late final Future<List<IAPProduct>> productsFuture;
bool _isLoading = true; bool _isLoading = true;
IAPProduct? monthly; IAPProduct? monthly;
IAPProduct? yearly;
IAPProduct? lifetime; IAPProduct? lifetime;
IAPProduct? selected; IAPProduct? selected;
@ -24,8 +25,9 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
void initState() { void initState() {
super.initState(); super.initState();
productsFuture = IAPProductsProvider.of(context).fetchProducts(); productsFuture = IAPProductsProvider.of(context).fetchProducts();
productsFuture.then((products) { productsFuture.then((products) async {
monthly = products.firstWhereOrNull((p) => p.type == PurchaseType.monthly); monthly = products.firstWhereOrNull((p) => p.type == PurchaseType.monthly);
yearly = products.firstWhereOrNull((p) => p.type == PurchaseType.yearly);
lifetime = products.firstWhereOrNull((p) => p.type == PurchaseType.lifetime); lifetime = products.firstWhereOrNull((p) => p.type == PurchaseType.lifetime);
selected = monthly ?? lifetime; selected = monthly ?? lifetime;
}).onError((_, __) { }).onError((_, __) {
@ -68,9 +70,13 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
AnimatedSwitcher( AnimatedSwitcher(
duration: Dimens.durationM, duration: Dimens.durationM,
child: _isLoading child: _isLoading
? const CircularProgressIndicator() ? const SizedBox(
height: 120,
child: Center(child: CircularProgressIndicator()),
)
: _Products( : _Products(
monthly: monthly, monthly: monthly,
yearly: yearly,
lifetime: lifetime, lifetime: lifetime,
selected: selected, selected: selected,
onProductSelected: (value) { onProductSelected: (value) {
@ -98,12 +104,14 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
class _Products extends StatelessWidget { class _Products extends StatelessWidget {
const _Products({ const _Products({
this.monthly, this.monthly,
this.yearly,
this.lifetime, this.lifetime,
required this.selected, required this.selected,
required this.onProductSelected, required this.onProductSelected,
}); });
final IAPProduct? monthly; final IAPProduct? monthly;
final IAPProduct? yearly;
final IAPProduct? lifetime; final IAPProduct? lifetime;
final IAPProduct? selected; final IAPProduct? selected;
final ValueChanged<IAPProduct> onProductSelected; final ValueChanged<IAPProduct> onProductSelected;
@ -114,13 +122,25 @@ class _Products extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (monthly case final monthly?) if (monthly case final monthly?)
_ProductItem( Padding(
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
child: _ProductItem(
title: S.of(context).monthly, title: S.of(context).monthly,
price: S.of(context).pricePerMonth(monthly.price), price: S.of(context).pricePerMonth(monthly.price),
isSelected: selected == monthly, isSelected: selected == monthly,
onPressed: () => onProductSelected(monthly), onPressed: () => onProductSelected(monthly),
), ),
const SizedBox(height: Dimens.grid8), ),
if (yearly case final yearly?)
Padding(
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
child: _ProductItem(
title: S.of(context).yearly,
price: S.of(context).pricePerYear(yearly.price),
isSelected: selected == yearly,
onPressed: () => onProductSelected(yearly),
),
),
if (lifetime case final lifetime?) if (lifetime case final lifetime?)
_ProductItem( _ProductItem(
title: S.of(context).lifetime, title: S.of(context).lifetime,
@ -149,9 +169,8 @@ class _ProductItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
color: isSelected color:
? Theme.of(context).colorScheme.primaryContainer isSelected ? Theme.of(context).colorScheme.primaryContainer : Theme.of(context).colorScheme.surfaceElevated2,
: Theme.of(context).colorScheme.surfaceElevated2,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(Dimens.borderRadiusM), borderRadius: BorderRadius.circular(Dimens.borderRadiusM),
side: isSelected side: isSelected
@ -216,6 +235,3 @@ class _ProductAnimatedText extends StatelessWidget {
); );
} }
} }
/// rgba(212,227,252,255) #d4e3fc
///