smat_pay_payment_plugin 1.0.8 copy "smat_pay_payment_plugin: ^1.0.8" to clipboard
smat_pay_payment_plugin: ^1.0.8 copied to clipboard

A payment plugin form for Smatpay users.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:smat_pay_payment_plugin/common/api_config.dart';
import 'package:smat_pay_payment_plugin/views/payment_form.dart';

const _brandIndigo = Color(0xFF2F1991);
const _brandPurple = Color(0xFF8141D5);
const _brandViolet = Color(0xFF5B35E5);
const _pageBackground = Color(0xFFF5F0FF);
const _cardBorder = Color(0xFFEAD7FF);

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SmatPay Plugin Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: _brandIndigo),
        useMaterial3: true,
      ),
      home: const _PaymentLauncher(),
    );
  }
}

class _PaymentLauncher extends StatefulWidget {
  const _PaymentLauncher();

  @override
  State<_PaymentLauncher> createState() => _PaymentLauncherState();
}

class _PaymentLauncherState extends State<_PaymentLauncher> {
  static const _sandboxBaseUrl = 'https://dev.smatpay.africa:8443';
  static const _liveBaseUrl = 'https://live.smatpay.africa:8443';

  final _formKey = GlobalKey<FormState>();
  late final TextEditingController _paymentCodeController;
  late final TextEditingController _baseUrlController;
  EndpointMode _endpointMode = EndpointMode.test;

  @override
  void initState() {
    super.initState();
    _paymentCodeController = TextEditingController(
      text: const String.fromEnvironment('SMATPAY_PAYMENT_CODE'),
    );
    _baseUrlController = TextEditingController(
      text: const String.fromEnvironment(
        'SMATPAY_BASE_URL',
        defaultValue: _sandboxBaseUrl,
      ),
    );
    _endpointMode = const bool.fromEnvironment('SMATPAY_USE_LIVE')
        ? EndpointMode.live
        : EndpointMode.test;
  }

  @override
  void dispose() {
    _paymentCodeController.dispose();
    _baseUrlController.dispose();
    super.dispose();
  }

  void _openPaymentForm() {
    if (!(_formKey.currentState?.validate() ?? false)) {
      return;
    }

    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (context) => PaymentForm(
          paymentCode: _paymentCodeController.text.trim(),
          baseUrl: _baseUrlController.text.trim(),
          endpointMode: _endpointMode,
        ),
      ),
    );
  }

  void _updateEndpointMode(EndpointMode? endpointMode) {
    if (endpointMode == null || endpointMode == _endpointMode) {
      return;
    }

    final currentBaseUrl = _baseUrlController.text.trim();
    final usesDefaultBaseUrl =
        currentBaseUrl == _sandboxBaseUrl || currentBaseUrl == _liveBaseUrl;

    setState(() {
      _endpointMode = endpointMode;
      if (usesDefaultBaseUrl) {
        _baseUrlController.text =
            endpointMode == EndpointMode.test ? _sandboxBaseUrl : _liveBaseUrl;
      }
    });
  }

  String? _validateRequired(String? value, String name) {
    if (value == null || value.trim().isEmpty) {
      return 'Enter a $name.';
    }

    return null;
  }

  String? _validateBaseUrl(String? value) {
    final baseUrl = value?.trim() ?? '';
    final uri = Uri.tryParse(baseUrl);
    if (uri == null || !uri.hasScheme || !uri.hasAuthority) {
      return 'Enter a valid URL, including https://.';
    }

    return null;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: _pageBackground,
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(24),
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 520),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const SizedBox(height: 24),
                  const Text(
                    'SmatPay',
                    style: TextStyle(
                      color: _brandIndigo,
                      fontSize: 30,
                      fontWeight: FontWeight.w800,
                      letterSpacing: -0.8,
                    ),
                  ),
                  const SizedBox(height: 10),
                  const Text(
                    'Payment Plugin Example',
                    style: TextStyle(
                      color: Color(0xFF1A133D),
                      fontSize: 24,
                      fontWeight: FontWeight.w700,
                    ),
                  ),
                  const SizedBox(height: 8),
                  const Text(
                    'Configure a test payment request, then open the plugin form.',
                    style: TextStyle(color: Color(0xFF4B5563), height: 1.45),
                  ),
                  const SizedBox(height: 28),
                  Card(
                    margin: EdgeInsets.zero,
                    elevation: 0,
                    color: Colors.white,
                    surfaceTintColor: Colors.white,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(24),
                      side: const BorderSide(color: _cardBorder),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.all(20),
                      child: Form(
                        key: _formKey,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: [
                            const Text(
                              'Connection settings',
                              style: TextStyle(
                                color: Color(0xFF1A133D),
                                fontSize: 18,
                                fontWeight: FontWeight.w700,
                              ),
                            ),
                            const SizedBox(height: 20),
                            DropdownButtonFormField<EndpointMode>(
                              initialValue: _endpointMode,
                              decoration: const InputDecoration(
                                labelText: 'Environment',
                                border: OutlineInputBorder(),
                              ),
                              items: const [
                                DropdownMenuItem(
                                  value: EndpointMode.test,
                                  child: Text('Sandbox'),
                                ),
                                DropdownMenuItem(
                                  value: EndpointMode.live,
                                  child: Text('Production'),
                                ),
                              ],
                              onChanged: _updateEndpointMode,
                            ),
                            const SizedBox(height: 16),
                            TextFormField(
                              controller: _baseUrlController,
                              keyboardType: TextInputType.url,
                              decoration: const InputDecoration(
                                labelText: 'Base URL',
                                hintText: _sandboxBaseUrl,
                                border: OutlineInputBorder(),
                              ),
                              validator: _validateBaseUrl,
                            ),
                            const SizedBox(height: 16),
                            TextFormField(
                              controller: _paymentCodeController,
                              decoration: const InputDecoration(
                                labelText: 'Payment code',
                                hintText: 'Paste a test payment code',
                                border: OutlineInputBorder(),
                              ),
                              validator: (value) =>
                                  _validateRequired(value, 'payment code'),
                            ),
                            const SizedBox(height: 24),
                            DecoratedBox(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(28),
                                gradient: const LinearGradient(
                                  colors: [_brandPurple, _brandViolet],
                                ),
                              ),
                              child: ElevatedButton.icon(
                                onPressed: _openPaymentForm,
                                icon: const Icon(Icons.open_in_new_rounded),
                                label: const Text('Open payment form'),
                                style: ElevatedButton.styleFrom(
                                  backgroundColor: Colors.transparent,
                                  foregroundColor: Colors.white,
                                  shadowColor: Colors.transparent,
                                  padding: const EdgeInsets.symmetric(
                                    horizontal: 20,
                                    vertical: 16,
                                  ),
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(28),
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 18),
                  const Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Icon(
                        Icons.info_outline_rounded,
                        size: 18,
                        color: _brandIndigo,
                      ),
                      SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          'Use a sandbox payment code while testing. The base URL can also be supplied with --dart-define values.',
                          style: TextStyle(
                            color: Color(0xFF4B5563),
                            fontSize: 12,
                            height: 1.4,
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}