ensemble_wifi 1.2.54 copy "ensemble_wifi: ^1.2.54" to clipboard
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&lt;WifiManager&gt;"]
    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:

  1. Uncomments the ensemble_wifi dependency in starter/pubspec.yaml
  2. Sets useWifi = true and registers WifiManagerImpl in starter/lib/generated/ensemble_modules.dart
  3. Adds Android WiFi permissions to AndroidManifest.xml (see Android permissions)
  4. Adds NSLocationWhenInUseUsageDescription to Info.plist
  5. Adds WiFi entitlements to Runner.entitlements and 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 #

  1. Physical device — WiFi connect does not work on the iOS Simulator.
  2. Location permission — grant when prompted (NSLocationWhenInUseUsageDescription).
  3. 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.
  4. 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 #

  1. Manifest permissions — added automatically by enable_wifi.dart (see table above).
  2. Location permission — grant when prompted at runtime (required on Android 10+).
  3. 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
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