currency_converter_pro 0.1.0 copy "currency_converter_pro: ^0.1.0" to clipboard
currency_converter_pro: ^0.1.0 copied to clipboard

Professional Currency and Crypto Converter for Flutter. Supports multi-providers, offline cache, historical rates, batch conversion, and real-time streams.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:currency_converter_pro/currency_converter_pro.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Currency Converter Pro',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.light,
          primary: Colors.deepPurple,
          secondary: Colors.amber,
        ),
        useMaterial3: true,
        cardTheme: CardThemeData(
          elevation: 4,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
          clipBehavior: Clip.antiAlias,
        ),
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.deepPurple,
          brightness: Brightness.dark,
          primary: Colors.deepPurpleAccent,
        ),
        useMaterial3: true,
      ),
      home: const MainDemoScreen(),
    );
  }
}

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

  @override
  State<MainDemoScreen> createState() => _MainDemoScreenState();
}

class _MainDemoScreenState extends State<MainDemoScreen> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        body: Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                Theme.of(context).colorScheme.primary.withOpacity(0.1),
                Theme.of(context).colorScheme.surface,
              ],
            ),
          ),
          child: Column(
            children: [
              const SizedBox(height: 40),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24.0),
                child: Row(
                  children: [
                    Container(
                      padding: const EdgeInsets.all(12),
                      decoration: BoxDecoration(
                        color: Theme.of(context).colorScheme.primary,
                        borderRadius: BorderRadius.circular(16),
                      ),
                      child: const Icon(Icons.currency_exchange, color: Colors.white, size: 32),
                    ),
                    const SizedBox(width: 16),
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'Currency Pro',
                          style: Theme.of(context).textTheme.headlineMedium?.copyWith(
                                fontWeight: FontWeight.bold,
                                color: Theme.of(context).colorScheme.primary,
                              ),
                        ),
                        const Text('Ultimate Conversion Suite', style: TextStyle(color: Colors.grey)),
                      ],
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 20),
              TabBar(
                labelColor: Theme.of(context).colorScheme.primary,
                unselectedLabelColor: Colors.grey,
                indicatorSize: TabBarIndicatorSize.label,
                tabs: const [
                  Tab(icon: Icon(Icons.swap_vert), text: 'Convert'),
                  Tab(icon: Icon(Icons.insights), text: 'Live'),
                  Tab(icon: Icon(Icons.auto_awesome), text: 'Pro'),
                ],
              ),
              const Expanded(
                child: TabBarView(
                  children: [
                    CurrencyConverterTab(),
                    LiveRatesTab(),
                    AdvancedFeaturesTab(),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<CurrencyConverterTab> createState() => _CurrencyConverterTabState();
}

class _CurrencyConverterTabState extends State<CurrencyConverterTab> {
  final TextEditingController _amountController = TextEditingController(text: '100');
  String _from = 'USD';
  String _to = 'INR';
  ConversionResult? _result;
  bool _isLoading = false;

  final _converter = CurrencyConverterPro(cacheDuration: const Duration(hours: 1));

  Future<void> _convert() async {
    setState(() => _isLoading = true);
    await Future.delayed(const Duration(milliseconds: 500)); // Smooth feel
    try {
      final res = await _converter.convertCurrency(
        amount: double.tryParse(_amountController.text) ?? 0,
        fromCurrency: _from,
        toCurrency: _to,
      );
      if (mounted) setState(() => _result = res);
    } catch (e) {
      if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
    } finally {
      if (mounted) setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        children: [
          Card(
            child: Padding(
              padding: const EdgeInsets.all(20.0),
              child: Column(
                children: [
                  TextField(
                    controller: _amountController,
                    keyboardType: TextInputType.number,
                    style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                    textAlign: TextAlign.center,
                    decoration: InputDecoration(
                      labelText: 'Amount to convert',
                      hintText: '0.00',
                      prefixIcon: const Icon(Icons.account_balance_wallet_outlined),
                      border: OutlineInputBorder(borderRadius: BorderRadius.circular(16)),
                      filled: true,
                      fillColor: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
                    ),
                  ),
                  const SizedBox(height: 24),
                  Row(
                    children: [
                      _buildCurrencyButton(context, _from, true),
                      Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 12),
                        child: CircleAvatar(
                          backgroundColor: Theme.of(context).colorScheme.primaryContainer,
                          child: IconButton(
                            icon: const Icon(Icons.swap_horiz),
                            onPressed: () {
                              setState(() {
                                final temp = _from;
                                _from = _to;
                                _to = temp;
                              });
                            },
                          ),
                        ),
                      ),
                      _buildCurrencyButton(context, _to, false),
                    ],
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 24),
          SizedBox(
            width: double.infinity,
            height: 60,
            child: FilledButton.icon(
              onPressed: _isLoading ? null : _convert,
              icon: _isLoading 
                ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white))
                : const Icon(Icons.bolt),
              label: const Text('Convert Now', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              style: FilledButton.styleFrom(
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
              ),
            ),
          ),
          if (_result != null) ...[
            const SizedBox(height: 32),
            _buildResultCard(context),
          ]
        ],
      ),
    );
  }

  Widget _buildCurrencyButton(BuildContext context, String code, bool isFrom) {
    final currency = CurrencyData.allCurrencies.firstWhere((c) => c.code == code, 
      orElse: () => Currency(code: code, name: '', symbol: '', flag: '🏳️'));
    
    return Expanded(
      child: InkWell(
        onTap: () => CurrencyPickerWidget.show(
          context: context,
          favorite: const ['USD', 'EUR', 'INR', 'GBP'],
          onSelect: (c) => setState(() {
            if (isFrom) _from = c.code; else _to = c.code;
          }),
        ),
        child: Container(
          padding: const EdgeInsets.symmetric(vertical: 16),
          decoration: BoxDecoration(
            color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
            borderRadius: BorderRadius.circular(16),
            border: Border.all(color: Theme.of(context).colorScheme.outline.withOpacity(0.2)),
          ),
          child: Column(
            children: [
              Text(currency.flag, style: const TextStyle(fontSize: 32)),
              const SizedBox(height: 4),
              Text(code, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildResultCard(BuildContext context) {
    return TweenAnimationBuilder<double>(
      tween: Tween(begin: 0, end: 1),
      duration: const Duration(milliseconds: 400),
      builder: (context, value, child) {
        return Opacity(
          opacity: value,
          child: Transform.translate(
            offset: Offset(0, 20 * (1 - value)),
            child: child,
          ),
        );
      },
      child: Card(
        color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.3),
        child: Padding(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            children: [
              Text(
                _result!.formattedAmount,
                textAlign: TextAlign.center,
                style: Theme.of(context).textTheme.displayMedium?.copyWith(
                  fontWeight: FontWeight.bold,
                  color: Theme.of(context).colorScheme.primary,
                ),
              ),
              const SizedBox(height: 16),
              const Divider(),
              const SizedBox(height: 16),
              _ResultDetail(Icons.analytics, 'Rate', '1 $_from = ${_result!.rate} $_to'),
              _ResultDetail(Icons.source, 'Provider', _result!.provider),
              _ResultDetail(Icons.history, 'Source', _result!.isCached ? 'Local Cache' : 'Real-time API'),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<LiveRatesTab> createState() => _LiveRatesTabState();
}

class _LiveRatesTabState extends State<LiveRatesTab> {
  final _converter = CurrencyConverterPro();
  String _base = 'USD';
  final List<String> _targets = ['INR', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD'];
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.all(16.0),
          child: InkWell(
            onTap: () => CurrencyPickerWidget.show(
              context: context,
              onSelect: (c) => setState(() => _base = c.code),
            ),
            child: Card(
              color: Theme.of(context).colorScheme.secondaryContainer,
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Row(
                  children: [
                    const Icon(Icons.currency_bitcoin),
                    const SizedBox(width: 16),
                    Text('Base: $_base', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    const Spacer(),
                    const Text('Change', style: TextStyle(color: Colors.blue)),
                  ],
                ),
              ),
            ),
          ),
        ),
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            itemCount: _targets.length,
            itemBuilder: (context, index) {
              final target = _targets[index];
              return StreamBuilder<double>(
                stream: _converter.rateStream(
                  fromCurrency: _base,
                  toCurrency: target,
                  interval: const Duration(seconds: 10),
                ),
                builder: (context, snapshot) {
                  return Card(
                    child: ListTile(
                      leading: CircleAvatar(
                        backgroundColor: Colors.green.withOpacity(0.1),
                        child: const Icon(Icons.trending_up, color: Colors.green),
                      ),
                      title: Text('$_base to $target', style: const TextStyle(fontWeight: FontWeight.bold)),
                      subtitle: const Text('Auto-updates every 10s'),
                      trailing: snapshot.hasData 
                        ? Text(
                            snapshot.data!.toStringAsFixed(4),
                            style: TextStyle(
                              color: Theme.of(context).colorScheme.primary,
                              fontWeight: FontWeight.bold,
                              fontSize: 18,
                            ),
                          )
                        : const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)),
                    ),
                  );
                },
              );
            },
          ),
        ),
      ],
    );
  }
}

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

  @override
  State<AdvancedFeaturesTab> createState() => _AdvancedFeaturesTabState();
}

class _AdvancedFeaturesTabState extends State<AdvancedFeaturesTab> {
  final _converter = CurrencyConverterPro(cacheDuration: const Duration(minutes: 30));
  Map<String, ConversionResult>? _batchResults;
  DateTime _selectedDate = DateTime.now().subtract(const Duration(days: 30));
  ConversionResult? _historicalResult;
  bool _isBatchLoading = false;
  bool _isHistoricalLoading = false;

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(24),
      children: [
        _buildFeatureCard(
          context,
          'Batch Conversion',
          'Convert one currency to many in a single efficient call.',
          Icons.layers,
          Colors.orange,
          Column(
            children: [
              SizedBox(
                width: double.infinity,
                child: OutlinedButton(
                  onPressed: _isBatchLoading ? null : () async {
                    setState(() => _isBatchLoading = true);
                    try {
                      final res = await _converter.convertMany(
                        amount: 100,
                        fromCurrency: 'USD',
                        toCurrencies: const ['EUR', 'GBP', 'INR', 'JPY'],
                      );
                      if (mounted) setState(() => _batchResults = res);
                    } finally {
                      if (mounted) setState(() => _isBatchLoading = false);
                    }
                  },
                  child: _isBatchLoading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Text('Run Batch (100 USD)'),
                ),
              ),
              if (_batchResults != null) ...[
                const SizedBox(height: 12),
                Wrap(
                  spacing: 8,
                  children: _batchResults!.entries.map((e) => Chip(
                    avatar: CircleAvatar(child: Text(e.key[0])),
                    label: Text('${e.key}: ${e.value.formattedAmount}'),
                    backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
                  )).toList(),
                ),
              ]
            ],
          ),
        ),
        const SizedBox(height: 20),
        _buildFeatureCard(
          context,
          'Historical Analysis',
          'Access market rates from any date in history.',
          Icons.history_toggle_off,
          Colors.blue,
          Column(
            children: [
              ListTile(
                contentPadding: EdgeInsets.zero,
                title: Text('${_selectedDate.year}-${_selectedDate.month}-${_selectedDate.day}'),
                trailing: const Icon(Icons.calendar_month),
                onTap: () async {
                  final date = await showDatePicker(
                    context: context,
                    initialDate: _selectedDate,
                    firstDate: DateTime(2020),
                    lastDate: DateTime.now(),
                  );
                  if (date != null && mounted) setState(() => _selectedDate = date);
                },
              ),
              SizedBox(
                width: double.infinity,
                child: FilledButton.tonal(
                  onPressed: _isHistoricalLoading ? null : () async {
                    setState(() => _isHistoricalLoading = true);
                    try {
                      final res = await _converter.convertCurrency(amount: 1, fromCurrency: 'USD', toCurrency: 'EUR', date: _selectedDate);
                      if (mounted) setState(() => _historicalResult = res);
                    } finally {
                      if (mounted) setState(() => _isHistoricalLoading = false);
                    }
                  },
                  child: _isHistoricalLoading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Text('Fetch Historical Rate'),
                ),
              ),
              if (_historicalResult != null) ...[
                const SizedBox(height: 12),
                Text('Rate was: ${_historicalResult!.rate} EUR', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
              ]
            ],
          ),
        ),
        const SizedBox(height: 20),
        _buildFeatureCard(
          context,
          'Performance & Fallback',
          'Manage local cache and test provider redundancy.',
          Icons.speed,
          Colors.green,
          Row(
            children: [
              Expanded(
                child: ActionButton(
                  icon: Icons.refresh,
                  label: 'Force Refresh',
                  onTap: () {
                    _converter.forceRefresh();
                    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Next request will bypass cache')));
                  },
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: ActionButton(
                  icon: Icons.delete_outline,
                  label: 'Clear Cache',
                  onTap: () async {
                    await _converter.clearCache();
                    if (mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Local cache cleared')));
                  },
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildFeatureCard(BuildContext context, String title, String sub, IconData icon, Color color, Widget content) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(icon, color: color),
                const SizedBox(width: 12),
                Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              ],
            ),
            const SizedBox(height: 8),
            Text(sub, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
            const SizedBox(height: 16),
            content,
          ],
        ),
      ),
    );
  }
}

class _ResultDetail extends StatelessWidget {
  final IconData icon;
  final String label;
  final String value;
  const _ResultDetail(this.icon, this.label, this.value);
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),
      child: Row(
        children: [
          Icon(icon, size: 20, color: Theme.of(context).colorScheme.primary.withOpacity(0.7)),
          const SizedBox(width: 12),
          Text(label, style: const TextStyle(color: Colors.grey)),
          const Spacer(),
          Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
        ],
      ),
    );
  }
}

class ActionButton extends StatelessWidget {
  final IconData icon;
  final String label;
  final VoidCallback onTap;
  const ActionButton({super.key, required this.icon, required this.label, required this.onTap});
  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(12),
      child: Container(
        padding: const EdgeInsets.all(12),
        decoration: BoxDecoration(
          border: Border.all(color: Theme.of(context).colorScheme.outline.withOpacity(0.2)),
          borderRadius: BorderRadius.circular(12),
        ),
        child: Column(
          children: [
            Icon(icon, size: 24),
            const SizedBox(height: 4),
            Text(label, style: const TextStyle(fontSize: 11)),
          ],
        ),
      ),
    );
  }
}
10
likes
120
points
294
downloads

Documentation

API reference

Publisher

verified publishershirsh.dev

Weekly Downloads

Professional Currency and Crypto Converter for Flutter. Supports multi-providers, offline cache, historical rates, batch conversion, and real-time streams.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, http, intl, plugin_platform_interface, shared_preferences

More

Packages that depend on currency_converter_pro

Packages that implement currency_converter_pro