guru_sdk/guru_ui/lib/pages/subscription/subscription_page.dart

414 lines
12 KiB
Dart
Raw Normal View History

import 'dart:ui';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:design/design.dart';
import 'package:guru_app/financial/iap/iap_model.dart';
import 'package:guru_app/guru_app.dart';
import 'package:guru_ui/guru_widget.dart';
import 'package:guru_ui/localizations/l10n/generated/app_localizations.dart';
import 'package:guru_ui/localizations/ui_strings.dart';
import 'package:guru_ui/pages/subscription/subscription_card.dart';
import 'package:guru_ui/pages/subscription/subscription_controller.dart';
import 'package:guru_widgets/button/single_tap_widget.dart';
import 'package:guru_widgets/pages/webview/guru_webview_page.dart';
import 'package:guru_widgets/theme/guru_theme.dart';
import 'package:guru_popup/guru_popup.dart';
import 'package:guru_utils/router/router.dart';
part 'subscription_page.g.dart';
@DesignSpec(width: 750, height: 1624)
abstract class SubscriptionDesignSpec implements BasicDesignSpec {
@SpecEdgeInsets.only(
start: SpecWidth(40), end: SpecWidth(40), top: SpecStatusBarHeight(50))
EdgeInsetsDirectional get appBarMargin;
@CombinedSpec(SpecHeight(88), SpecStatusBarHeight(50))
double get appBarHeight;
@SpecHeight(48)
double get closeIconSize;
@SpecAbsoluteFontSize(24, consistent: true)
double get restoreFontSize;
@SpecEdgeInsets.only(
top: SpecHeight(24, consistent: true),
bottom: SpecNavigationBarHeight(30),
start: SpecWidth(32),
end: SpecWidth(32))
EdgeInsetsDirectional get bottomCardsPadding;
@SpecHeight(48, consistent: true)
double get premiumCardsBottomSpacing;
@SpecSize(SpecWidth(630), SpecHeight(104))
Size get buttonSize;
@SpecEdgeInsets.only(top: SpecHeight(28), bottom: SpecHeight(32))
EdgeInsetsDirectional get policyPadding;
@SpecAbsoluteFontSize(20)
double get policyFontSize;
@NestedSpec(242, 320)
SubscriptionMainCardDesignSpec get mainCardSpec;
@NestedSpec(206, 268)
SubscriptionEdgeCardDesignSpec get edgeCardSpec;
@NestedSpec(650, 124)
SubscriptionListCardDesignSpec get listCardSpec;
@SpecHeight(22)
double get listCardBottomSpacing;
static SubscriptionDesignSpec get() => _SubscriptionDesignSpec.get();
}
enum SubscriptionCardType { edge, main, list }
class SubscriptionCardStyle {
final Decoration? decoration;
final Color? cardTitleColor;
final Color? cardSubTitleColor;
final Color? cardThenColor;
final Color? cardPriceColor;
SubscriptionCardStyle(
{this.decoration,
this.cardTitleColor,
this.cardSubTitleColor,
this.cardThenColor,
this.cardPriceColor});
}
class SubscriptionTheme {
final Color? backgroundColor;
final Decoration? bottomDecoration;
final SubscriptionCardStyle? cardStyle;
final SubscriptionCardStyle? activeCardStyle;
final GuruButtonStyle buttonStyle;
static SubscriptionTheme defaultTheme =
SubscriptionTheme(buttonStyle: GuruButtonStyle.neutral);
SubscriptionTheme(
{this.backgroundColor,
this.bottomDecoration,
this.cardStyle,
this.activeCardStyle,
required this.buttonStyle});
}
class SubscriptionCardItem {
// final IapProduct productId;
final String title;
final String? subTitle;
final String? thenTexg;
final String price;
final String? labelImage;
final String? labelText;
final String? buttonText;
final SubscriptionCardType type;
SubscriptionCardItem({
// required this.productId,
required this.title,
this.subTitle,
this.thenTexg,
required this.price,
this.labelText,
this.labelImage,
this.buttonText,
this.type = SubscriptionCardType.edge,
});
}
class _UIHolder {
ThemeData? _theme;
GuruThemeData? _guruTheme;
SubscriptionTheme? _subscriptionTheme;
late SubscriptionDesignSpec _designSpec;
late AppLocalizations appStrings;
_UIHolder();
void attach(BuildContext context) {
_theme ??= Theme.of(context);
_guruTheme ??= GuruTheme.of(context);
_subscriptionTheme ??= _guruTheme?.getCustomTheme<SubscriptionTheme>(
SubscriptionTheme.defaultTheme.runtimeType) ??
SubscriptionTheme.defaultTheme;
_designSpec = SubscriptionDesignSpec.get();
appStrings = UIStrings.get();
}
}
mixin _UIData {
final _UIHolder _uiHolder = _UIHolder();
ThemeData get theme => _uiHolder._theme ?? ThemeData();
GuruThemeData get guruTheme => _uiHolder._guruTheme ?? GuruThemeData();
GuruColorScheme get colorScheme => guruTheme.colorScheme;
SubscriptionTheme get subscriptionTheme =>
_uiHolder._subscriptionTheme ?? SubscriptionTheme.defaultTheme;
SubscriptionDesignSpec get designSpec => _uiHolder._designSpec;
AppLocalizations get appStrings => _uiHolder.appStrings;
}
class SubscriptionPage extends GetWidget<SubscriptionController> with _UIData {
final List<SubscriptionCardItem> items;
final Widget Function(BuildContext) contentBuilder;
SubscriptionPage(
{super.key, required this.items, required this.contentBuilder});
Widget _buildPremiumCards(BuildContext context, int index) {
bool hasListItem = false;
for (var item in items) {
if (item.type == SubscriptionCardType.list) {
hasListItem = true;
}
}
final List<Widget> widgets = [];
for (var i = 0; i < items.length; i++) {
final selected = i == index;
if (hasListItem) {
widgets.add(SubscriptionListCard(
model: SubscriptionListCardModel(
cardItem: items[i],
designSpec: designSpec.listCardSpec,
subscriptionTheme: subscriptionTheme,
onTap: () {
controller.updateSelectedProductIndex(i);
},
selected: selected,
product: null),
));
if (i != items.length - 1) {
widgets.add(SizedBox(height: designSpec.listCardBottomSpacing));
}
} else {
if (items[i].type == SubscriptionCardType.edge) {
widgets.add(SubscriptionEdgeCard(
model: SubscriptionEdgeCardModel(
cardItem: items[i],
designSpec: designSpec.edgeCardSpec,
subscriptionTheme: subscriptionTheme,
onTap: () {
controller.updateSelectedProductIndex(i);
},
selected: selected,
product: null),
));
} else {
widgets.add(SubscriptionMainCard(
model: SubscriptionMainCardModel(
cardItem: items[i],
designSpec: designSpec.mainCardSpec,
subscriptionTheme: subscriptionTheme,
onTap: () {
controller.updateSelectedProductIndex(i);
},
selected: selected,
product: null),
));
}
}
}
return Padding(
padding: EdgeInsets.only(bottom: designSpec.premiumCardsBottomSpacing),
child: hasListItem
? Column(
children: widgets,
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: widgets,
));
}
Widget _buildBottom(BuildContext context) {
return Container(
decoration: subscriptionTheme.bottomDecoration ??
const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0x00121212), Color(0xFF121212), Color(0xFF121212)],
stops: [0.0, 55 / 584, 1.0],
),
),
padding: designSpec.bottomCardsPadding,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<int>(
stream: controller.observableSelectProductIndex,
builder: (context, snapshot) {
final int index = snapshot.data ?? 1;
return _buildPremiumCards(context, index);
},
),
StreamBuilder<int>(
stream: controller.observableSelectProductIndex,
builder: (context, snapshot) {
final int index = snapshot.data ?? 1;
return GuruButton(
size: designSpec.buttonSize,
sizeSpec: GuruButtonSizeSpec.s1,
action: items[index].buttonText ?? 'Buy',
onPressed: () {
// final selectedProduct = items[index].productId;
// controller.purchase(selectedProduct, selectedProduct.offerId);
},
);
},
),
Padding(
padding: designSpec.policyPadding,
child: _buildPolicy(),
),
],
),
);
}
Widget _buildContent(BuildContext context) {
return Stack(fit: StackFit.expand, children: [
SingleChildScrollView(
child: contentBuilder(context),
),
Align(
alignment: Alignment.bottomCenter,
child: _buildBottom(context),
),
Align(
alignment: Alignment.topCenter,
child: Container(
width: designSpec.measuredSize.width,
height: designSpec.appBarHeight,
padding: designSpec.appBarMargin,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TapWidget(
onTap: () {
RouteCenter.instance.back();
},
child: guruTheme.iconScheme.closeIcon != null
? Image.asset(
guruTheme.iconScheme.closeIcon!,
width: designSpec.closeIconSize,
height: designSpec.closeIconSize,
fit: BoxFit.fill,
)
: Icon(
Icons.close,
size: designSpec.closeIconSize,
color:
colorScheme.primaryContentColor ?? Colors.white,
),
),
TapWidget(
onTap: () {},
child: Text(
'restore',
style: TextStyle(
color: colorScheme.primaryContentColor ?? Colors.white,
fontSize: designSpec.restoreFontSize,
fontWeight: GuruTheme.fwMedium,
),
),
),
],
),
))
]);
}
@override
Widget build(BuildContext context) {
_uiHolder.attach(context);
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: Scaffold(
backgroundColor: subscriptionTheme.backgroundColor ??
colorScheme.backgroundColor ??
const Color(0xFF121212),
body: _buildContent(context),
),
);
}
Widget _buildPolicy() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_textLink(
text: 'privacyPolicy',
// text: appStrings.privacyPolicy,
url: GuruApp.instance.details.policyUrl,
onAfterTap: () {
GuruAnalytics.instance
.logEventEx("settings", itemName: "privacy_clk");
},
),
Text(
" & ",
style: TextStyle(
fontSize: designSpec.policyFontSize,
color: const Color(0x66FFFFFF),
fontWeight: GuruTheme.fwRegular,
),
),
_textLink(
text: 'termsOfService',
// text: appStrings.termsOfService,
url: GuruApp.instance.details.termsUrl,
onAfterTap: () {
GuruAnalytics.instance.logEventEx("settings", itemName: "tos_clk");
},
)
],
);
}
Widget _textLink(
{required String text, required String url, VoidCallback? onAfterTap}) {
return TapWidget(
child: AutoSizeText(
text,
stepGranularity: 0.1,
minFontSize: 5,
maxLines: 1,
style: TextStyle(
fontSize: designSpec.policyFontSize,
color: const Color(0x66FFFFFF),
decoration: TextDecoration.underline,
fontWeight: GuruTheme.fwRegular,
),
),
onTap: () {
GuruPopup.instance.showDialog(
widget: GuruWebviewPage(url: Uri.dataFromString(url), title: text),
useSafeArea: false);
onAfterTap?.call();
},
);
}
}