Question #227HardGeneralCloud/Backend

Create a currency converter app with the provided API ? (Interview Question)

#api

Answer

Overview

This is a common Flutter interview question — implement a currency converter app using an exchange rate API. The key is a clean UI with API integration, state management, and error handling.


Prerequisites

yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.0

Complete Currency Converter Implementation

dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() => runApp(MaterialApp(home: CurrencyConverterScreen()));

class CurrencyConverterScreen extends StatefulWidget {
  
  _CurrencyConverterScreenState createState() => _CurrencyConverterScreenState();
}

class _CurrencyConverterScreenState extends State<CurrencyConverterScreen> {
  final _amountController = TextEditingController();
  String _fromCurrency = 'USD';
  String _toCurrency = 'INR';
  double? _result;
  bool _isLoading = false;
  String? _error;

  final List<String> _currencies = ['USD', 'EUR', 'GBP', 'INR', 'JPY', 'AUD', 'CAD'];

  Future<void> _convert() async {
    final amount = double.tryParse(_amountController.text);
    if (amount == null) {
      setState(() => _error = 'Please enter a valid amount');
      return;
    }

    setState(() { _isLoading = true; _error = null; });

    try {
      // Free API: exchangerate-api.com (replace with your API key)
      final url = 'https://v6.exchangerate-api.com/v6/YOUR_API_KEY/pair/$_fromCurrency/$_toCurrency/$amount
      final response = await http.get(Uri.parse(url));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        setState(() => _result = data['conversion_result']?.toDouble());
      } else {
        setState(() => _error = 'API error: ${response.statusCode}');
      }
    } catch (e) {
      setState(() => _error = 'Network error. Check your connection.');
    } finally {
      setState(() => _isLoading = false);
    }
  }

  
  void dispose() {
    _amountController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Currency Converter'), centerTitle: true),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Amount input
            TextField(
              controller: _amountController,
              keyboardType: TextInputType.numberWithOptions(decimal: true),
              decoration: InputDecoration(
                labelText: 'Amount',
                prefixIcon: Icon(Icons.attach_money),
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
            SizedBox(height: 16),

            // Currency selectors
            Row(
              children: [
                Expanded(
                  child: DropdownButtonFormField<String>(
                    value: _fromCurrency,
                    decoration: InputDecoration(
                      labelText: 'From',
                      border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
                    ),
                    items: _currencies.map((c) => DropdownMenuItem(value: c, child: Text(c))).toList(),
                    onChanged: (v) => setState(() => _fromCurrency = v!),
                  ),
                ),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 8),
                  child: IconButton(
                    icon: Icon(Icons.swap_horiz, size: 32),
                    onPressed: () => setState(() {
                      final temp = _fromCurrency;
                      _fromCurrency = _toCurrency;
                      _toCurrency = temp;
                    }),
                  ),
                ),
                Expanded(
                  child: DropdownButtonFormField<String>(
                    value: _toCurrency,
                    decoration: InputDecoration(
                      labelText: 'To',
                      border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
                    ),
                    items: _currencies.map((c) => DropdownMenuItem(value: c, child: Text(c))).toList(),
                    onChanged: (v) => setState(() => _toCurrency = v!),
                  ),
                ),
              ],
            ),
            SizedBox(height: 24),

            // Convert button
            ElevatedButton(
              onPressed: _isLoading ? null : _convert,
              style: ElevatedButton.styleFrom(
                padding: EdgeInsets.symmetric(vertical: 16),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
              child: _isLoading
                ? CircularProgressIndicator(color: Colors.white)
                : Text('Convert', style: TextStyle(fontSize: 18)),
            ),
            SizedBox(height: 24),

            // Result / Error
            if (_error != null)
              Container(
                padding: EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.red.shade50,
                  borderRadius: BorderRadius.circular(12),
                  border: Border.all(color: Colors.red.shade200),
                ),
                child: Text(_error!, style: TextStyle(color: Colors.red)),
              ),
            if (_result != null && _error == null)
              Container(
                padding: EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.green.shade50,
                  borderRadius: BorderRadius.circular(12),
                  border: Border.all(color: Colors.green.shade200),
                ),
                child: Column(
                  children: [
                    Text('Converted Amount', style: TextStyle(color: Colors.grey)),
                    SizedBox(height: 8),
                    Text(
                      '${_result!.toStringAsFixed(2)} $_toCurrency',
                      style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.green.shade700),
                    ),
                  ],
                ),
              ),
          ],
        ),
      ),
    );
  }
}

Free APIs for Currency Conversion

APIFree TierURL
ExchangeRate-API1500 req/monthexchangerate-api.com
Open Exchange Rates1000 req/monthopenexchangerates.org
Fixer.io100 req/monthfixer.io
frankfurter.appFree, no keyfrankfurter.app
dart
// Frankfurter (no API key needed!)
final url = 'https://api.frankfurter.app/latest?from=$_fromCurrency&to=$_toCurrency&amount=$amount
final data = jsonDecode(response.body);
final result = data['rates'][_toCurrency];

Interview Tips: Show currency swap button, handle loading/error states, use

text
TextEditingController
properly with
text
dispose()
, and demonstrate API error handling.