knt_base_monetized 4.2.2 copy "knt_base_monetized: ^4.2.2" to clipboard
knt_base_monetized: ^4.2.2 copied to clipboard

Helper blocs for handling common in-app-purchase flow

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:knt_base_monetized/knt_base_monetized.dart';
import 'package:knt_bloc/knt_bloc.dart';

import 'bloc/subscription_bloc.dart';
import 'data/monetize_repo.dart';
import 'data/purchase_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Knt Monetized Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BlocProvider(
        create: (BuildContext context) => SubscriptionBloc(
          ArticleMonetizedRepo(),
          entitlementID: Purchases.defaultEntitlementID,
        ),
        child: const MyHomePage(title: 'Knt Monetized Demo'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _iapTag = (MyHomePage).toString();
  final _logs = <String>[];

  @override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero, _onFetchSku);
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<SubscriptionBloc, BaseState>(
      listener: (context, state) {
        if (!mounted) {
          return;
        }

        void showSnackBar(String message) {
          ScaffoldMessenger.of(context).removeCurrentSnackBar();
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              duration: const Duration(milliseconds: 2811),
              content: Text(message),
            ),
          );
        }

        final stateTag = state.tag;
        final timeStamps = DateTime.now();
        final newMessageLog = '[${DateFormat.Hms().format(timeStamps)}]: '
            '${state.runtimeType} with tag [$stateTag]';
        showSnackBar(newMessageLog);
        setState(() {
          _logs.insert(0, newMessageLog);
        });
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            BlocBuilder<SubscriptionBloc, BaseState>(
              buildWhen: (previous, current) {
                return current is FetchingOfferingsState ||
                    current is FetchedOfferingsState ||
                    current is FetchedOfferingsFailureState;
              },
              builder: (context, state) => switch (state) {
                FetchedOfferingsFailureState() => Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text(
                        'Error: ${state.exception.additionalInfo}',
                        style: Theme.of(context).textTheme.bodyLarge,
                      ),
                      const SizedBox(height: 8),
                      TextButton(
                        onPressed: _onFetchSku,
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(
                              Icons.refresh,
                              size: 24,
                            ),
                            SizedBox(width: 8),
                            Text('Retry'),
                          ],
                        ),
                      ),
                    ],
                  ),
                FetchingOfferingsState() => const SizedBox(
                    height: 240,
                    child: Center(
                      child: CircularProgressIndicator(),
                    ),
                  ),
                FetchedOfferingsState(:final data) => Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: _SubscriptionBox(
                      itemList: data
                        ..sort(
                          (s1, s2) => s2.price.compareTo(s1.price),
                        ),
                      onItemPicked: _onItemPicked,
                    ),
                  ),
                _ => const SizedBox(),
              },
            ),
            const SizedBox(height: 16),
            Expanded(
              child: SingleChildScrollView(
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: Column(
                  children: [
                    for (final (index, log) in _logs.indexed)
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Container(
                            width: 20,
                            height: 20,
                            margin: const EdgeInsets.symmetric(vertical: 2),
                            decoration: const BoxDecoration(
                              shape: BoxShape.circle,
                              color: Colors.green,
                            ),
                            child: Center(
                              child: FittedBox(
                                child: Text(
                                  '${_logs.length - index}',
                                  style: Theme.of(context)
                                      .textTheme
                                      .labelSmall
                                      ?.copyWith(color: Colors.white),
                                ),
                              ),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: Text(log),
                          ),
                        ],
                      ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              child: Row(
                children: [
                  Expanded(
                    child: ElevatedButton(
                      onPressed: _onFetchSku,
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.red,
                      ),
                      child: Text(
                        'Retry',
                        style: Theme.of(context)
                            .textTheme
                            .bodyMedium
                            ?.copyWith(color: Colors.white),
                      ),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: _onRestore,
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.green,
                      ),
                      child: Text(
                        'Restore',
                        style: Theme.of(context)
                            .textTheme
                            .bodyMedium
                            ?.copyWith(color: Colors.white),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 32),
          ],
        ),
      ),
    );
  }

  void _onFetchSku() {
    _makeLogGap();
    context.read<SubscriptionBloc>().add(FetchOfferingsEvent(tag: _iapTag));
  }

  void _onItemPicked(SubscriptionItem item) {
    _makeLogGap();
    context.read<SubscriptionBloc>().add(
          RequestSubscriptionEvent(
            item,
            tag: _iapTag,
          ),
        );
  }

  void _onRestore() {
    _makeLogGap();
    context
        .read<SubscriptionBloc>()
        .add(RestoreSubscriptionEvent(tag: _iapTag));
  }

  void _makeLogGap() {
    setState(() {
      _logs.insert(0, '---------------------');
    });
  }
}

class _SubscriptionBox extends StatelessWidget {
  const _SubscriptionBox({
    this.itemList = const [],
    this.onItemPicked,
  });

  final List<SubscriptionItem> itemList;
  final ValueSetter<SubscriptionItem>? onItemPicked;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        for (final item in itemList)
          InkWell(
            onTap: () => onItemPicked?.call(item),
            child: Container(
              margin: const EdgeInsets.only(top: 12),
              padding: const EdgeInsets.symmetric(
                horizontal: 16,
                vertical: 8,
              ),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                border: Border.all(
                  color: (item.period == Period.annually
                          ? Colors.red
                          : Colors.grey)
                      .withOpacity(0.3),
                ),
              ),
              child: Row(
                children: [
                  const Icon(
                    Icons.shopping_cart,
                    size: 24,
                    color: Colors.orange,
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          item.content,
                          style: Theme.of(context).textTheme.bodyLarge,
                        ),
                        const SizedBox(height: 4),
                        Text(
                          '${item.sku} - ${item.localizedPrice}',
                          style: Theme.of(context)
                              .textTheme
                              .bodyMedium
                              ?.copyWith(color: Colors.green),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        const SizedBox(height: 16),
      ],
    );
  }
}
0
likes
160
points
215
downloads

Publisher

verified publisherappixi.net

Weekly Downloads

Helper blocs for handling common in-app-purchase flow

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

flutter, knt_bloc, meta

More

Packages that depend on knt_base_monetized