finance_sdk 1.0.2 copy "finance_sdk: ^1.0.2" to clipboard
finance_sdk: ^1.0.2 copied to clipboard

A Flutter plugin for Firebase-based dynamic API orchestration. Fetches API configurations from Firestore and Remote Config to handle HTTP requests dynamically.

example/lib/main.dart

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Note: Firebase is initialized internally by the plugin
  // No need to call Firebase.initializeApp() here
  
  runApp(const MyApp());
}

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

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

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  String _status = 'Initializing...';
  String _response = '';
  Map<String, String> _availableApiKeys = {};
  Map<String, String> _availableServices = {};
  final _financeSdkPlugin = FinanceSdk();
  final _testService = FinanceSdkTestService.instance;
  final TextEditingController _apiKeyController = TextEditingController();
  final TextEditingController _requestBodyController = TextEditingController();
  final TextEditingController _baseUrlController = TextEditingController();
  final TextEditingController _bearerTokenController = TextEditingController();
  bool _isRunningTests = false;
  bool _isJsonBody = true;

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

  @override
  void dispose() {
    _apiKeyController.dispose();
    _requestBodyController.dispose();
    _baseUrlController.dispose();
    _bearerTokenController.dispose();
    super.dispose();
  }

  Future<void> initPlatformState() async {
    String platformVersion;
    try {
      platformVersion =
          await _financeSdkPlugin.getPlatformVersion() ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  Future<void> _initializeSdk() async {
    try {
      setState(() {
        _status = 'Initializing Finance SDK...';
      });

      await _financeSdkPlugin.initialize();

      // Get available API keys and services
      final apiKeys = await _financeSdkPlugin.getAvailableApiKeys();
      final services = await _financeSdkPlugin.getAvailableServices();

      // Load current base URL from Remote Config or override
      final baseUrlSource = _financeSdkPlugin.getBaseUrlSource();
      
      setState(() {
        _status = 'SDK initialized successfully!';
        _availableApiKeys = apiKeys;
        _availableServices = services;
        // Show current base URL source
        if (baseUrlSource == 'Local Override') {
          _status = 'SDK initialized (using Local Override)';
        }
      });
    } catch (e) {
      setState(() {_status = 'Failed to initialize SDK: $e';});
    }
  }

  Future<void> _sendRequest() async {
    if (_apiKeyController.text.isEmpty || _requestBodyController.text.isEmpty) {
      setState(() {
        _response = 'Please enter API key and request body';
      });
      return;
    }

    try {
      setState(() {
        _status = 'Sending request...';
      });

      // Parse request body (supports JSON or key=value&key2=value2)
      Map<String, dynamic> requestBody;
      if (_isJsonBody) {
        try {
          final decoded = jsonDecode(_requestBodyController.text);
          requestBody = decoded is Map<String, dynamic>
              ? decoded
              : {'data': decoded};
        } catch (_) {
          requestBody = {'data': _requestBodyController.text};
        }
      } else {
        try {
          requestBody = Map<String, dynamic>.from(
            Uri.splitQueryString(_requestBodyController.text)
          );
        } catch (_) {
          requestBody = {'data': _requestBodyController.text};
        }
      }

      final response = await _financeSdkPlugin.sendRequest(
        apiKey: _apiKeyController.text,
        requestBody: requestBody,
      );

      setState(() {
        _status = 'Request completed';
        _response = response.success 
            ? 'Success: ${response.data}'
            : 'Error: ${response.error}';
      });
    } catch (e) {
      setState(() {
        _status = 'Request failed';
        _response = 'Error: $e';
      });
    }
  }

  Future<void> _refreshData() async {
    try {
      setState(() {
        _status = 'Refreshing data...';
      });

      await _financeSdkPlugin.refreshData();

      // Get updated API keys and services
      final apiKeys = await _financeSdkPlugin.getAvailableApiKeys();
      final services = await _financeSdkPlugin.getAvailableServices();

      setState(() {
        _status = 'Data refreshed successfully!';
        _availableApiKeys = apiKeys;
        _availableServices = services;
      });
    } catch (e) {
      setState(() {
        _status = 'Failed to refresh data: $e';
      });
    }
  }

  Future<void> _runTestGet() async {
    if (_isRunningTests) return;
    
    setState(() {
      _isRunningTests = true;
      _status = 'Running GET test...';
      _response = '';
    });

    try {
      final result = await _testService.testGetRequest();
      
      setState(() {
        _status = result.success ? 'GET test completed!' : 'GET test failed';
        _response = result.success
            ? '✅ SUCCESS\n\nStatus: ${result.statusCode}\n\nResponse:\n${_formatResponse(result.data)}'
            : '❌ FAILED\n\nError: ${result.error}';
        _isRunningTests = false;
      });
    } catch (e) {
      setState(() {
        _status = 'GET test error';
        _response = '❌ Exception: $e';
        _isRunningTests = false;
      });
    }
  }

  Future<void> _runTestPost() async {
    if (_isRunningTests) return;
    
    setState(() {
      _isRunningTests = true;
      _status = 'Running POST test...';
      _response = '';
    });

    try {
      final result = await _testService.testPostRequest();
      
      setState(() {
        _status = result.success ? 'POST test completed!' : 'POST test failed';
        _response = result.success
            ? '✅ SUCCESS\n\nStatus: ${result.statusCode}\n\nResponse:\n${_formatResponse(result.data)}'
            : '❌ FAILED\n\nError: ${result.error}';
        _isRunningTests = false;
      });
    } catch (e) {
      setState(() {
        _status = 'POST test error';
        _response = '❌ Exception: $e';
        _isRunningTests = false;
      });
    }
  }

  Future<void> _runAllTests() async {
    if (_isRunningTests) return;
    
    setState(() {
      _isRunningTests = true;
      _status = 'Running all tests...';
      _response = '';
    });

    try {
      // First verify configuration
      final isConfigured = await _testService.verifyConfiguration();
      
      if (!isConfigured) {
        setState(() {
          _status = 'Configuration check failed';
          _response = '⚠️ Please ensure TEST_GET_REQUEST and TEST_POST_REQUEST are configured in Firestore.';
          _isRunningTests = false;
        });
        return;
      }

      final results = await _testService.runAllTests();
      
      final summary = results['summary'] as Map<String, dynamic>;
      final testResults = results['results'] as Map<String, dynamic>;
      
      final buffer = StringBuffer();
      buffer.writeln('📊 TEST SUMMARY');
      buffer.writeln('════════════════════════════════════════');
      buffer.writeln('Total: ${summary['total']}');
      buffer.writeln('✅ Passed: ${summary['passed']}');
      buffer.writeln('❌ Failed: ${summary['failed']}');
      buffer.writeln('');
      buffer.writeln('════════════════════════════════════════');
      
      for (final entry in testResults.entries) {
        final testName = entry.key;
        final result = entry.value as Map<String, dynamic>;
        buffer.writeln('');
        buffer.writeln('$testName Test:');
        buffer.writeln('  Status: ${result['success'] == true ? '✅ PASSED' : '❌ FAILED'}');
        if (result['statusCode'] != null) {
          buffer.writeln('  HTTP Status: ${result['statusCode']}');
        }
        if (result['error'] != null) {
          buffer.writeln('  Error: ${result['error']}');
        }
        if (result['data'] != null) {
          buffer.writeln('  Response: ${_formatResponse(result['data'])}');
        }
      }
      
      setState(() {
        _status = 'All tests completed!';
        _response = buffer.toString();
        _isRunningTests = false;
      });
    } catch (e) {
      setState(() {
        _status = 'Test suite error';
        _response = '❌ Exception: $e';
        _isRunningTests = false;
      });
    }
  }

  String _formatResponse(dynamic data) {
    try {
      if (data is Map || data is List) {
        const encoder = JsonEncoder.withIndent('  ');
        return encoder.convert(data);
      }
      return data.toString();
    } catch (e) {
      return data.toString();
    }
  }

  void _setBaseUrlOverride() {
    final url = _baseUrlController.text.trim();
    if (url.isEmpty) {
      _financeSdkPlugin.clearBaseUrlOverride();
      setState(() {
        _status = 'Base URL override cleared (using Remote Config)';
      });
    } else {
      _financeSdkPlugin.setBaseUrlOverride(url);
      setState(() {
        _status = 'Base URL override set: $url';
      });
    }
  }

  void _setBearerToken() {
    final token = _bearerTokenController.text.trim();
    if (token.isEmpty) {
      _financeSdkPlugin.clearBearerToken();
      setState(() {
        _status = 'Bearer token cleared';
      });
    } else {
      _financeSdkPlugin.setBearerToken(token);
      setState(() {
        _status = 'Bearer token set successfully';
      });
    }
  }

  void _copyResponse() async {
    try {
      await Clipboard.setData(ClipboardData(text: _response));
      if (mounted) {
        setState(() {
          _status = 'Response copied to clipboard';
        });
      }
    } catch (_) {}
  }

  void _prettifyResponse() {
    try {
      final trimmed = _response.trim();
      String? headerless;
      final marker = 'Response:\n';
      final idx = trimmed.indexOf(marker);
      if (idx >= 0) {
        headerless = trimmed.substring(idx + marker.length);
      }

      String candidate = headerless ?? trimmed;
      dynamic parsed;
      try {
        parsed = jsonDecode(candidate);
      } catch (_) {
        // Try to find JSON braces within the text
        final start = candidate.indexOf('{');
        final startArr = candidate.indexOf('[');
        int s = -1;
        if (start >= 0 && startArr >= 0) {
          s = start < startArr ? start : startArr;
        } else {
          s = start >= 0 ? start : startArr;
        }
        if (s >= 0) {
          final sub = candidate.substring(s);
          parsed = jsonDecode(sub);
        }
      }

      if (parsed != null) {
        const encoder = JsonEncoder.withIndent('  ');
        final pretty = encoder.convert(parsed);
        if (headerless != null) {
          setState(() {
            _response = trimmed.substring(0, idx + marker.length) + pretty;
          });
        } else {
          setState(() {
            _response = pretty;
          });
        }
      }
    } catch (_) {}
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Finance SDK Example'),
          backgroundColor: Colors.blue,
          foregroundColor: Colors.white,
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Platform: $_platformVersion',
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 8),
                      Text('Status: $_status'),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              
              // Available API Keys
            /*  Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Available API Keys:',
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                      ),
                      const SizedBox(height: 8),
                      if (_availableApiKeys.isEmpty)
                        const Text('No API keys available')
                      else
                        ..._availableApiKeys.entries.map((entry) => 
                          Text('• ${entry.key}: ${entry.value}')),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              
              // Available Services
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Available Services:',
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                      ),
                      const SizedBox(height: 8),
                      if (_availableServices.isEmpty)
                        const Text('No services available')
                      else
                        ..._availableServices.entries.map((entry) => 
                          Text('• ${entry.key}: ${entry.value}')),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),*/
              
              // Base URL Configuration
            /*  Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          const Icon(Icons.settings, size: 20),
                          const SizedBox(width: 8),
                          const Text(
                            'Base URL Configuration:',
                            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      Text(
                        'Source: ${_financeSdkPlugin.getBaseUrlSource()}',
                        style: TextStyle(
                          color: _financeSdkPlugin.getBaseUrlSource() == 'Local Override'
                              ? Colors.orange
                              : Colors.grey[600],
                          fontSize: 12,
                        ),
                      ),
                      const SizedBox(height: 12),
                      TextField(
                        controller: _baseUrlController,
                        decoration: InputDecoration(
                          labelText: 'Base URL Override (Optional)',
                          hintText: 'https://httpbin.org',
                          filled: true,
                          fillColor: Colors.white,
                          contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Colors.grey.shade300),
                          ),
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
                          ),
                          prefixIcon: const Icon(Icons.link_rounded),
                          suffixIcon: (_baseUrlController.text.isNotEmpty)
                              ? IconButton(
                                  icon: const Icon(Icons.clear),
                                  onPressed: () {
                                    setState(() => _baseUrlController.clear());
                                  },
                                )
                              : null,
                          helperText: 'Leave empty to use Remote Config value',
                        ),
                        onChanged: (_) => setState(() {}),
                      ),
                      const SizedBox(height: 12),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton.icon(
                              onPressed: _setBaseUrlOverride,
                              icon: const Icon(Icons.save_rounded),
                              label: const Text('Set Override'),
                              style: ElevatedButton.styleFrom(
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: OutlinedButton.icon(
                              onPressed: () {
                                _baseUrlController.clear();
                                _financeSdkPlugin.clearBaseUrlOverride();
                                setState(() {
                                  _status = 'Base URL override cleared';
                                });
                              },
                              icon: const Icon(Icons.refresh_rounded),
                              label: const Text('Clear'),
                              style: OutlinedButton.styleFrom(
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              */
              
              // Bearer Token Configuration
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          const Icon(Icons.lock_outline, size: 20),
                          const SizedBox(width: 8),
                          const Text(
                            'Bearer Token Configuration:',
                            style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      Text(
                        'Status: ${_bearerTokenController.text.isNotEmpty ? 'Token Set' : 'No Token'}',
                        style: TextStyle(
                          color: _bearerTokenController.text.isNotEmpty
                              ? Colors.green
                              : Colors.grey[600],
                          fontSize: 12,
                        ),
                      ),
                      const SizedBox(height: 12),
                      TextField(
                        controller: _bearerTokenController,
                        obscureText: true,
                        decoration: InputDecoration(
                          labelText: 'Bearer Token (Optional)',
                          hintText: 'Enter your bearer token',
                          filled: true,
                          fillColor: Colors.white,
                          contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Colors.grey.shade300),
                          ),
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
                          ),
                          prefixIcon: const Icon(Icons.vpn_key),
                          suffixIcon: (_bearerTokenController.text.isNotEmpty)
                              ? IconButton(
                                  icon: const Icon(Icons.clear),
                                  onPressed: () {
                                    setState(() => _bearerTokenController.clear());
                                  },
                                )
                              : null,
                          helperText: 'Token will be sent as "Authorization: Bearer <token>" in all requests',
                        ),
                        onChanged: (_) => setState(() {}),
                      ),
                      const SizedBox(height: 12),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton.icon(
                              onPressed: _setBearerToken,
                              icon: const Icon(Icons.save_rounded),
                              label: const Text('Set Token'),
                              style: ElevatedButton.styleFrom(
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: OutlinedButton.icon(
                              onPressed: () {
                                _bearerTokenController.clear();
                                _financeSdkPlugin.clearBearerToken();
                                setState(() {
                                  _status = 'Bearer token cleared';
                                });
                              },
                              icon: const Icon(Icons.refresh_rounded),
                              label: const Text('Clear'),
                              style: OutlinedButton.styleFrom(
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              
              // Test Buttons
              /*Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        '🧪 Test Requests:',
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                      ),
                      const SizedBox(height: 16),
                      Row(
                        children: [
                          Expanded(
                            child: ElevatedButton.icon(
                              onPressed: _isRunningTests ? null : _runTestGet,
                              icon: const Icon(Icons.get_app),
                              label: const Text('Test GET'),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.green,
                                foregroundColor: Colors.white,
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                          const SizedBox(width: 8),
                          Expanded(
                            child: ElevatedButton.icon(
                              onPressed: _isRunningTests ? null : _runTestPost,
                              icon: const Icon(Icons.send),
                              label: const Text('Test POST'),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.blue,
                                foregroundColor: Colors.white,
                                padding: const EdgeInsets.symmetric(vertical: 12),
                              ),
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton.icon(
                          onPressed: _isRunningTests ? null : _runAllTests,
                          icon: const Icon(Icons.play_arrow),
                          label: const Text('Run All Tests'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.purple,
                            foregroundColor: Colors.white,
                            padding: const EdgeInsets.symmetric(vertical: 12),
                          ),
                        ),
                      ),
                      if (_isRunningTests) ...[
                        const SizedBox(height: 8),
                        const Center(
                          child: CircularProgressIndicator(),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),*/
              
              // Request Form
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Send API Request:',
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                      ),
                      const SizedBox(height: 16),
                      // API Key selector + manual entry
                      if (_availableApiKeys.isNotEmpty) ...[
                        DropdownButtonFormField<String>(
                          decoration: const InputDecoration(
                            labelText: 'Choose API Key',
                            border: OutlineInputBorder(),
                            prefixIcon: Icon(Icons.list_alt_rounded),
                          ),
                          isExpanded: true,
                          items: _availableApiKeys.values
                              .map((k) => DropdownMenuItem<String>(
                                    value: k,
                                    child: Text(k),
                                  ))
                              .toList(),
                          onChanged: (val) {
                            if (val != null) _apiKeyController.text = val;
                          },
                        ),
                        const SizedBox(height: 12),
                      ],
                      /*TextField(
                        controller: _apiKeyController,
                        decoration: InputDecoration(
                          labelText: 'API Key',
                          hintText: 'e.g., GET_USER_DETAIL',
                          filled: true,
                          fillColor: Colors.white,
                          contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Colors.grey.shade300),
                          ),
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
                          ),
                          prefixIcon: const Icon(Icons.vpn_key_rounded),
                          suffixIcon: (_apiKeyController.text.isNotEmpty)
                              ? IconButton(
                                  icon: const Icon(Icons.clear),
                                  onPressed: () {
                                    setState(() => _apiKeyController.clear());
                                  },
                                )
                              : null,
                        ),
                        onChanged: (_) => setState(() {}),
                      ),
                      const SizedBox(height: 16),*/
                      // Body type toggle
                      Row(
                        children: [
                          const Icon(Icons.description_outlined, size: 18),
                          const SizedBox(width: 8),
                          const Text('Body as JSON'),
                          const Spacer(),
                          Switch(
                            value: _isJsonBody,
                            onChanged: (v) => setState(() => _isJsonBody = v),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      TextField(
                        controller: _requestBodyController,
                        decoration: InputDecoration(
                          labelText: _isJsonBody
                              ? 'Request Body (JSON)'
                              : 'Request Body (key=value&key2=value2)',
                          hintText: _isJsonBody
                              ? '{"userId":"123","name":"John"}'
                              : 'userId=123&name=John',
                          filled: true,
                          fillColor: Colors.white,
                          contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Colors.grey.shade300),
                          ),
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12),
                            borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2),
                          ),
                        ),
                        minLines: 3,
                        maxLines: 8,
                      ),
                      const SizedBox(height: 16),
                      Row(children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _sendRequest,
                            icon: const Icon(Icons.send_rounded),
                            label: const Text('Send Request'),
                            style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 14)),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: OutlinedButton.icon(
                            onPressed: _refreshData,
                            icon: const Icon(Icons.refresh_rounded),
                            label: const Text('Refresh Data'),
                            style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 14)),
                          ),
                        ),
                      ]),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),
              
              // Response
              Card(
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Response:',
                        style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
                      ),
                      const SizedBox(height: 8),
                      Row(
                        children: [
                          ElevatedButton.icon(
                            onPressed: _copyResponse,
                            icon: const Icon(Icons.copy),
                            label: const Text('Copy'),
                          ),
                          const SizedBox(width: 8),
                          ElevatedButton.icon(
                            onPressed: _prettifyResponse,
                            icon: const Icon(Icons.format_align_left),
                            label: const Text('Pretty'),
                          ),
                        ],
                      ),
                      const SizedBox(height: 8),
                      Container(
                        width: double.infinity,
                        padding: const EdgeInsets.all(12),
                        decoration: BoxDecoration(
                          color: Colors.grey[100],
                          borderRadius: BorderRadius.circular(8),
                        ),
                        child: Text(
                          _response.isEmpty ? 'No response yet' : _response,
                          style: const TextStyle(fontFamily: 'monospace'),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
140
points
54
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for Firebase-based dynamic API orchestration. Fetches API configurations from Firestore and Remote Config to handle HTTP requests dynamically.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

cloud_firestore, firebase_core, firebase_performance, firebase_remote_config, flutter, http, plugin_platform_interface, shared_preferences

More

Packages that depend on finance_sdk

Packages that implement finance_sdk