mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2025-08-26 15:06:43 +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 {
|
||||
const LightmeterProOffering({
|
||||
super.key,
|
||||
required this.isEnabled,
|
||||
required this.onBuyProduct,
|
||||
});
|
||||
|
||||
final bool isEnabled;
|
||||
final ValueChanged<IAPProduct> onBuyProduct;
|
||||
|
||||
@override
|
||||
|
@ -49,19 +51,26 @@ class _LightmeterProOfferingState extends State<LightmeterProOffering> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!_isLifetimeOnly)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
||||
child: _Products(
|
||||
monthly: IAPProducts.of(context).monthly,
|
||||
yearly: IAPProducts.of(context).yearly,
|
||||
lifetime: IAPProducts.of(context).lifetime,
|
||||
selected: selected,
|
||||
onProductSelected: _selectProduct,
|
||||
AnimatedOpacity(
|
||||
duration: Dimens.durationM,
|
||||
opacity: widget.isEnabled ? Dimens.enabledOpacity : Dimens.disabledOpacity,
|
||||
child: IgnorePointer(
|
||||
ignoring: !widget.isEnabled,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: Dimens.paddingS),
|
||||
child: _Products(
|
||||
monthly: IAPProducts.of(context).monthly,
|
||||
yearly: IAPProducts.of(context).yearly,
|
||||
lifetime: IAPProducts.of(context).lifetime,
|
||||
selected: selected,
|
||||
onProductSelected: _selectProduct,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
FilledButtonLarge(
|
||||
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:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
|
||||
typedef PurchasesState = ({bool isPurchasingProduct, bool isRestoringPurchases});
|
||||
|
||||
class LightmeterProScreen extends StatefulWidget {
|
||||
const LightmeterProScreen({super.key});
|
||||
|
||||
|
@ -20,19 +22,34 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
|||
final features =
|
||||
defaultTargetPlatform == TargetPlatform.android ? AppFeature.androidFeatures : AppFeature.iosFeatures;
|
||||
|
||||
final _isRestoringPurchases = ValueNotifier(false);
|
||||
final _isPurchasingProduct = ValueNotifier(false);
|
||||
final _purchasesNotifier = ValueNotifier<PurchasesState>(
|
||||
(
|
||||
isPurchasingProduct: false,
|
||||
isRestoringPurchases: false,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverScreen(
|
||||
title: Text(S.of(context).proFeaturesTitle),
|
||||
appBarActions: [
|
||||
/// Restoration is working, but it does not trigger pop
|
||||
/// add await of didChangeDependencies
|
||||
_RestorePurchasesButton(
|
||||
valueListenable: _isRestoringPurchases,
|
||||
onPressed: _restorePurchases,
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _purchasesNotifier,
|
||||
builder: (context, value, _) {
|
||||
if (value.isRestoringPurchases) {
|
||||
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: [
|
||||
|
@ -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 {
|
||||
_isRestoringPurchases.value = true;
|
||||
_purchasesNotifier.isRestoringPurchases = true;
|
||||
try {
|
||||
final isPro = await IAPProductsProvider.of(context).restorePurchases();
|
||||
if (mounted && isPro) {
|
||||
|
@ -91,12 +122,12 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
|||
} catch (e) {
|
||||
_showSnackbar(e.toString());
|
||||
} finally {
|
||||
_isRestoringPurchases.value = true;
|
||||
_purchasesNotifier.isRestoringPurchases = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _buyPro(IAPProduct product) async {
|
||||
_isPurchasingProduct.value = true;
|
||||
_purchasesNotifier.isPurchasingProduct = true;
|
||||
try {
|
||||
final isPro = await IAPProductsProvider.of(context).buyPro(product);
|
||||
if (mounted && isPro) {
|
||||
|
@ -107,7 +138,7 @@ class _LightmeterProScreenState extends State<LightmeterProScreen> {
|
|||
} catch (e) {
|
||||
_showSnackbar(e.toString());
|
||||
} 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 {
|
||||
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