savers_app_sdk 1.0.7 copy "savers_app_sdk: ^1.0.7" to clipboard
savers_app_sdk: ^1.0.7 copied to clipboard

Flutter SDK for the Savers hosted app: WebView bridge, native maps/dialer/browser, device/session helpers, and encrypted URL generation.

example/lib/main.dart

import 'dart:convert';
import 'dart:io';

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

import 'screens/api_hub_screen.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Savers App Flutter SDKSample',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF2563EB)),
        scaffoldBackgroundColor: const Color(0xFFF8FAFC),
      ),
      home: const HomeScreen(),
    );
  }
}

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  String deviceId = '-';
  String locationResult = '-';
  String apiKey = '-';
  String encryptionKey = '-';
  String pRefCode = '-';
  String generatedUrl = '-';

  @override
  void initState() {
    super.initState();
    if (Platform.environment.containsKey('FLUTTER_TEST')) {
      return;
    }
    _boot();
  }

  Future<void> _boot() async {
    try {
      await _initializeSdk();
    } catch (_) {}
    await _fetchInfo();
  }

  Future<void> _initializeSdk() async {
    try {
      await SaversAppSDK.initialize(
        apiKey: 'API_KEY',
        encryptionKey: 'BASE64_ENCRYPTION_KEY',
        pRefCode: 'PROGRAM_REFERRAL_CODE',
        environment: SaversSdkHostedEnvironment.sandbox,
      );
    } catch (e) {
      LogsManager.log(LogType.general, 'SDK init failed: $e');
    }

    try {
      final id = await getDeviceId();
      await SaversAppSDK.registerDevice(
        deviceId: id,
        location: (lat: 37.77495, lng: -122.41945),
      );
    } catch (e) {
      LogsManager.log(LogType.general, 'Device registration failed: $e');
    }

    try {
      await SaversAppSDK.initializeUserSession(
        userId: 'USER_ID',
        firstname: 'FIRST_NAME',
        lastname: 'LAST_NAME',
        email: 'EMAIL',
        phone: 'PHONE',
        city: 'CITY',
        zipcode: 'ZIPCODE',
        dob: 'DOB',
        pv: 'PV',
        ev: 'PV',
      );
    } catch (e) {
      LogsManager.log(LogType.general, 'User session init failed: $e');
    }
    
  }

  Future<void> _fetchInfo() async {
    try {
      final id = await getDeviceId();
      setState(() => deviceId = id);
    } catch (e) {
      setState(() => deviceId = e.toString());
    }

    try {
      final loc = await getLocationCoordinates();
      setState(() {
        locationResult = loc != null
            ? '${loc['lat']}, ${loc['lng']}'
            : 'Location unavailable';
      });
    } catch (e) {
      setState(() => locationResult = e.toString());
    }

    try {
      final k = await getApiKey();
      setState(() => apiKey = k);
    } catch (e) {
      setState(() => apiKey = e.toString());
    }

    try {
      final ek = await getEncryptionKey();
      setState(() => encryptionKey = ek);
    } catch (e) {
      setState(() => encryptionKey = e.toString());
    }

    try {
      final pr = await getPRefCode();
      setState(() => pRefCode = pr);
    } catch (e) {
      setState(() => pRefCode = e.toString());
    }

    try {
      final url = await generateUrl();
      LogsManager.log(LogType.general, 'generateUrl result: $url');
      setState(() => generatedUrl = url);
    } catch (e) {
      LogsManager.log(LogType.general, 'generateUrl failed: $e');
      setState(() => generatedUrl = 'generateUrl failed: $e');
    }
  }

  Future<void> _handleAction(
    String action,
    Map<String, dynamic> payload,
  ) async {
    handleWebMessage(
      jsonEncode({'action': action, 'payload': payload}),
      postBack: (_) {},
    );
  }

  bool get _hasGeneratedUrl {
    final uri = Uri.tryParse(generatedUrl);
    return uri != null &&
        (uri.scheme == 'http' || uri.scheme == 'https') &&
        uri.host.isNotEmpty;
  }

  void _openHostedApp(String url) {
    Navigator.of(context).push(
      MaterialPageRoute(builder: (_) => HostedAppScreen(saversAppUrl: url)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            const _Header(),
            Expanded(
              child: Padding(
                padding: const EdgeInsets.symmetric(
                  horizontal: 16,
                  vertical: 24,
                ),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    _Card(
                      title: 'Generated URL',
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          _ValueBox(text: generatedUrl, mono: true),
                          Padding(
                            padding: const EdgeInsets.only(top: 12),
                            child: SizedBox(
                              width: double.infinity,
                              child: _PrimaryButton(
                                label: 'Open in WebView',
                                color: const Color(0xFF14B8A6),
                                onPressed: _hasGeneratedUrl
                                    ? () => _openHostedApp(generatedUrl)
                                    : null,
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                    _Card(
                      title: 'API Testing',
                      child: _PrimaryButton(
                        label: 'Open API Tests',
                        color: const Color(0xFF10B981),
                        onPressed: _hasGeneratedUrl
                            ? () {
                                Navigator.of(context).push(
                                  MaterialPageRoute(
                                    builder: (_) => const ApiHubScreen(),
                                  ),
                                );
                              }
                            : null,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class HostedAppScreen extends StatelessWidget {
  const HostedAppScreen({super.key, required this.saversAppUrl});

  final String saversAppUrl;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: HostedAppComponent(
        saversAppUrl: saversAppUrl,
        onSaversSdkMessage: (raw, postBack) {
          try {
            final msg = jsonDecode(raw);
            if (msg is Map && msg['action'] == 'USER_SESSION_ID') {
              final payload = msg['payload'];
              LogsManager.log(
                LogType.general,
                '[Demo] USER_SESSION_ID from WebView',
                [
                  'payloadKeys=${payload is Map ? payload.keys.toList() : payload}',
                ],
              );
            }
          } catch (_) {}
          handleWebMessage(raw, postBack: postBack);
        },
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
          child: Column(
            children: [
              const _Card(
                title: 'Close Current Screen',
                child: _ValueBox(
                  text:
                      'Press the button to close this screen via Navigator.maybePop().',
                ),
              ),
              _MiniActionBar(onClose: () => Navigator.of(context).maybePop()),
              Padding(
                padding: const EdgeInsets.only(top: 12),
                child: _PrimaryButton(
                  label: 'Push Another Screen',
                  color: const Color(0xFF14B8A6),
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const CloseTestScreen(),
                      ),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _Header extends StatelessWidget {
  const _Header();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
      color: const Color(0xFF2563EB),
      child: const Row(
        children: [
          Text(
            'Savers App Flutter SDK Sample',
            style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.w700,
              color: Colors.white,
            ),
          ),
          SizedBox(width: 12),
          Text(
            'Native features showcase',
            style: TextStyle(fontSize: 12, color: Color(0xFFE5E7EB)),
          ),
        ],
      ),
    );
  }
}

class _Card extends StatelessWidget {
  final String title;
  final Widget child;

  const _Card({required this.title, required this.child});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      margin: const EdgeInsets.symmetric(vertical: 8),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: const [
          BoxShadow(
            color: Color(0x1A000000),
            blurRadius: 8,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: const TextStyle(fontSize: 12, color: Color(0xFF6B7280)),
          ),
          const SizedBox(height: 8),
          child,
        ],
      ),
    );
  }
}

class _InfoCard extends StatelessWidget {
  final String title;
  final String value;
  final bool mono;

  const _InfoCard({
    required this.title,
    required this.value,
    this.mono = false,
  });

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: title,
      child: _ValueBox(text: value, mono: mono),
    );
  }
}

class _ValueBox extends StatelessWidget {
  final String text;
  final bool mono;

  const _ValueBox({required this.text, this.mono = false});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      decoration: BoxDecoration(
        color: const Color(0xFFF3F4F6),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Text(
        text,
        style: TextStyle(
          fontSize: mono ? 14 : 16,
          color: const Color(0xFF111827),
        ),
      ),
    );
  }
}

class _PrimaryButton extends StatelessWidget {
  final String label;
  final Color color;
  final VoidCallback? onPressed;

  const _PrimaryButton({
    required this.label,
    required this.color,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 40,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          backgroundColor: color,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
        ),
        onPressed: onPressed,
        child: Text(
          label,
          style: const TextStyle(
            color: Colors.white,
            fontSize: 14,
            fontWeight: FontWeight.w600,
          ),
        ),
      ),
    );
  }
}

class _ActionBar extends StatelessWidget {
  final Future<void> Function(String action, Map<String, dynamic> payload)
  onAction;

  const _ActionBar({required this.onAction});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 64,
      margin: const EdgeInsets.fromLTRB(16, 0, 16, 12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0xFFE5E7EB)),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 8),
        child: SizedBox(
          height: _actionButtonHeight,
          child: ListView(
            scrollDirection: Axis.horizontal,
            padding: const EdgeInsets.symmetric(horizontal: 16),
            children: [
              _ActionButton(
                label: 'Open Map',
                onPressed: () => onAction('OPEN_MAP', {
                  'lat': 37.7749,
                  'lng': -122.4194,
                  'label': 'SaverAppsPoint',
                }),
              ),
              _ActionButton(
                label: 'Open Dialpad',
                onPressed: () =>
                    onAction('SHOW_DIAL_PAD', {'number': '+1234567890'}),
              ),
              _ActionButton(
                label: 'Open Browser',
                onPressed: () => onAction('MERCHANT_PORTAL_REDIRECT', {
                  'url': 'https://www.google.com',
                }),
              ),
              _ActionButton(
                label: 'Set OAuth Session ID',
                onPressed: () => onAction('OAUTH_SESSION_ID', {
                  'oAuthSessionId': '123456789',
                }),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class _MiniActionBar extends StatelessWidget {
  final VoidCallback onClose;

  const _MiniActionBar({required this.onClose});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 64,
      margin: const EdgeInsets.only(top: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0xFFE5E7EB)),
      ),
      child: Center(
        child: _ActionButton(label: 'Close Screen', onPressed: onClose),
      ),
    );
  }
}

const double _actionButtonHeight = 44;

class _ActionButton extends StatelessWidget {
  final String label;
  final VoidCallback? onPressed;

  const _ActionButton({required this.label, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(right: 8),
      child: OutlinedButton(
        style: OutlinedButton.styleFrom(
          backgroundColor: const Color(0xFFF5F5F5),
          foregroundColor: const Color(0xFF2563EB),
          disabledForegroundColor: const Color(0xFF9CA3AF),
          disabledBackgroundColor: const Color(0xFFF5F5F5),
          side: const BorderSide(color: Color(0xFFCCCCCC)),
          padding: const EdgeInsets.symmetric(horizontal: 16),
          minimumSize: const Size(0, _actionButtonHeight),
          maximumSize: const Size(double.infinity, _actionButtonHeight),
          fixedSize: const Size.fromHeight(_actionButtonHeight),
          tapTargetSize: MaterialTapTargetSize.shrinkWrap,
          visualDensity: VisualDensity.compact,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
        ),
        onPressed: onPressed,
        child: Text(label, style: const TextStyle(fontSize: 16)),
      ),
    );
  }
}
0
likes
160
points
454
downloads

Documentation

API reference

Publisher

verified publishersaversapp.com

Weekly Downloads

Flutter SDK for the Savers hosted app: WebView bridge, native maps/dialer/browser, device/session helpers, and encrypted URL generation.

Homepage

License

MIT (license)

Dependencies

connectivity_plus, crypto, cryptography, device_info_plus, dio, flutter, flutter_secure_storage, geolocator, shared_preferences, url_launcher, uuid, webview_flutter

More

Packages that depend on savers_app_sdk