mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-08-26 23:16:42 +00:00
reworked restoring purchases
This commit is contained in:
parent
b83f3193c3
commit
430d7abfb0
2 changed files with 71 additions and 52 deletions
|
@ -8,9 +8,11 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
class LightmeterProOffering extends StatefulWidget {
|
class LightmeterProOffering extends StatefulWidget {
|
||||||
const LightmeterProOffering({
|
const LightmeterProOffering({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.isEnabled,
|
||||||
required this.onBuyProduct,
|
required this.onBuyProduct,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool isEnabled;
|
||||||
final ValueChanged<IAPProduct> onBuyProduct;
|
final ValueChanged<IAPProduct> onBuyProduct;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -49,19 +51,26 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (!_isLifetimeOnly)
|
if (!_isLifetimeOnly)
|
||||||
Padding(
|
AnimatedOpacity(
|
||||||
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
duration: Dimens.durationM,
|
||||||
child: _Products(
|
opacity: widget.isEnabled ? Dimens.enabledOpacity : Dimens.disabledOpacity,
|
||||||
monthly: IAPProducts.of(context).monthly,
|
child: IgnorePointer(
|
||||||
yearly: IAPProducts.of(context).yearly,
|
ignoring: !widget.isEnabled,
|
||||||
lifetime: IAPProducts.of(context).lifetime,
|
child: Padding(
|
||||||
selected: selected,
|
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
||||||
onProductSelected: _selectProduct,
|
child: _Products(
|
||||||
|
monthly: IAPProducts.of(context).monthly,
|
||||||
|
yearly: IAPProducts.of(context).yearly,
|
||||||
|
lifetime: IAPProducts.of(context).lifetime,
|
||||||
|
selected: selected,
|
||||||
|
onProductSelected: _selectProduct,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FilledButtonLarge(
|
FilledButtonLarge(
|
||||||
title: S.of(context).continuePurchase,
|
title: S.of(context).continuePurchase,
|
||||||
onPressed: selected != null ? () => widget.onBuyProduct(selected!) : null,
|
onPressed: widget.isEnabled && selected != null ? () => widget.onBuyProduct(selected!) : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,6 +9,8 @@ import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||||
import 'package:lightmeter/utils/text_height.dart';
|
import 'package:lightmeter/utils/text_height.dart';
|
||||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
|
typedef PurchasesState = ({bool isPurchasingProduct, bool isRestoringPurchases});
|
||||||
|
|
||||||
class LightmeterProScreen extends StatefulWidget {
|
class LightmeterProScreen extends StatefulWidget {
|
||||||
const LightmeterProScreen({super.key});
|
const LightmeterProScreen({super.key});
|
||||||
|
|
||||||
|
@ -20,19 +22,34 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
||||||
final features =
|
final features =
|
||||||
defaultTargetPlatform == TargetPlatform.android ? AppFeature.androidFeatures : AppFeature.iosFeatures;
|
defaultTargetPlatform == TargetPlatform.android ? AppFeature.androidFeatures : AppFeature.iosFeatures;
|
||||||
|
|
||||||
final _isRestoringPurchases = ValueNotifier(false);
|
final _purchasesNotifier = ValueNotifier<PurchasesState>(
|
||||||
final _isPurchasingProduct = ValueNotifier(false);
|
(
|
||||||
|
isPurchasingProduct: false,
|
||||||
|
isRestoringPurchases: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverScreen(
|
return SliverScreen(
|
||||||
title: Text(S.of(context).proFeaturesTitle),
|
title: Text(S.of(context).proFeaturesTitle),
|
||||||
appBarActions: [
|
appBarActions: [
|
||||||
/// Restoration is working, but it does not trigger pop
|
ValueListenableBuilder(
|
||||||
/// add await of didChangeDependencies
|
valueListenable: _purchasesNotifier,
|
||||||
_RestorePurchasesButton(
|
builder: (context, value, _) {
|
||||||
valueListenable: _isRestoringPurchases,
|
if (value.isRestoringPurchases) {
|
||||||
onPressed: _restorePurchases,
|
return const SizedBox.square(
|
||||||
|
dimension: Dimens.grid24 - Dimens.grid4,
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: value.isPurchasingProduct ? null : _restorePurchases,
|
||||||
|
icon: const Icon(Icons.restore),
|
||||||
|
tooltip: S.of(context).restorePurchases,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
slivers: [
|
slivers: [
|
||||||
|
@ -75,12 +92,26 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
bottomNavigationBar: LightmeterProOffering(onBuyProduct: _buyPro),
|
bottomNavigationBar: ValueListenableBuilder(
|
||||||
|
valueListenable: _purchasesNotifier,
|
||||||
|
builder: (context, value, _) {
|
||||||
|
return LightmeterProOffering(
|
||||||
|
isEnabled: !value.isRestoringPurchases && !value.isPurchasingProduct,
|
||||||
|
onBuyProduct: _buyPro,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_purchasesNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _restorePurchases() async {
|
Future<void> _restorePurchases() async {
|
||||||
_isRestoringPurchases.value = true;
|
_purchasesNotifier.isRestoringPurchases = true;
|
||||||
try {
|
try {
|
||||||
final isPro = await IAPProductsProvider.of(context).restorePurchases();
|
final isPro = await IAPProductsProvider.of(context).restorePurchases();
|
||||||
if (mounted && isPro) {
|
if (mounted && isPro) {
|
||||||
|
@ -91,12 +122,12 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_showSnackbar(e.toString());
|
_showSnackbar(e.toString());
|
||||||
} finally {
|
} finally {
|
||||||
_isRestoringPurchases.value = true;
|
_purchasesNotifier.isRestoringPurchases = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _buyPro(IAPProduct product) async {
|
Future<void> _buyPro(IAPProduct product) async {
|
||||||
_isPurchasingProduct.value = true;
|
_purchasesNotifier.isPurchasingProduct = true;
|
||||||
try {
|
try {
|
||||||
final isPro = await IAPProductsProvider.of(context).buyPro(product);
|
final isPro = await IAPProductsProvider.of(context).buyPro(product);
|
||||||
if (mounted && isPro) {
|
if (mounted && isPro) {
|
||||||
|
@ -107,7 +138,7 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_showSnackbar(e.toString());
|
_showSnackbar(e.toString());
|
||||||
} finally {
|
} finally {
|
||||||
_isPurchasingProduct.value = false;
|
_purchasesNotifier.isPurchasingProduct = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,37 +154,6 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RestorePurchasesButton extends StatelessWidget {
|
|
||||||
const _RestorePurchasesButton({
|
|
||||||
required this.valueListenable,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ValueNotifier<bool> valueListenable;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ValueListenableBuilder(
|
|
||||||
valueListenable: valueListenable,
|
|
||||||
builder: (context, value, _) {
|
|
||||||
if (value) {
|
|
||||||
return const SizedBox.square(
|
|
||||||
dimension: Dimens.grid24 - Dimens.grid4,
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return IconButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
icon: const Icon(Icons.restore),
|
|
||||||
tooltip: S.of(context).restorePurchases,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FeaturesHeader extends StatelessWidget {
|
class _FeaturesHeader extends StatelessWidget {
|
||||||
const _FeaturesHeader();
|
const _FeaturesHeader();
|
||||||
|
|
||||||
|
@ -292,3 +292,13 @@ class _CheckBox extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension on ValueNotifier<PurchasesState> {
|
||||||
|
set isPurchasingProduct(bool isPurchasingProduct) {
|
||||||
|
value = (isPurchasingProduct: isPurchasingProduct, isRestoringPurchases: value.isRestoringPurchases);
|
||||||
|
}
|
||||||
|
|
||||||
|
set isRestoringPurchases(bool isRestoringPurchases) {
|
||||||
|
value = (isPurchasingProduct: value.isPurchasingProduct, isRestoringPurchases: isRestoringPurchases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue