Checkout Flutter SDK

A comprehensive Flutter SDK for integrating payment processing into your Flutter applications. This SDK provides a secure, user-friendly checkout experience with support for multiple payment methods, currencies, and customization options across both Android and iOS platforms.

Table of Contents

Overview

The Checkout Flutter SDK allows you to integrate payment processing into your Flutter app with just a few lines of code. It handles the complex payment flow, security, and user interface across both Android and iOS platforms, so you can focus on your app's core functionality.

Github link
pub.dev

What does it do?

  • πŸ’³ Processes payments securely across platforms
  • 🌍 Supports multiple currencies and payment methods
  • πŸ“± Handles both card and digital wallet payments
  • πŸ” Manages security and encryption automatically
  • πŸš€ Works seamlessly on Android and iOS

Features

βœ… Cross-Platform: Works on both Android and iOS

βœ… Multiple Payment Methods: Credit/Debit cards, Apple Pay, Google Pay, and more

βœ… Multi-Currency Support: Process payments in various currencies

βœ… Localization: Support for English and Arabic languages

βœ… Security: Built-in encryption and security measures

βœ… Card Scanner: Scan card details using device camera

βœ… Real-time Validation: Instant feedback on payment details

βœ… Flutter-Native: Built specifically for Flutter development

Installation

Requirements

  • Flutter 3.3.0 or later
  • Dart 3.7.2 or later
  • Android SDK 21+ / iOS 11.0+

Add to pubspec.yaml

dependencies:
  checkout_flutter: ^1.0.0

Install the package

flutter pub get

Platform Setup

Android

No additional setup required! The plugin handles Android configuration automatically.

iOS

No additional setup required! The plugin handles iOS configuration automatically.

Quick Start

Here's how to get started with the Checkout Flutter SDK in just 5 minutes:

1. Import the package

import 'package:checkout_flutter/checkout_flutter.dart';

2. Create a basic checkout

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:checkout_flutter/checkout_flutter.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _checkoutStatus = 'Ready to checkout';

  @override
  void initState() {
    super.initState();
  }

  /// Start the checkout process using direct function calls
  /// No need for CheckoutFlutter class - just call startCheckout() directly
  Future<void> _startCheckout() async {
    try {
      setState(() {
        _checkoutStatus = 'Starting checkout...';
      });
      Map<String, dynamic> configurations = {
        "hashString": "",
        "language": "en",
        "themeMode": "light",
        "supportedPaymentMethods": "ALL",
        "paymentType": "ALL",
        "selectedCurrency": "KWD",
        "supportedCurrencies": "ALL",
        "supportedPaymentTypes": [],
        "supportedRegions": [],
        "supportedSchemes": [],
        "supportedCountries": [],
        "gateway": {
          "publicKey": "pk_test_your_key_here",
          "merchantId": "",
        },
        "customer": {
          "firstName": "Android",
          "lastName": "Test",
          "email": "[email protected]",
          "phone": {"countryCode": "965", "number": "55567890"},
        },
        "transaction": {
          "mode": "charge",
          "charge": {
            "saveCard": true,
            "auto": {"type": "VOID", "time": 100},
            "redirect": {
              "url": "https://demo.staging.tap.company/v2/sdk/checkout",
            },
            "threeDSecure": true,
            "subscription": {
              "type": "SCHEDULED",
              "amount_variability": "FIXED",
              "txn_count": 0,
            },
            "airline": {
              "reference": {"booking": ""},
            },
          },
        },
        "amount": "5",
        "order": {
          "id": "",
          "currency": "KWD",
          "amount": "5",
          "items": [
            {
              "amount": "5",
              "currency": "KWD",
              "name": "Item Title 1",
              "quantity": 1,
              "description": "item description 1",
            },
          ],
        },
        "cardOptions": {
          "showBrands": true,
          "showLoadingState": false,
          "collectHolderName": true,
          "preLoadCardName": "",
          "cardNameEditable": true,
          "cardFundingSource": "all",
          "saveCardOption": "all",
          "forceLtr": false,
          "alternativeCardInputs": {"cardScanner": true, "cardNFC": true},
        },
        "isApplePayAvailableOnClient": true,
      };

      // Call startCheckout function directly
      final success = await startCheckout(
        configurations: configurations,
        onReady: () {
          setState(() {
            _checkoutStatus = 'Checkout is ready!';
          });
          print('Checkout is ready!');
        },
        onSuccess: (data) {
          setState(() {
            _checkoutStatus = 'Payment successful: $data';
          });
          print('Payment successful: $data');
        },
        onError: (error) {
          setState(() {
            _checkoutStatus = 'Payment failed: $error';
          });
          print('Payment failed: $error');
        },
        onClose: () {
          setState(() {
            _checkoutStatus = 'Checkout closed';
          });
          print('Checkout closed');
        },
        onCancel: () {
          setState(() {
            _checkoutStatus = 'Checkout cancelled';
          });
          print('Checkout cancelled (Android)');
        },
      );

      if (!success) {
        setState(() {
          _checkoutStatus = 'Failed to start checkout';
        });
      }
    } catch (e) {
      setState(() {
        _checkoutStatus = 'Error: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Checkout Flutter Example'),
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      Text(
                        'Status',
                        style: Theme.of(context).textTheme.titleMedium,
                      ),
                      SizedBox(height: 8),
                      Text(
                        _checkoutStatus,
                        style: Theme.of(context).textTheme.bodyMedium,
                        textAlign: TextAlign.center,
                      ),
                    ],
                  ),
                ),
              ),
              SizedBox(height: 24),
              ElevatedButton.icon(
                onPressed: _startCheckout,
                icon: Icon(Icons.credit_card),
                label: Text('Start Checkout'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.blue,
                  foregroundColor: Colors.white,
                  padding: EdgeInsets.symmetric(vertical: 16),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

That's it! You now have a working payment system in Flutter.

Basic Usage

Understanding the Flow

sequenceDiagram
    participant App as Flutter App
    participant SDK as Checkout SDK
    participant Native as Native Platform
    participant UI as Payment UI
    participant Server as Payment Server
    
    App->>SDK: startCheckout(config, callbacks)
    SDK->>Native: Initialize native checkout
    Native->>UI: Present checkout interface
    UI->>App: onReady()
    
    Note over UI: User enters payment details
    
    UI->>Server: Process payment
    Server->>UI: Payment result
    
    alt Payment Success
        UI->>App: onSuccess(data)
    else Payment Failed
        UI->>App: onError(error)
    end
    
    UI->>App: onClose()
    Native->>SDK: Cleanup

Step-by-Step Integration

Step 1: Configure Your Payment

Map<String, dynamic> configurations = {
  // Required: Amount to charge
  "amount": "25.99",
  
  // Required: Gateway configuration
  "gateway": {
    "publicKey": "pk_test_your_key_here",
    "merchantId": "your_merchant_id"
  },
  
  // Required: Customer information
  "customer": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "[email protected]",
    "phone": {
      "countryCode": "1",
      "number": "1234567890"
    }
  },
  
  // Required: Order details
  "order": {
    "currency": "USD",
    "amount": "25.99",
    "items": [
      {
        "name": "Premium Subscription",
        "amount": "25.99",
        "currency": "USD",
        "quantity": 1,
        "description": "Monthly premium subscription"
      }
    ]
  }
};

Step 2: Start Checkout with Callbacks

Future<void> _processPayment() async {
  final success = await startCheckout(
    configurations: configurations,
    
    // Called when the checkout UI is ready
    onReady: () {
      print('Checkout interface is ready');
      // Optional: Show loading indicator, log analytics, etc.
    },
    
    // Called when payment is successful
    onSuccess: (String data) {
      print('Payment completed successfully!');
      // Parse the success data and update your UI
      _handlePaymentSuccess(data);
    },
    
    // Called when payment fails
    onError: (String error) {
      print('Payment failed: $error');
      // Handle payment error
      _handlePaymentError(error);
    },
    
    // Called when user closes the checkout
    onClose: () {
      print('User closed the checkout');
      // Handle checkout cancellation
    },
    
    // Called when checkout is cancelled (Android only)
    onCancel: () {
      print('Checkout was cancelled');
      // Handle cancellation specific to Android
    },
  );
  
  if (!success) {
    print('Failed to start checkout');
    // Handle startup failure
  }
}

Step 3: Handle Results

void _handlePaymentSuccess(String data) {
  // Parse the payment data
  try {
    final paymentData = jsonDecode(data);
    final transactionId = paymentData['id'];
    
    // Update your app's state
    setState(() {
      _paymentStatus = 'Payment successful!';
      _transactionId = transactionId;
    });
    
    // Navigate to success screen
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => PaymentSuccessScreen(
          transactionId: transactionId,
        ),
      ),
    );
  } catch (e) {
    print('Failed to parse payment data: $e');
  }
}

void _handlePaymentError(String error) {
  setState(() {
    _paymentStatus = 'Payment failed';
  });
  
  // Show error dialog
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('Payment Failed'),
      content: Text('Please try again or use a different payment method.'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('OK'),
        ),
      ],
    ),
  );
}

Configuration

Basic Configuration Options

Map<String, dynamic> configurations = {
  // REQUIRED FIELDS
  "amount": "10.00",                    // Amount to charge
  "gateway": {...},                     // Gateway configuration
  "customer": {...},                    // Customer details
  "order": {...},                       // Order information
  
  // OPTIONAL FIELDS
  "language": "en",                     // "en" or "ar"
  "themeMode": "light",                 // "light", "dark", "light_mono", "dark_colored"
  "supportedPaymentMethods": "ALL",     // "ALL" or specific methods
  "selectedCurrency": "USD",            // Default currency
  "paymentType": "ALL"                  // "ALL", "WEB", "CARD", "DEVICE"
};

Gateway Configuration

"gateway": {
  "publicKey": "pk_test_your_public_key",    // Your public key from the dashboard
  "merchantId": "your_merchant_id"           // Your merchant ID
}

⚠️ Important: Use pk_test_ for testing and pk_live_ for production.

Customer Configuration

"customer": {
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]",
  "phone": {
    "countryCode": "1",               // Country code without +
    "number": "1234567890"            // Phone number
  }
}

Order Configuration

"order": {
  "id": "order_123",                    // Optional: Your order ID
  "currency": "USD",                    // Currency code (USD, EUR, GBP, etc.)
  "amount": "25.99",                    // Order amount
  "items": [                            // Array of items
    {
      "name": "Product Name",
      "amount": "25.99",
      "currency": "USD",
      "quantity": 1,
      "description": "Product description"
    }
  ]
}

Minimal Configuration

// Use this minimal config for testing
Map<String, dynamic> testConfig = {
  "amount": "1.00",
  "gateway": {
    "publicKey": "pk_test_your_key",
    "merchantId": "test_merchant"
  },
  "customer": {
    "email": "[email protected]"
  },
  "order": {
    "currency": "USD",
    "amount": "1.00"
  }
};

Callbacks

Understanding what each callback means and how to handle them:

onReady()

onReady: () {
  // The checkout interface has loaded and is ready for user interaction
  // This is a good place to:
  // - Hide loading indicators
  // - Log analytics events
  // - Update UI state
  print('Checkout is ready for user interaction');
}

onSuccess(String data)

onSuccess: (String data) {
  // Payment was processed successfully
  // The 'data' parameter contains payment details in JSON format
  
  try {
    final paymentResult = jsonDecode(data);
    final transactionId = paymentResult['id'];
    final amount = paymentResult['amount'];
    
    print('Transaction ID: $transactionId');
    print('Amount: $amount');
    
    // Update your app's state
    // - Save transaction details
    // - Update user's account
    // - Show success message
    // - Navigate to confirmation screen
    
  } catch (e) {
    print('Failed to parse payment data: $e');
  }
}

onError(String error)

onError: (String error) {
  // Payment failed or encountered an error
  // The 'error' parameter contains error details
  
  print('Payment error: $error');
  
  // Common error handling patterns:
  if (error.contains('cancelled')) {
    // User cancelled the payment
    _showMessage('Payment was cancelled');
  } else if (error.contains('declined')) {
    // Payment was declined
    _showErrorDialog('Payment Declined', 'Please check your card details');
  } else if (error.contains('network')) {
    // Network error
    _showErrorDialog('Network Error', 'Please check your internet connection');
  } else {
    // Other errors
    _showErrorDialog('Error', 'Something went wrong. Please try again.');
  }
}

onClose()

onClose: () {
  // The checkout interface was closed
  // This is called after onSuccess, onError, or when user manually closes
  
  print('Checkout interface closed');
  
  // Clean up any resources
  // - Remove loading indicators
  // - Reset UI state
  // - Log analytics
  // - Return to previous screen if needed
}

onCancel() (Android Only)

onCancel: () {
  // Checkout was cancelled (Android-specific callback)
  // This provides additional cancellation handling for Android
  
  print('Checkout was cancelled on Android');
  
  // Handle Android-specific cancellation logic
  // - Show cancellation message
  // - Reset form state
  // - Log cancellation event
}

Advanced Features

Theme Customization

"themeMode": "light"        // Default light theme
"themeMode": "dark"         // Dark theme
"themeMode": "light_mono"   // Light monochrome
"themeMode": "dark_colored" // Dark with colors

Payment Method Selection

// Allow all payment methods
"supportedPaymentMethods": "ALL"

// Specific payment methods
"supportedPaymentMethods": ["VISA", "MASTERCARD", "APPLE_PAY"]

Card Options

"cardOptions": {
  "showBrands": true,                    // Show card brand logos
  "collectHolderName": true,             // Ask for cardholder name
  "cardNameEditable": true,              // Allow editing card name
  "saveCardOption": "all",               // Show save card option
  "alternativeCardInputs": {
    "cardScanner": true,                 // Enable card scanning
    "cardNFC": true                      // Enable NFC reading
  }
}

Multi-language Support

"language": "en"    // English
"language": "ar"    // Arabic

Transaction Configuration

"transaction": {
  "mode": "charge",                      // Transaction mode
  "charge": {
    "saveCard": true,                    // Save card for future use
    "threeDSecure": true,                // Enable 3D Secure
    "redirect": {
      "url": "https://your-redirect-url.com"
    }
  }
}

Example App

The following example app demonstrates all SDK features:

https://github.com/Tap-Payments/Checkout-Flutter/tree/main/example

Running the Example

  1. Navigate to the example directory:
    cd example
  2. Install dependencies:
    flutter pub get
  3. Add your test API keys in lib/main.dart
  4. Run the app:
    flutter run

Key Features Demonstrated

  • Basic Integration: Simple payment flow
  • Configuration Options: All customization options
  • Callback Handling: Proper response handling
  • Error Management: Comprehensive error handling
  • UI Updates: Real-time status updates

Example App Structure

example/
β”œβ”€β”€ lib/
β”‚   └── main.dart              # Main example implementation
β”œβ”€β”€ pubspec.yaml               # Dependencies
└── README.md                  # Example-specific documentation

Platform Support

Android

  • Minimum SDK: 21 (Android 5.0)
  • Target SDK: Latest
  • Supported Features: All payment methods, card scanner, NFC
  • Permissions: Automatically handled by the SDK

iOS

  • Minimum Version: 11.0
  • Supported Features: All payment methods, Apple Pay, card scanner
  • Permissions: Automatically handled by the SDK

Flutter

  • Minimum Version: 3.3.0
  • Dart Version: 3.7.2+
  • Supported Platforms: Android, iOS

Troubleshooting

Common Issues

1. SDK Not Starting

Problem: startCheckout returns false
Solution:

  • Check that all required fields are provided in configuration
  • Verify your public key is correct
  • Ensure proper Flutter/Dart versions
  • Check device connectivity

2. Payment Fails Immediately

Problem: onError is called immediately
Solution:

  • Verify your API keys are correct
  • Check network connectivity
  • Ensure amount is properly formatted as string
  • Validate customer email format

3. Callbacks Not Working

Problem: Callback functions not being called
Solution:

  • Ensure callbacks are properly defined
  • Check for syntax errors in callback functions
  • Verify the SDK version compatibility
  • Test on both Android and iOS

4. Platform-Specific Issues

Problem: Works on one platform but not the other
Solution:

  • Check platform-specific requirements
  • Verify minimum SDK/iOS versions
  • Test on physical devices
  • Check platform-specific permissions
  • Architecture

    SDK Structure Overview

    graph TB
        A[Flutter App] --> B[Checkout Flutter SDK]
        B --> C[Platform Interface]
        C --> D[Method Channel]
        D --> E[Android Native]
        D --> F[iOS Native]
        
        E --> G[Android Checkout UI]
        F --> H[iOS Checkout UI]
        
        B --> I[Callback System]
        I --> J[onReady]
        I --> K[onSuccess]
        I --> L[onError]
        I --> M[onClose]
        I --> N[onCancel]
        
        G --> O[Payment Gateway]
        H --> O

API Reference

startCheckout Function

Future<bool> startCheckout({
  required Map<String, dynamic> configurations,
  Function()? onClose,
  Function()? onReady,
  Function(String)? onSuccess,
  Function(String)? onError,
  Function()? onCancel,
})

Starts the checkout process with the given configuration and callbacks.

Parameters:

  • configurations: Map containing payment configuration (required)
  • onClose: Called when checkout interface is closed (optional)
  • onReady: Called when checkout interface is ready (optional)
  • onSuccess: Called when payment succeeds with data (optional)
  • onError: Called when payment fails with error message (optional)
  • onCancel: Called when checkout is cancelled - Android only (optional)

Returns:

  • Future<bool>: true if checkout started successfully, false otherwise

Preparing Your Flutter App for Release

When you're ready to publish your Flutter app to the Play Store or share it with users
it’s important to build it in release mode to improve performance and reduce app size. Before you start, please follow the official Flutter guide to sign your app:

Add ProGuard Rules

To further optimize and protect your Android app, we recommend adding a ProGuard file.

  • In your project, go to android/app/
  • Create a new file called proguard-rules.pro
  • Copy and paste the following content
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.

# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# ========================================
# FLUTTER RECOMMENDED PROGUARD RULES
# ========================================

# Keep all Flutter classes
-keep class io.flutter.** { *; }
-keep class io.flutter.embedding.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.plugins.** { *; }

# Keep Flutter engine and embedding classes
-keep class io.flutter.embedding.engine.** { *; }
-keep class io.flutter.embedding.android.** { *; }

# Keep Flutter plugin registry and platform channels
-keep class io.flutter.plugin.common.** { *; }
-keep class io.flutter.embedding.engine.plugins.** { *; }

# Keep Flutter method channel implementations
-keep class * implements io.flutter.plugin.common.MethodChannel$MethodCallHandler { *; }
-keep class * implements io.flutter.plugin.common.EventChannel$StreamHandler { *; }

# Keep Flutter platform view factories
-keep class * implements io.flutter.plugin.platform.PlatformViewFactory { *; }
-keep class * implements io.flutter.plugin.platform.PlatformView { *; }

# ========================================
# KEEP ALL TAP SDK CLASSES
# ========================================

# Keep ALL Tap Checkout SDK classes - no obfuscation at all
-keep class company.tap.tapcheckout_android.** { *; }
-keep interface company.tap.tapcheckout_android.** { *; }
-keepnames class company.tap.tapcheckout_android.** { *; }
-keepclassmembers class company.tap.tapcheckout_android.** { *; }

# Keep all inner classes, companion objects, and nested classes
-keep class company.tap.tapcheckout_android.**$* { *; }
-keep class company.tap.tapcheckout_android.**$Companion { *; }
-keep class company.tap.tapcheckout_android.**$** { *; }

# Keep all methods, fields, and constructors for Tap SDK
-keepclassmembers class company.tap.tapcheckout_android.** {
    public <init>(...);
    public <methods>;
    private <methods>;
    protected <methods>;
    <fields>;
}

# ========================================
# KEEP ALL FLUTTER PLUGIN CLASSES
# ========================================

# Keep ALL Flutter plugin classes - no obfuscation at all
-keep class com.example.checkout_flutter.** { *; }
-keep interface com.example.checkout_flutter.** { *; }
-keepnames class com.example.checkout_flutter.** { *; }
-keepclassmembers class com.example.checkout_flutter.** { *; }

# Keep all inner classes and companion objects for plugin
-keep class com.example.checkout_flutter.**$* { *; }
-keep class com.example.checkout_flutter.**$Companion { *; }

# Keep all methods, fields, and constructors for plugin
-keepclassmembers class com.example.checkout_flutter.** {
    public <init>(...);
    public <methods>;
    private <methods>;
    protected <methods>;
    <fields>;
}

# ========================================
# KOTLIN SPECIFIC RULES
# ========================================

# Keep all Kotlin metadata to prevent reflection issues
-keep class kotlin.Metadata { *; }
-keep class kotlin.** { *; }
-keepclassmembers class kotlin.** { *; }

# Keep Kotlin coroutines completely
-keep class kotlinx.coroutines.** { *; }
-keepnames class kotlinx.coroutines.** { *; }
-keepclassmembers class kotlinx.coroutines.** { *; }
-dontwarn kotlinx.coroutines.**

# Keep all companion objects and singletons
-keepnames class * {
    public static ** Companion;
    public static ** INSTANCE;
}

# Keep Kotlin data classes and their properties
-keepclassmembers class * {
    @kotlin.jvm.JvmField <fields>;
    public ** component*();
    public ** copy(...);
    public ** copy$default(...);
}

# ========================================
# SERIALIZATION AND JSON
# ========================================

# Keep Gson classes
-keep class com.google.gson.** { *; }
-keep class * extends com.google.gson.TypeAdapter { *; }
-keep class * implements com.google.gson.TypeAdapterFactory { *; }
-keep class * implements com.google.gson.JsonSerializer { *; }
-keep class * implements com.google.gson.JsonDeserializer { *; }

# Keep serialization annotations
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
    @com.google.gson.annotations.Expose <fields>;
}

# Keep all fields for data classes to prevent ClassCastException
-keepclassmembers class * {
    <fields>;
}

# ========================================
# NETWORK LIBRARIES
# ========================================

# Keep OkHttp3 classes completely
-dontwarn okhttp3.**
-dontwarn okio.**
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-keepnames class okhttp3.** { *; }

# Keep old OkHttp classes (com.squareup.okhttp) - used by some dependencies
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** { *; }
-keep interface com.squareup.okhttp.** { *; }

# Keep Retrofit classes completely  
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepnames class retrofit2.** { *; }

# Keep Picasso and related classes
-keep class com.squareup.picasso.** { *; }
-dontwarn com.squareup.picasso.**

# ========================================
# ANDROID FRAMEWORK CLASSES
# ========================================

# Keep WebView related classes
-keep class android.webkit.** { *; }
-keep class * extends android.webkit.WebViewClient { *; }
-keep class * extends android.webkit.WebChromeClient { *; }

# Keep custom view classes with all constructors
-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public <init>(android.content.Context, android.util.AttributeSet, int, int);
    public void set*(...);
    *** get*();
    <fields>;
    <methods>;
}

# Keep Parcelable implementations
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
    <fields>;
    <methods>;
}

# Keep enum classes
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
    **[] $VALUES;
    public *;
}

# Keep native methods
-keepclasseswithmembernames class * {
    native <methods>;
}

# ========================================
# ESSENTIAL ATTRIBUTES
# ========================================

# Keep all annotations and signatures for runtime reflection
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes InnerClasses
-keepattributes EnclosingMethod
-keepattributes RuntimeVisibleAnnotations
-keepattributes RuntimeInvisibleAnnotations
-keepattributes RuntimeVisibleParameterAnnotations
-keepattributes RuntimeInvisibleParameterAnnotations

# Keep source file names and line numbers for better crash reports
-keepattributes SourceFile,LineNumberTable

# ========================================
# PREVENT COMMON ISSUES
# ========================================

# Keep classes that might be instantiated via reflection
-keep class * {
    public <init>();
    public <init>(...);
}

# Additional safety rules to prevent ClassCastException
-keep,allowshrinking,allowoptimization class * {
    <fields>;
}

# ========================================
# MISSING CLASSES - IGNORE WARNINGS FOR NON-ESSENTIAL CLASSES
# ========================================

# Google Play Core classes - ignore warnings (not essential for basic functionality)
-dontwarn com.google.android.play.core.splitcompat.**
-dontwarn com.google.android.play.core.splitinstall.**
-dontwarn com.google.android.play.core.tasks.**

# Old OkHttp (com.squareup.okhttp) - ignore warnings for Picasso compatibility
-dontwarn com.squareup.okhttp.**

# Java beans classes - ignore warnings (not available on Android)
-dontwarn java.beans.**

# Jackson databind classes - ignore warnings
-dontwarn com.fasterxml.jackson.**

# Other JVM-specific classes not available on Android
-dontwarn java.lang.instrument.**
-dontwarn sun.misc.**
-dontwarn javax.annotation.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# Ignore warnings for classes that may not be available in all Android versions
-dontwarn android.support.**
-dontwarn androidx.annotation.Keep

Build Your App for Release

Once you’ve set that up, you can build your app in release mode.
Just run the commands in your terminal:

flutter build apk --release

Support

For questions, issues, or feature requests: