414 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
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();
 | 
						|
      },
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |