Flutter Integration
Gift Card Store - Flutter integration example
This example demonstrates how to integrate the Hubble Gift Card Store SDK in a Flutter application.
Parameters
| Parameter | Required | Description |
|---|---|---|
clientId | Yes | Client ID provided by the Hubble team |
clientSecret | Yes | Client secret provided by the Hubble team |
token | No | Auth token that uniquely identifies your user. Optional if lazy login is enabled. |
wrap-plt | Yes | Platform identifier. Use flutter. |
appVersion | No | App version string. Defaults to "10000". |
deviceId | No | Device identifier. |
Deep Links
To navigate directly to a specific page, append the route path before the query parameters. See the Deeplinks page for the full route mapping.
| Page | URL Format |
|---|---|
| Home (default) | https://vouchers.myhubble.money/sdk/gc/?clientId=... |
| Brand purchase | https://vouchers.myhubble.money/sdk/gc/buy/{brandId}?clientId=... |
| Search | https://vouchers.myhubble.money/sdk/gc/search?clientId=... |
| Transactions | https://vouchers.myhubble.money/sdk/gc/transactions?clientId=... |
| Help | https://vouchers.myhubble.money/sdk/gc/help?clientId=... |
Events
The SDK communicates with the host app via a JavaScript channel named FlutterHost. Events are JSON objects with a type field:
Action events — SDK lifecycle and navigation:
{"type": "action", "action": "close"}{"type": "action", "action": "app_ready"}{"type": "action", "action": "error"}Analytics events — user interaction tracking:
{"type": "analytics", "event": "event_name", "properties": {"key": "value"}}| Action | Description |
|---|---|
close | User tapped close. Dismiss the WebView. |
app_ready | SDK loaded successfully. |
error | SDK failed to load. Show an error state. |
See the Events page for the full list of analytics events.
Full Example
import 'dart:convert'; // Required for jsonDecodeimport 'package:flutter/material.dart';import 'package:webview_flutter/webview_flutter.dart';import 'package:url_launcher/url_launcher.dart';
class HubbleWebView extends StatefulWidget { const HubbleWebView({Key? key}) : super(key: key);
@override State<HubbleWebView> createState() => _HubbleWebViewState();}
class _HubbleWebViewState extends State<HubbleWebView> { late final WebViewController _controller; bool _isWebViewReady = false;
@override void initState() { super.initState(); _initWebView(); }
void _initWebView() { final params = { 'token': 'your_auth_token', // your auth token (optional for lazy login) 'clientId': 'id_given_by_hubble', 'clientSecret': 'secret_given_by_hubble', 'wrap-plt': 'flutter', // 'appVersion': '10000', // optional, defaults to 10000 // 'deviceId': 'device_id', // optional };
final baseUrl = 'https://vouchers.dev.myhubble.money/sdk/gc/'; // prod baseUrl is https://vouchers.myhubble.money/sdk/gc/
final sourceUrl = '$baseUrl?clientId=${params['clientId']}&clientSecret=${params['clientSecret']}&token=${params['token']}&wrap-plt=${params['wrap-plt']}';
// Deep link example: to open a brand page directly, append the route path: // final sourceUrl = '${baseUrl}buy/uber?clientId=${params['clientId']}&clientSecret=${params['clientSecret']}&token=${params['token']}&wrap-plt=${params['wrap-plt']}';
_controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setBackgroundColor(Colors.white) ..setNavigationDelegate( NavigationDelegate( onPageStarted: (String url) { print('Page started loading: $url'); }, onPageFinished: (String url) { setState(() { _isWebViewReady = true; }); print('Page finished loading: $url'); }, onWebResourceError: (WebResourceError error) { print('Web resource error: ${error.description}'); }, onNavigationRequest: (NavigationRequest request) { if (request.url.startsWith(baseUrl) || request.url.startsWith('https://api.razorpay.com')) { return NavigationDecision.navigate; } else { _launchURL(request.url); return NavigationDecision.prevent; } }, ), ) ..addJavaScriptChannel( 'FlutterHost', onMessageReceived: (JavaScriptMessage message) { _handleEvent(message.message); }, ) ..loadRequest(Uri.parse(sourceUrl)); }
Future<void> _launchURL(String url) async { final uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } else { print('Could not launch $url'); } }
void _handleEvent(String jsonString) { try { final eventData = jsonDecode(jsonString) as Map<String, dynamic>; final String? type = eventData['type'] as String?;
if (type == 'action') { final String? action = eventData['action'] as String?; if (action == 'close') { print("Received 'close' action from WebView. Popping screen."); Navigator.of(context).pop(); } else if (action == 'app_ready') { print('Hubble SDK is ready'); } else if (action == 'error') { print('Hubble SDK encountered an error'); } } else if (type == 'analytics') { final String? event = eventData['event'] as String?; final Map<String, dynamic>? properties = eventData['properties'] as Map<String, dynamic>?; if (event != null) { _logEvent(event, properties ?? {}); } } } catch (e) { print('Error handling event from WebView: $e'); print('Received message: $jsonString'); } }
void _logEvent(String eventType, Map<String, dynamic> eventParams) { // Your implementation of sending events to analytics print('Analytics Event Type: $eventType'); print('Analytics Event Params: $eventParams'); }
@override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; if (!_isWebViewReady) { Navigator.of(context).pop(); return; } if (await _controller.canGoBack()) { await _controller.goBack(); } else { if (context.mounted) { Navigator.of(context).pop(); } } }, child: Scaffold( body: SafeArea( child: WebViewWidget(controller: _controller), ), ), ); }}