Skip to content

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

ParameterRequiredDescription
clientIdYesClient ID provided by the Hubble team
clientSecretYesClient secret provided by the Hubble team
tokenNoAuth token that uniquely identifies your user. Optional if lazy login is enabled.
wrap-pltYesPlatform identifier. Use flutter.
appVersionNoApp version string. Defaults to "10000".
deviceIdNoDevice identifier.

To navigate directly to a specific page, append the route path before the query parameters. See the Deeplinks page for the full route mapping.

PageURL Format
Home (default)https://vouchers.myhubble.money/sdk/gc/?clientId=...
Brand purchasehttps://vouchers.myhubble.money/sdk/gc/buy/{brandId}?clientId=...
Searchhttps://vouchers.myhubble.money/sdk/gc/search?clientId=...
Transactionshttps://vouchers.myhubble.money/sdk/gc/transactions?clientId=...
Helphttps://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"}}
ActionDescription
closeUser tapped close. Dismiss the WebView.
app_readySDK loaded successfully.
errorSDK 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 jsonDecode
import '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),
),
),
);
}
}