© image by Francis Mwakitumbula
A flutter plugin implemanting usehover.com ussd gateway sdk using Android Intent and receiving the transaction information back in response. android only
The Hover USSD plugin provides a simple and easy-to-use interface for integrating Hover USSD services into your Flutter applications. It allows you to start USSD transactions, listen to transaction states, and retrieve available actions from the Hover API.
-
Installation
Add
hover_ussd
to yourpubspec.yaml
file:dependencies: hover_ussd: ^latest_version
Then, run:
flutter pub get
-
Initialization
Initialize HoverUssd with your Hover API key at docs.usehover.com, branding, and logo information:
final HoverUssd _hoverUssd = HoverUssd(); await _hoverUssd.initialize( apiKey: "YOUR_API_KEY", branding: "Your App Name", logo: "ic_launcher", notificationLogo: "ic_launcher", );
-
Start a Transaction
Start a USSD transaction with the provided parameters:
await _hoverUssd.startTransaction( actionId: "ACTION_ID", extras: {}, theme: "myhoverTheme", //located in android/app/main/values/styles.xml header: "Hover Ussd Example", showUserStepDescriptions: true, );
-
Permissions Check
Check if the app has all the necessary permissions:
bool hasPermissions = await _hoverUssd.hasAllPermissions();
-
Overlay and Accessibility Check
Check if the app has overlay and accessibility permissions:
bool isOverlayEnabled = await _hoverUssd.isOverlayEnabled(); bool isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled();
-
Retrieve Actions
Retrieve a list of available actions:
List<HoverAction> actions = await _hoverUssd.getAllActions();
-
Refresh Actions
Refresh actions to get the latest updates:
await _hoverUssd.refreshActions();
-
Listen to Transaction States
Get a stream of transaction states:
Stream<TransactionState> transactionStream = _hoverUssd.getUssdTransactionState();
-
Listen to Download Action States
Get a stream of download action states:
Stream<DonwloadActionState> downloadStream = _hoverUssd.getDownloadActionState();
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
<style name="myHoverTheme" parent="Theme.AppCompat.Light.NoActionBar" >
<item name="android:windowActionBar">true</item>
<item name="android:windowContentTransitions">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="hover_primaryColor">@color/colorPrimaryDark</item>
<item name="hover_textColor">@color/colorPureWhite</item>
<item name="hover_textColorTitle">@color/colorHoverWhite</item>
<item name="hover_pinEntryColor">@color/colorPureWhite</item>
<item name="hover_pinTheme">@style/PinButtonTheme</item>
<item name="hover_posTheme">@style/PosButtonTheme</item>
</style>
<style name="PinButtonTheme" parent="Widget.AppCompat.Button.Borderless">
<item name="android:textColor">@color/colorPureWhite</item>
<item name="android:textSize">24sp</item>
<item name="android:background">@color/colorPrimaryDark</item>
<item name="android:typeface">normal</item>
<item name="android:textAllCaps">true</item>
</style>
<style name="PosButtonTheme" parent="Widget.AppCompat.Button.Colored">
<item name="android:textColor">@color/colorPureWhite</item>
<item name="background">@color/colorPrimary</item>
<item name="android:typeface">normal</item>
<item name="android:textAllCaps">false</item>
</style>
</resources>
await _hoverUssd.startTransaction(
actionId: "ACTION_ID",
extras: {},
theme: "myhoverTheme", // as in android/app/main/values/styles.xml
header: "Hover Ussd Example"// the title of your hover session,
showUserStepDescriptions: true,
);
final String activityName = "com.example.yourApp.custompermissionsActivity";
_hoverUssd.setPermissionsActivity(activityName: activityName);
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final HoverUssd _hoverUssd = HoverUssd();
// Create an instance of HoverUssd you can pass it to a provider,
// or use get_it to make it available to the entire app
late StreamSubscription _transactionListening;
late StreamSubscription _actionDownloadListening;
bool _hasPermissions = false;
bool _isOverlayEnabled = false;
bool _isAccessibilityEnabled = false;
@override
void initState() {
_transactionListening =
_hoverUssd.getUssdTransactionState().listen((event) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(event.toMap().toString())));
});
_actionDownloadListening =
_hoverUssd.getDownloadActionState().listen((event) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(event.toString())));
});
_checkAccessibility();
_checkPermissions();
_checkOverlay();
//initialize hover after the downlad listener is set
_actionDownloadListening.onData((event) {
_initHover();
});
super.initState();
}
@override
void dispose() {
_transactionListening.cancel();
_actionDownloadListening.cancel();
super.dispose();
}
//check for permissions
Future<void> _checkPermissions() async {
_hasPermissions = await _hoverUssd.hasAllPermissions();
setState(() {});
}
Future<void> _checkAccessibility() async {
_isAccessibilityEnabled = await _hoverUssd.isAccessibilityEnabled();
setState(() {});
}
Future<void> _checkOverlay() async {
_isOverlayEnabled = await _hoverUssd.isOverlayEnabled();
setState(() {});
}
Future<void> _initHover() async {
try {
await _hoverUssd.initialize(
apiKey: "15ccc2bd81801d8c5fbfd5847d3b4e77",
branding: "My Hover App",
logo: "ic_launcher",
notificationLogo: "ic_launcher",
);
} catch (e) {
print(e);
}
}
Future<void> _getAndGoToActions() async {
final actions = await _hoverUssd.getAllActions();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HoverActionListPage(
hoverActions: actions,
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Hover Ussd Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_hoverUssd.startTransaction(
actionId: "c6e45e62",
extras: {"price": "4000"},
theme: "HoverTheme",
header: "Hover Ussd Example",
showUserStepDescriptions: true,
);
},
child: const Text("Start Transaction"),
),
ElevatedButton(
onPressed: _getAndGoToActions,
child: const Text("Get Actions"),
),
//refresh actions
ElevatedButton(
onPressed: () {
_hoverUssd.refreshActions();
},
child: const Text("Refresh Actions"),
),
Text(
_hasPermissions
? "Permissions Granted"
: "Permissions Not Granted",
style: TextStyle(
color: _hasPermissions ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
Text(
_isAccessibilityEnabled
? "Accessibility Granted"
: "Accessibility Not Granted",
style: TextStyle(
color: _isAccessibilityEnabled ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
Text(
_isOverlayEnabled ? "Overlay Granted" : "Overlay Not Granted",
style: TextStyle(
color: _isOverlayEnabled ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
),
),
StreamBuilder<DonwloadActionState>(
stream: _hoverUssd.getDownloadActionState(),
builder: (context, snapshot) {
final state = snapshot.data;
String statusText;
Color textColor;
if (state is ActionDownloaded) {
statusText = "Actions Downloaded";
textColor = Colors.green;
} else if (state is ActionDownloadFailed) {
statusText = "Actions Not Downloaded";
textColor = Colors.red;
} else if (state is ActionDownloading) {
statusText = "Actions Downloading";
textColor =
Colors.blue; // Adjust color as per your preference
} else {
statusText = "Action Download Status: Unknown";
textColor = Colors.grey;
}
return Text(
statusText,
style:
TextStyle(color: textColor, fontWeight: FontWeight.bold),
);
},
),
StreamBuilder<TransactionState>(
stream: _hoverUssd.getUssdTransactionState(),
builder: (BuildContext context, snapshot) {
if (snapshot.data is SmsParsed) {
return Text("Sms parsed : \n${snapshot.data!.toMap()}");
}
if (snapshot.data is UssdSucceeded) {
return Text("Ussd Succeded : \n${snapshot.data!.toMap()}");
}
if (snapshot.data is UssdLoading) {
return const Text("loading...");
}
if (snapshot.data is UssdFailed) {
return Text("Ussd Failed : \n${snapshot.data!.toMap()}");
}
if (snapshot.data is EmptyState) {
return const Text("Empty State");
}
return const Text("No state");
},
),
],
),
),
);
}
}
- This is a unofficial plugin
- Thanks to the authors of useHover android sdk, this work based of it