Flutter

To use our SDK with Flutter, you only need to add a JavaScriptChannel named FlutterWebViewwhen using the webview_flutter package. This channel allows our server to communicate with WebViews in Flutter. See the example below:

// ignore_for_file: avoid_print

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
// https://pub.dev/packages/webview_flutter
import 'package:webview_flutter/webview_flutter.dart'; 

const String sdkMobileServer = 'https://sdk-mobile.cert.zerohash.com/v1';
const String zeroHashAppsURL = 'https://web-sdk.cert.zerohash.com';

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

  @override
  WebViewScreenState createState() => WebViewScreenState();
}

class WebViewScreenState extends State<WebViewScreen> {
  String token = "";
  late WebViewController controller;

  @override
  void initState() {
    super.initState();
    fetchData();
  }

  // Setup webview controller with the token in the URL
  // Do not remove FlutterWebView channel, it is required for communication between webview and webpage
  void setupController() {
    controller = WebViewController()
        ..setJavaScriptMode(JavaScriptMode.unrestricted)
        ..addJavaScriptChannel('FlutterWebView', onMessageReceived: onMessageReceived)
        ..loadRequest(
          Uri.parse('$sdkMobileServer?achDepositsJWT=$token&cryptoBuyJWT=$token&cryptoSellJWT=$token&cryptoWithdrawalsJWT=$token&fiatWithdrawalsJWT=$token&userOnboardingJWT=$token&zeroHashAppsURL=$zeroHashAppsURL')
    );
  }

  // Fetch token from the server and setup webview controller with the token
  Future<void> fetchData() async {
    try {
      final url = Uri.parse('<API_URL_TO_FETCH_TOKEN>');
      final response = await http.post(url,
          body: json.encode(
            {
              'participant_code': '<PARTICIPANT_CODE>',
              'permissions': [
                'crypto-buy',
                'crypto-sell',
                'crypto-withdrawals',
                'fiat-deposits',
                'fiat-withdrawals',
              ]
            }
          )
        );
      if (response.statusCode >= 200 && response.statusCode < 300) {
        // Store the data in the 'token' and setup webview controller
        var body = json.decode(response.body);
        setState(() {
          token = body['message']['token'];
        });
        setupController();
      } else {
        print('Failed to fetch data: $response');
      }
    } catch (e) {
      // Handle network or API call errors
      print('Failed to fetch data: $e');
    }
  }

  // Handle messages received from the webview
  void onMessageReceived(JavaScriptMessage message) {
    try {
      print('Receving message: ${message.message}');
      // Parse the message JSON
      Map<String, dynamic> messageJson = jsonDecode(message.message);
      if (messageJson.containsKey('type')) {
        // Indicates that the SDK mobile is ready to receive postMessage
        if (messageJson['type'] == 'SDK_MOBILE_READY') {
          print('SDK mobile ready, opening modal');
          // Open the modal for the app with the identifier 'crypto-sell'
          controller.runJavaScript('window.postMessage({ "type": "OPEN_MODAL", "payload": { "appIdentifier": "crypto-buy"}})');
        }
      }
    } catch (e) {
      print('Error parsing JSON: $e');
    }
  }

  Widget _buildChild() {
    // If the token is empty, show a loading message
    if (token.isEmpty) {
      return const Center(
        child: Text('Fetching token...'),
      );
    }
    // Return the WebViewWidget with the controller
    return WebViewWidget(controller: controller);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              Expanded(
                child: _buildChild(),
              ),
            ],
          ),
        ),
      ),
    );
  }