ensemble_wifi 1.2.54
ensemble_wifi: ^1.2.54 copied to clipboard
Programmatic WiFi connection support for Ensemble apps.
ensemble_wifi #
Ensemble module for programmatic WiFi connections on mobile devices. It wraps plugin_wifi_connect and exposes a single ensemble action, connectToWifi.
Use this module when your app needs to join a WiFi network from code — for example IoT device setup where the device broadcasts a known SSID or SSID prefix.
Not the same as
ensemble_network_info— that module reads WiFi metadata (SSID, IP, BSSID). This module connects to a network.
Overview #
flowchart LR
subgraph app["Ensemble app"]
YAML["YAML / JS<br/>connectToWifi"]
Action["ConnectToWifiAction"]
end
subgraph core["ensemble (core)"]
GetIt["GetIt<WifiManager>"]
Stub["WifiManagerStub"]
end
subgraph module["ensemble_wifi"]
Impl["WifiManagerImpl"]
end
subgraph native["Native"]
Plugin["plugin_wifi_connect"]
OS["Android / iOS WiFi APIs"]
end
YAML --> Action
Action --> GetIt
GetIt -->|module enabled| Impl
GetIt -->|module disabled| Stub
Impl --> Plugin
Plugin --> OS
Stub -.->|throws config error| Action
When the module is disabled, the core runtime uses WifiManagerStub, which throws a configuration error if connectToWifi is invoked.
ensemble_wifi vs ensemble_network_info #
flowchart TB
subgraph read["ensemble_network_info"]
R1["getNetworkInfo()"]
R2["Current SSID, IP, BSSID"]
end
subgraph connect["ensemble_wifi"]
C1["connectToWifi"]
C2["Join a network programmatically"]
end
Device["Device WiFi stack"]
R1 --> R2
R2 -.->|read only| Device
C1 --> C2
C2 -->|write / join| Device
Platform support #
| Platform | Supported | Notes |
|---|---|---|
| Android | Yes | Android 10+ (API 29) recommended. Requires CHANGE_NETWORK_STATE + runtime location permission. Legacy WiFi permissions for API 28 and below. |
| iOS | Yes | iOS 11+. Requires a physical device — simulators cannot join WiFi. |
| Web | No | Action routes to onError with a web-not-supported message. |
Enable the module #
From the starter directory:
dart scripts/modules/enable_wifi.dart --platforms=android,ios
Or via the Ensemble CLI:
enable wifi
The enable script:
- Uncomments the
ensemble_wifidependency instarter/pubspec.yaml - Sets
useWifi = trueand registersWifiManagerImplinstarter/lib/generated/ensemble_modules.dart - Adds Android WiFi permissions to
AndroidManifest.xml(see Android permissions) - Adds
NSLocationWhenInUseUsageDescriptiontoInfo.plist - Adds WiFi entitlements to
Runner.entitlementsand links them in the Xcode project
Then from the repo root:
melos bootstrap
Rebuild the app (full rebuild after native changes):
cd starter
flutter clean
cd ios && pod install && cd ..
flutter run
iOS setup checklist #
- Physical device — WiFi connect does not work on the iOS Simulator.
- Location permission — grant when prompted (
NSLocationWhenInUseUsageDescription). - Apple Developer portal — enable Hotspot Configuration and Access WiFi Information for your App ID. Entitlements are written to
Runner.entitlements, but Apple must approve Hotspot Configuration for distribution. - System join prompt — when iOS shows “Join Network”, tap Join. Cancelling returns a failed result.
Android permissions #
The enable script adds these manifest permissions:
| Permission | Scope | Purpose |
|---|---|---|
ACCESS_FINE_LOCATION |
All API levels | Runtime location grant for WiFi connect |
CHANGE_NETWORK_STATE |
All API levels | Required for ConnectivityManager.requestNetwork() on Android 10+ |
ACCESS_WIFI_STATE |
API 28 and below (maxSdkVersion="28") |
Read WiFi state on older Android |
CHANGE_WIFI_STATE |
API 28 and below | Legacy connect/disconnect on older Android |
Runtime: WifiManagerImpl requests location permission via Geolocator before connect. Location services must also be enabled on the device.
CHANGE_NETWORK_STATE is a normal (install-time) permission — no runtime prompt, but it must be in the manifest without maxSdkVersion="28" or Android 10+ throws SecurityException on connect.
flowchart LR
Manifest["AndroidManifest.xml<br/>(enable script)"]
Runtime["Geolocator<br/>(WifiManagerImpl)"]
Connect["connectToWifi"]
Manifest -->|all API levels| NetChange["CHANGE_NETWORK_STATE"]
Manifest -->|API 29+| LocPerm["ACCESS_FINE_LOCATION"]
Manifest -->|API 28−| Legacy["ACCESS/CHANGE_WIFI_STATE"]
Connect --> Runtime
Runtime -->|grant + services on| Connect
NetChange --> Connect
LocPerm --> Connect
Legacy --> Connect
Android setup checklist #
- Manifest permissions — added automatically by
enable_wifi.dart(see table above). - Location permission — grant when prompted at runtime (required on Android 10+).
- Location services — must be enabled on the device.
Ensemble action: connectToWifi #
One action handles all WiFi operations. Set operation to choose the mode (default: connect).
Operations #
| Operation | Description | Platforms |
|---|---|---|
connect |
Join a WiFi network (default) | Android, iOS |
disconnect |
Disconnect from a network joined via this plugin | Android, iOS |
Connect routing #
For operation: connect (or when operation is omitted), the action picks the underlying API based on the parameters you provide:
flowchart TD
Start(["operation: connect"])
Op{"operation?"}
Prefix{"ssidPrefix<br/>set?"}
Pwd{"password<br/>set?"}
SecPrefix["connectToSecureNetworkByPrefix"]
OpenPrefix["connectByPrefix"]
SecSsid["connectToSecureNetwork"]
OpenSsid["connect"]
Start --> Op
Op -->|disconnect| Disc["disconnect()"]
Op -->|connect / default| Prefix
Prefix -->|yes| Pwd
Prefix -->|no| HasSsid{"ssid<br/>set?"}
Pwd -->|yes| SecPrefix
Pwd -->|no| OpenPrefix
HasSsid -->|yes| Pwd2{"password<br/>set?"}
HasSsid -->|no| Err["onError:<br/>ssid or ssidPrefix required"]
Pwd2 -->|yes| SecSsid
Pwd2 -->|no| OpenSsid
| Parameters provided | Behavior |
|---|---|
ssidPrefix + password |
Secured connect to nearest network matching prefix |
ssidPrefix only |
Open connect to nearest network matching prefix |
ssid + password |
Secured connect to exact SSID |
ssid only |
Open connect to exact SSID |
Runtime flow (connect) #
sequenceDiagram
participant User
participant Action as ConnectToWifiAction
participant Impl as WifiManagerImpl
participant Geo as Geolocator
participant Plugin as plugin_wifi_connect
participant OS as OS WiFi
User->>Action: connectToWifi (ssid, password, …)
Action->>Action: eval ssid / password expressions
alt Web platform
Action-->>User: onError (not supported)
else Mobile
Action->>Impl: connectToSecureNetwork(…)
Impl->>Geo: check / request location permission
alt Location services disabled
Geo-->>Impl: disabled
Impl-->>Action: WifiLocationDisabledException
Action-->>User: onLocationDisabled (falls back to onError)
else Permission denied
Geo-->>Impl: denied
Impl-->>Action: WifiPermissionDeniedException
Action-->>User: onPermissionDenied (falls back to onError)
else Permission granted
Geo-->>Impl: granted
Impl->>Plugin: connectToSecureNetwork(…)
Plugin->>OS: join network
OS-->>Plugin: result
Plugin-->>Impl: true / false / null
Impl-->>Action: result
alt result == true
Action-->>User: onSuccess (connected: true)
else result == false or null
Action-->>User: onError (denied or unverified)
end
end
end
Parameters #
| Parameter | Required | Default | Description |
|---|---|---|---|
operation |
No | connect |
connect or disconnect |
ssid |
One of ssid or ssidPrefix (connect only) |
— | Exact network name |
ssidPrefix |
One of ssid or ssidPrefix (connect only) |
— | Match nearest network by SSID prefix (common for IoT devices) |
password |
No | — | If set, uses secured connect |
saveNetwork |
No | false |
Remember the network on the device after connecting |
isWep |
No | false |
WEP encryption (not supported on Android) |
isWpa3 |
No | false |
WPA3 network |
isHidden |
No | false |
Hidden SSID (exact ssid connect only) |
onSuccess |
No | — | Action run when the operation succeeds |
onError |
No | — | Action run for connection / other failures |
onPermissionDenied |
No | — | Action run when location permission is denied (falls back to onError) |
onLocationDisabled |
No | — | Action run when location services are off (falls back to onError) |
All string parameters support Ensemble expressions (e.g. ${devicePassword}).
Event data #
onSuccess (connect / disconnect)
data:
connected: true # connect/disconnect returned true
onError
error: "<message>"
data:
status: error
connected: false # or null, when connect verification failed
onPermissionDenied
error: "Location permission is required to connect to WiFi on this device."
data:
status: permissionDenied
onLocationDisabled
error: "Location services are disabled. Enable location to connect to WiFi."
data:
status: locationDisabled
Use ${event.error} and ${event.data.status} in nested actions (e.g. showToast).
JavaScript #
connectToWifi is exposed to page scripts via ActionInvokable:
ensemble.connectToWifi({
ssid: 'MyNetwork',
password: myPassword,
onSuccess: { showToast: { message: 'Connected' } },
onPermissionDenied: { showToast: { message: 'Please grant location permission' } },
onLocationDisabled: { showToast: { message: 'Please enable location services' } },
onError: { showToast: { message: event.error } }
});
Examples #
Typical IoT provisioning flow #
sequenceDiagram
participant App as Ensemble app
participant Phone as Phone WiFi
participant IoT as IoT device hotspot
participant Home as Home router
Note over App,Home: Phase 1 — join device hotspot
App->>Phone: connectToWifi (ssidPrefix: Device-)
Phone->>IoT: connect to Device-ABC123
IoT-->>App: device reachable on local network
Note over App,Home: Phase 2 — configure device, then rejoin home WiFi
App->>IoT: send home SSID + password
App->>Phone: connectToWifi (ssid: HomeNet, password, saveNetwork: true)
Phone->>Home: connect to home network
Open network by SSID #
onTap:
connectToWifi:
ssid: MyIoT-Device
onSuccess:
showToast:
message: Connected
onPermissionDenied:
showToast:
message: Please grant location permission to connect to WiFi
onLocationDisabled:
showToast:
message: Please enable location services
onError:
showToast:
message: ${event.error}
Secured home network #
onTap:
connectToWifi:
ssid: KPN089C36
password: ${wifiPassword}
saveNetwork: true
onSuccess:
navigateScreen:
name: Home
IoT device by SSID prefix #
Many IoT devices broadcast SSIDs like DeviceName-ABC123. Use a prefix match:
onTap:
connectToWifi:
ssidPrefix: DeviceName-
password: ${devicePassword}
onSuccess:
showToast:
message: Connected to device
Disconnect #
onTap:
connectToWifi:
operation: disconnect
onSuccess:
showToast:
message: Disconnected
Troubleshooting #
| Symptom | Likely cause |
|---|---|
| Config error: module not enabled | Run enable_wifi.dart and melos bootstrap |
| Web not supported | Expected — use Android or iOS device |
iOS hotspotError_8 / internal error on Simulator |
Expected — test on a physical iPhone |
| Connect fails after location granted | Wrong SSID/password, join prompt cancelled, or Hotspot Configuration not enabled in Apple Developer portal |
MissingPluginException for activate/deactivate |
Not supported — use connect / disconnect only (see Operations above) |
SecurityException: CHANGE_NETWORK_STATE not granted |
Remove maxSdkVersion="28" from CHANGE_NETWORK_STATE in manifest; rebuild and reinstall |
Development #
melos bootstrap
melos exec --scope="ensemble_wifi" -- flutter analyze
Related #
| Package / module | Purpose |
|---|---|
ensemble (core) |
ConnectToWifiAction, WifiManager stub, action registration |
ensemble_network_info |
Read current WiFi name, IP, BSSID (does not connect) |
plugin_wifi_connect |
Native WiFi connect plugin |