Skip to main content
ParityEdge
Flutter (Dart)
Mobile & Cross-Platform

Flutter (Dart) Multi-Platform Integration

Resolve localized purchasing power parity pricing policies across Flutter apps on iOS, Android, Web, macOS, and Windows with offline caching.

Production Flutter Service with SharedPreferences Caching

Create a thread-safe singleton service that caches resolved PPP models in SharedPreferences for instant offline rendering:

// lib/services/parity_edge_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

class ParityModel {
  final bool eligible;
  final int discountPercentage;
  final String couponCode;
  final String countryName;
  final String countryCode;
  final String currency;
  final int tier;

  ParityModel({
    required this.eligible,
    required this.discountPercentage,
    required this.couponCode,
    required this.countryName,
    required this.countryCode,
    required this.currency,
    required this.tier,
  });

  factory ParityModel.fromJson(Map<String, dynamic> json) {
    return ParityModel(
      eligible: json['eligible'] ?? false,
      discountPercentage: json['discountPercentage'] ?? 0,
      couponCode: json['couponCode'] ?? '',
      countryName: json['countryName'] ?? 'United States',
      countryCode: json['countryCode'] ?? 'US',
      currency: json['currency'] ?? 'USD',
      tier: json['tier'] ?? 1,
    );
  }

  Map<String, dynamic> toJson() => {
    'eligible': eligible,
    'discountPercentage': discountPercentage,
    'couponCode': couponCode,
    'countryName': countryName,
    'countryCode': countryCode,
    'currency': currency,
    'tier': tier,
  };
}

class ParityEdgeService {
  static const String _endpoint =
      'https://api.parityedge.app/v1/lookup?pid=prj_live_YOUR_KEY';
  static const String _cacheKey = 'parity_edge_cached_model';

  static Future<ParityModel?> resolveParity() async {
    final prefs = await SharedPreferences.getInstance();

    try {
      final response = await http.get(Uri.parse(_endpoint)).timeout(
            const Duration(milliseconds: 1200),
          );

      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        final model = ParityModel.fromJson(data);
        await prefs.setString(_cacheKey, json.encode(model.toJson()));
        return model;
      }
    } catch (_) {
      // Network timeout: Load from offline cache
      final cachedJson = prefs.getString(_cacheKey);
      if (cachedJson != null) {
        return ParityModel.fromJson(json.decode(cachedJson));
      }
    }

    return null;
  }
}

Flutter Pricing Banner & Checkout Widget

// lib/widgets/pricing_card.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../services/parity_edge_service.dart';

class PricingCardWidget extends StatelessWidget {
  final ParityModel? parity;
  final double basePrice = 49.00;

  const PricingCardWidget({super.key, required this.parity});

  Future<void> _launchCheckout() async {
    String url = 'https://buy.stripe.com/your_checkout_id';
    if (parity != null && parity!.eligible && parity!.couponCode.isNotEmpty) {
      url = '$url?prefilled_promo_code=${parity!.couponCode}';
    }

    final uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    }
  }

  @override
  Widget build(BuildContext context) {
    final hasDiscount = parity != null && parity!.eligible && parity!.discountPercentage > 0;
    final finalPrice = hasDiscount
        ? basePrice * (1.0 - (parity!.discountPercentage / 100.0))
        : basePrice;

    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: const Color(0xFF0F172A),
        borderRadius: BorderRadius.circular(20),
        border: Border.all(color: const Color(0xFF1E293B)),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (hasDiscount)
            Container(
              margin: const EdgeInsets.bottom(16),
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              decoration: BoxDecoration(
                color: const Color(0xFF064E3B),
                borderRadius: BorderRadius.circular(10),
                border: Border.all(color: const Color(0xFF059669)),
              ),
              child: Text(
                '🎉 Special ${parity!.discountPercentage}% purchasing power discount for ${parity!.countryName}!',
                style: const TextStyle(color: Color(0xFF34D399), fontSize: 12, fontWeight: FontWeight.w600),
              ),
            ),
          Text(
            '\$${finalPrice.toStringAsFixed(2)} / year',
            style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white),
          ),
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: _launchCheckout,
            style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF10B981)),
            child: const Text('Subscribe with Local Discount'),
          ),
        ],
      ),
    );
  }
}

Flutter Multi-Platform Best Practices

Zero Permission Dialogs

ParityEdge determines country and PPP tier at the Anycast edge with zero location permissions required in your AndroidManifest.xml or Info.plist.

Anti-VPN Security Shield

Datacenter proxies and commercial VPN tunnels are flagged at the edge layer, protecting mobile checkouts from geographic arbitrage.