aptoide_iap 0.0.1 copy "aptoide_iap: ^0.0.1" to clipboard
aptoide_iap: ^0.0.1 copied to clipboard

PlatformiOS

A Flutter plugin for Aptoide IAP on iOS. Wraps the AppCoins SDK for in-app purchases on iOS 18.0+ devices in the EU.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:aptoide_iap/aptoide_iap.dart';
import 'package:aptoide_iap/aptoide_environment.dart';

// 🧪 EXAMPLE APP CONFIGURATION
// This example app uses SANDBOX environment by default for safe testing.
// No real charges will occur in sandbox mode.
// You can switch to production using the menu in the AppBar.

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Aptoide IAP Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Aptoide IAP 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> {
  bool _isAvailable = false;
  bool _isLoading = true;
  List<Map<String, dynamic>> _products = [];
  String _statusMessage = 'Initializing...';
  AptoideEnvironment _currentEnvironment = AptoideEnvironment.sandbox; 

  @override
  void initState() {
    super.initState();
  
    AptoideIap.setLoggingEnabled(true);
    _initializeIAP();
  }

 
  Future<void> _initializeIAP() async {
    try {
   
      await AptoideIap.initialize(
        environment: _currentEnvironment,
        onUnfinishedPurchase: (purchase) async {
          setState(() {
            _statusMessage = 'Restoring purchase: ${purchase['sku']}';
          });
          
  
          await _grantItemToUser(purchase['sku']);
          
          await AptoideIap.finishPurchase(purchase['id']);
          
          setState(() {
            _statusMessage = 'Restored: ${purchase['sku']}';
          });
        },
      );

    
      final available = await AptoideIap.isAvailable;
      
      setState(() {
        _isAvailable = available;
        _statusMessage = available 
            ? 'Aptoide IAP is available' 
            : 'Aptoide IAP not available (requires iOS 17.4+ in EU)';
      });

      if (available) {
        await _loadProducts();
      }
    } catch (e) {
      setState(() {
        _statusMessage = 'Error: $e';
      });
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }


  Future<void> _loadProducts() async {
    try {
      setState(() {
        _statusMessage = 'Loading products...';
      });

      final products = await AptoideIap.queryProducts([
        'com.ranking.filter.weekly',
        'com.ranking.filter.monthly',
        'com.ranking.filter.yearly',
      ]);

      setState(() {
        _products = products;
        _statusMessage = 'Loaded ${products.length} products';
      });
    } catch (e) {
      setState(() {
        _statusMessage = 'Failed to load products: $e';
      });
    }
  }

  Future<void> _purchaseProduct(String sku) async {
    try {
      setState(() {
        _statusMessage = 'Purchasing $sku...';
      });

      final purchase = await AptoideIap.purchase(
        sku,
        payload: 'user_123',
      );


      final verified = purchase['verified'] == true;
      
      if (verified) {
     
        await _grantItemToUser(sku);
        
        setState(() {
          _statusMessage = 'Purchase successful: $sku';
        });
        
        if (mounted) {
          _showSuccessDialog('Purchase completed successfully!');
        }
      } else {
  
        setState(() {
          _statusMessage = 'Purchase unverified: $sku (${purchase['verificationError']})';
        });
        
        if (mounted) {
          _showWarningDialog(
            'Purchase completed but verification failed. '
            'This may be refunded automatically.'
          );
        }
      }
    } on PlatformException catch (e) {
      String message;
      switch (e.code) {
        case 'CANCELLED':
          message = 'Purchase cancelled';
          break;
        case 'PENDING':
          message = 'Purchase pending';
          break;
        case 'FAILED':
          message = 'Purchase failed: ${e.message}';
          break;
        default:
          message = 'Error: ${e.message}';
      }
      
      setState(() {
        _statusMessage = message;
      });
      
      if (mounted && e.code != 'CANCELLED') {
        _showErrorDialog(message);
      }
    }
  }


  Future<void> _grantItemToUser(String sku) async {
  
    
    print('Granting item: $sku');
    

    await Future.delayed(const Duration(milliseconds: 500));
  }

  void _showSuccessDialog(String message) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Success'),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  void _showErrorDialog(String message) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Error'),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }

  void _showWarningDialog(String message) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Warning'),
        content: Text(message),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('OK'),
          ),
        ],
      ),
    );
  }


  Future<void> _switchEnvironment(AptoideEnvironment newEnvironment) async {
    try {
      setState(() {
        _statusMessage = 'Switching to ${newEnvironment.value}...';
        _isLoading = true;
      });

      await AptoideIap.setEnvironment(newEnvironment);
      
      setState(() {
        _currentEnvironment = newEnvironment;
        _statusMessage = 'Environment: ${newEnvironment.value}';
      });


      if (_isAvailable) {
        await _loadProducts();
      }
    } catch (e) {
      setState(() {
        _statusMessage = 'Failed to switch environment: $e';
      });
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
        actions: [
         
          PopupMenuButton<AptoideEnvironment>(
            icon: Icon(
              _currentEnvironment == AptoideEnvironment.sandbox
                  ? Icons.science
                  : Icons.rocket_launch,
            ),
            tooltip: 'Switch Environment',
            onSelected: _switchEnvironment,
            itemBuilder: (context) => [
              PopupMenuItem(
                value: AptoideEnvironment.sandbox,
                child: Row(
                  children: [
                    const Icon(Icons.science, size: 20),
                    const SizedBox(width: 8),
                    Text(
                      'Sandbox',
                      style: TextStyle(
                        fontWeight: _currentEnvironment == AptoideEnvironment.sandbox
                            ? FontWeight.bold
                            : FontWeight.normal,
                      ),
                    ),
                  ],
                ),
              ),
              PopupMenuItem(
                value: AptoideEnvironment.production,
                child: Row(
                  children: [
                    const Icon(Icons.rocket_launch, size: 20),
                    const SizedBox(width: 8),
                    Text(
                      'Production',
                      style: TextStyle(
                        fontWeight: _currentEnvironment == AptoideEnvironment.production
                            ? FontWeight.bold
                            : FontWeight.normal,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              children: [
         
                Container(
                  width: double.infinity,
                  padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                  color: _currentEnvironment == AptoideEnvironment.sandbox
                      ? Colors.amber[100]
                      : Colors.blue[100],
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        _currentEnvironment == AptoideEnvironment.sandbox
                            ? Icons.science
                            : Icons.rocket_launch,
                        size: 16,
                        color: _currentEnvironment == AptoideEnvironment.sandbox
                            ? Colors.amber[900]
                            : Colors.blue[900],
                      ),
                      const SizedBox(width: 8),
                      Text(
                        'Environment: ${_currentEnvironment.value.toUpperCase()}',
                        style: TextStyle(
                          color: _currentEnvironment == AptoideEnvironment.sandbox
                              ? Colors.amber[900]
                              : Colors.blue[900],
                          fontWeight: FontWeight.bold,
                          fontSize: 12,
                        ),
                      ),
                    ],
                  ),
                ),
              
                // Status message
                Container(
                  width: double.infinity,
                  padding: const EdgeInsets.all(16),
                  color: _isAvailable ? Colors.green[100] : Colors.orange[100],
                  child: Text(
                    _statusMessage,
                    style: TextStyle(
                      color: _isAvailable ? Colors.green[900] : Colors.orange[900],
                      fontWeight: FontWeight.bold,
                    ),
                    textAlign: TextAlign.center,
                  ),
                ),
                
        
                Expanded(
                  child: _isAvailable
                      ? _products.isEmpty
                          ? const Center(
                              child: Text('No products available'),
                            )
                          : ListView.builder(
                              padding: const EdgeInsets.all(16),
                              itemCount: _products.length,
                              itemBuilder: (context, index) {
                                final product = _products[index];
                                return Card(
                                  margin: const EdgeInsets.only(bottom: 12),
                                  child: ListTile(
                                    title: Text(
                                      product['title'] ?? 'Unknown',
                                      style: const TextStyle(
                                        fontWeight: FontWeight.bold,
                                      ),
                                    ),
                                    subtitle: Text(
                                      product['description'] ?? '',
                                    ),
                                    trailing: ElevatedButton(
                                      onPressed: () => _purchaseProduct(
                                        product['id'],
                                      ),
                                      child: Text(
                                        product['priceLabel'] ?? 'Buy',
                                      ),
                                    ),
                                  ),
                                );
                              },
                            )
                      : Center(
                          child: Padding(
                            padding: const EdgeInsets.all(24),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Icon(
                                  Icons.info_outline,
                                  size: 64,
                                  color: Colors.grey[400],
                                ),
                                const SizedBox(height: 16),
                                Text(
                                  'Aptoide IAP Not Available',
                                  style: Theme.of(context).textTheme.headlineSmall,
                                  textAlign: TextAlign.center,
                                ),
                                const SizedBox(height: 8),
                                Text(
                                  'Requirements:\n'
                                  '• iOS 17.4 or later\n'
                                  '• Device in EU region\n'
                                  '• App distributed via Aptoide',
                                  style: Theme.of(context).textTheme.bodyMedium,
                                  textAlign: TextAlign.center,
                                ),
                              ],
                            ),
                          ),
                        ),
                ),
              ],
            ),
      floatingActionButton: _isAvailable
          ? FloatingActionButton(
              onPressed: _loadProducts,
              tooltip: 'Refresh Products',
              child: const Icon(Icons.refresh),
            )
          : null,
    );
  }
}
0
likes
160
points
78
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for Aptoide IAP on iOS. Wraps the AppCoins SDK for in-app purchases on iOS 18.0+ devices in the EU.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on aptoide_iap

Packages that implement aptoide_iap