A Flutter package for provisioning Viam robots using hotspot connections. This widget provides a complete flow for connecting (or reconnecting) to a robot's hotspot, selecting a network, and provisioning the robot with network credentials.
flutter pub add viam_flutter_hotspot_provisioning_widget
Before using this widget, you must flash your device with the Viam defaults configuration:
-
Flash your Device: Use the Viam CLI to flash your device: For more instructions on device, see the Viam Documentation for an example on flashing a Raspberry Pi.
-
Configure provisioning defaults.: Create a provisioning configuration file (
viam-defaults.json), by specifying at least the following info:Important: The
hotspot_prefixmust be at least 3 characters long.{ "network_configuration": { "hotspot_prefix": "your-hotspot-prefix", "disable_captive_portal_redirect": true, "hotspot_password": "your-hotspot-password", "fragment_id": "your-fragment-id", } }For more instructions on setting up the config, see the Viam Documentation.
-
Install viam-agent: Run the pre-install script and pass in the location of your viam-defaults.json. This way your machine will know the hotspot prefix and password:
sudo ./preinstall.sh
For more instructions on running the pre-install script, see the Viam Documentation.
Add the following to your Entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.HotspotConfiguration</key>
<true/>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
</dict>
</plist>Add the following to your Info.plist:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Finding and connecting nearby local bluetooth devices</string>Add the following permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>Before starting the provisioning flow, you need to:
- Initialize Viam instance: Create a Viam instance with your API credentials
- Create or get a robot: Either create a new robot or retrieve an existing one from your Viam organization
- Get the main part: Retrieve the main robot part that will be provisioned
These steps are required because the widget needs a valid robot and Viam instance to communicate with the Viam cloud and provision the robot.
import 'package:viam_flutter_hotspot_provisioning_widget/viam_flutter_hotspot_provisioning_widget.dart';
// 1. Initialize a Viam instance with your API credentials
final viam = await Viam.withApiKey(apiKeyId, apiKey);
// 2. Create a new robot or get an existing one
final robot = await viam.appClient.getRobot(robotId);
// OR create a new robot:
// final robotId = await viam.appClient.newMachine(robotName, locationId);
// final robot = await viam.appClient.getRobot(robotId);
// 3. Get the main robot part
final mainPart = (await viam.appClient.listRobotParts(robot.id))
.firstWhere((element) => element.mainPart);
// 4. Start the provisioning flow
// Option 1: Use hardcoded credentials
final result = await HotspotProvisioningFlow.show(
context,
robot: robot,
viam: viam,
mainPart: mainPart,
fragmentId: 'your-fragment-id', // Optional, if null, the fragmentId will be read from the device.
hotspotPrefix: 'your-hotspot-prefix', // Must match viam-defaults.json & must be at least 3 characters long
hotspotPassword: 'your-hotspot-password', // Must match viam-defaults.json
promptForCredentials: false, // Use hardcoded credentials
overrideFragment: true, // Set to true if you want to override the fragment, common for new machines.
replaceHardware: false, // Set to true when replacing hardware and want to apply saved robot config
robotConfig: null, // Optional, pass saved robot config when replaceHardware is true
);
// Option 2: Prompt user for credentials
final result = await HotspotProvisioningFlow.show(
context,
robot: robot,
viam: viam,
mainPart: mainPart,
fragmentId: 'your-fragment-id',
promptForCredentials: true, // This will show a credential input screen
overrideFragment: true, // Set to true if you want to override the fragment, common for new machines.
replaceHardware: false, // Set to true when replacing hardware and want to apply saved robot config
robotConfig: null, // Optional, pass saved robot config when replaceHardware is true
);
// 5. Handle the result
if (result != null) {
if (result.status == RobotStatus.online) {
// Robot successfully provisioned. Robot is online
print('Robot ${result.robot.name} is online!');
} else {
// Provisioning failed or timed out. Robot is offline
print('Robot provisioning failed');
}
}The HotspotProvisioningFlow now supports two ways to provide hotspot credentials:
Pass the credentials directly as parameters.
final result = await HotspotProvisioningFlow.show(
context,
robot: robot,
viam: viam,
mainPart: mainPart,
hotspotPrefix: 'your-prefix',
hotspotPassword: 'your-password',
promptForCredentials: false, // Use hardcoded credentials
overrideFragment: true, // Set to true if you want to override the fragment, common for new machines.
replaceHardware: false, // Set to true when replacing hardware and want to apply saved robot config
robotConfig: null, // Optional, pass saved robot config when replaceHardware is true
);Prompt the user to enter credentials through a simple input screen.
final result = await HotspotProvisioningFlow.show(
context,
robot: robot,
viam: viam,
mainPart: mainPart,
promptForCredentials: true, // This will show a credential input screen
overrideFragment: true, // Set to true if you want to override the fragment, common for new machines.
replaceHardware: false, // Set to true when replacing hardware and want to apply saved robot config
robotConfig: null, // Optional, pass saved robot config when replaceHardware is true
);When promptForCredentials is true, the hotspotPrefix and hotspotPassword parameters are optional and will be ignored.
When replacing hardware, you can preserve the robot's configuration by setting replaceHardware: true and passing the saved robot configuration to robotConfig.
Important: To replace hardware, you need to:
- Create a new robot instance for the replacement hardware
- Save the configuration from the old robot's main part
- Apply the saved configuration to the new robot during provisioning
// Get the saved robot configuration from the old robot
final savedRobotConfig = oldRobotPart.robotConfig.toMap();
// Apply the saved configuration to the new robot
final result = await HotspotProvisioningFlow.show(
context,
robot: newRobot, // Pass in the new replacement robot
viam: viam,
mainPart: newMainPart, // Pass in the new replacement mainPart
fragmentId: 'your-fragment-id',
hotspotPrefix: 'your-hotspot-prefix',
hotspotPassword: 'your-hotspot-password',
promptForCredentials: false,
overrideFragment: false,
replaceHardware: true, // Enable hardware replacement mode
robotConfig: savedRobotConfig, // Pass the saved configuration
);The main widget that handles the entire provisioning flow.
What you need to pass into the widget:
robot: The Viam robot to provisionviam: The Viam SDK instancefragmentId: The optional fragment ID you want to configure this robot with.mainPart: The main robot parthotspotPrefix: The SSID prefix for the robot's hotspot. This prefix must match the prefix you set in the viam-defaults.json. The hotspot prefix must be at least 3 characters long. (Optional whenpromptForCredentialsis true)hotspotPassword: The password for the robot's hotspot. This password must match the password you set in the viam-defaults.json. (Optional whenpromptForCredentialsis true)promptForCredentials: Whether to show a credential input screen for the user to enter hotspot prefix and password. When true,hotspotPrefixandhotspotPasswordare optional.overrideFragment: Whether this is a new machine being provisioned for the first time. Set totruefor new machines,falsefor reconnecting existing machines. Whentrue, the fragment override will be performed after successful provisioning.replaceHardware: Whether you are replacing hardware and want to apply a saved robot configuration. Set totruewhen replacing hardware,falseotherwise.robotConfig: Optional saved robot configuration to apply whenreplaceHardwareistrue. Pass the configuration from the old robot that you want to apply to the new robot. Can be omitted whenreplaceHardwareisfalse.
Contains the result of the provisioning attempt:
robot: The robot that was provisionedstatus: The robot's status (online/offline)
- Manual Network Entry: Fallback option to manually enter network credentials when automatic detection fails
- Error Handling: User-friendly error messages for common issues like incorrect hotspot password.
- Network Type Indicators: Icons to distinguish between public and private Wi-Fi networks
- Public Network Support: Connect to public networks that don't require a password
This package depends on:
plugin_wifi_connect: For Wi-Fi connection functionalityviam_sdk: For Viam robot communicationpermission_handler: For platform permissionsflutter_platform_widgets: For platform-specific UIprovider: For state management
See the example/hotspot_provisioning directory for a complete working example app that allows you to provision Viam devices.
- Do not connect manually: Users should not connect to the hotspot through their device's Wi-Fi settings. The app will prompt them to connect when ready.
- Hotspot credentials: The hotspot prefix and password must match what's configured in your
viam-defaults.jsonfile.
-
Cannot connect to hotspot: Ensure the hotspot prefix and password match your
viam-defaults.jsonconfiguration. -
Permission errors: Make sure you've added the required iOS entitlements and permissions.
-
Robot not appearing: Verify your robot is properly flashed with the Viam image and
viam-defaults.json. -
Network not found: Ensure your robot's hotspot is active and broadcasting.
See the LICENSE file for license rights and limitations.
