It always looks cool to control any electrical equipment with a mobile device remotely without even going near it.
Today, we will demonstrate how you can give any normal AC power plug, socket or switch a touch of magic using IoT. This will enable us to control any electrical equipment in our house using a mobile device, with the help of an app (of course, built with Flutter) having Bluetooth capability.
This article will cover a lot of things:
- Integrating Bluetooth in a Flutter app (for communicating with the IoT device)
- Connecting various modules to Arduino (Bluetooth module and a 5V relay)
- Setting up Arduino (to send and receive signals)
Tutorial by Codemagic: Creating an IoT based Flutter app to interact with any home electrical equipment. Wow!CLICK TO TWEET
Let’s get started by integrating Bluetooth in a Flutter app.
Integrating Bluetooth
The final app UI will look like this:
For this project, we will be using a plugin called flutter_bluetooth_serial. There is another awesome plugin for integrating Bluetooth in a Flutter app, flutter_blue.
I am using flutter_bluetooth_serial plugin in this project because it has a lot more features and gives much greater control over the Bluetooth connection while communicating with other devices.
The downside of this plugin is that it only has support for Android as of now. Any iOS developer who wants to contribute to this project can add iOS support to it (there is also an issue open on the plugin’s GitHub repo for this).
Defining the basic structure
Create a new Flutter app and add the following to the main.dart
file after removing all of the Flutter Demo Counter app code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BluetoothApp(),
);
}
}
class BluetoothApp extends StatefulWidget {
@override
_BluetoothAppState createState() => _BluetoothAppState();
}
class _BluetoothAppState extends State<BluetoothApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(),
);
}
}
This is the basic structure for starting our project. We have to use a StatefulWidget for the BluetoothApp class because we will need to mutate the state multiple times during the lifecycle of the app.
Enabling Bluetooth and retrieving devices
Now that we have the basic structure of the app, we can focus on enabling the Bluetooth connection of the user’s device and retrieve the paired devices.
- Initialize some of the variables which will be required (we will be adding more class member variables as needed later while building the app):
// main.dart class _BluetoothAppState extends State<BluetoothApp> { // Initializing the Bluetooth connection state to be unknown BluetoothState _bluetoothState = BluetoothState.UNKNOWN; // Get the instance of the Bluetooth FlutterBluetoothSerial _bluetooth = FlutterBluetoothSerial.instance; // Track the Bluetooth connection with the remote device BluetoothConnection connection; // To track whether the device is still connected to Bluetooth bool get isConnected => connection != null && connection.isConnected; // ... }
- Inside
initState()
, we first have to get the current Bluetooth state. If the state indicates that Bluetooth is not turned on, request that the user give Bluetooth permissions for enabling Bluetooth on their device.// main.dart // Inside _BluetoothAppState class // This member variable will be used for tracking // the Bluetooth device connection state int _deviceState; @override void initState() { super.initState(); // Get current state FlutterBluetoothSerial.instance.state.then((state) { setState(() { _bluetoothState = state; }); }); _deviceState = 0; // neutral // If the Bluetooth of the device is not enabled, // then request permission to turn on Bluetooth // as the app starts up enableBluetooth(); // Listen for further state changes FlutterBluetoothSerial.instance .onStateChanged() .listen((BluetoothState state) { setState(() { _bluetoothState = state; // For retrieving the paired devices list getPairedDevices(); }); }); }
- Now, we have to define the two functions:
enableBluetooth()
for getting Bluetooth permissions from the user.getPairedDevices()
for retrieving the paired devices list.
// main.dart // Inside _BluetoothAppState class Future<void> enableBluetooth() async { // Retrieving the current Bluetooth state _bluetoothState = await FlutterBluetoothSerial.instance.state; // If the Bluetooth is off, then turn it on first // and then retrieve the devices that are paired. if (_bluetoothState == BluetoothState.STATE_OFF) { await FlutterBluetoothSerial.instance.requestEnable(); await getPairedDevices(); return true; } else { await getPairedDevices(); } return false; }
The function implementation for retrieving the Bluetooth devices list:// main.dart // Inside _BluetoothAppState class // Define a new class member variable // for storing the devices list List<BluetoothDevice> _devicesList = []; Future<void> getPairedDevices() async { List<BluetoothDevice> devices = []; // To get the list of paired devices try { devices = await _bluetooth.getBondedDevices(); } on PlatformException { print("Error"); } // It is an error to call [setState] unless [mounted] is true. if (!mounted) { return; } // Store the [devices] list in the [_devicesList] for accessing // the list outside this class setState(() { _devicesList = devices; }); }
- In order to avoid any memory leaks, we will be defining the
dispose()
method to make sure that the connection is closed and resources are freed.// Define a member variable to track // when the disconnection is in progress bool isDisconnecting = false; @override void dispose() { if (isConnected) { isDisconnecting = true; connection.dispose(); connection = null; } super.dispose(); }
UI for enabling Bluetooth
So, we have all the methods required for establishing a proper Bluetooth connection with a device, and now we have to map them to some UI widgets.
First of all, we will be adding a Switch widget which can toggle between only two modes (on/off).
The code for this component is available below:
// main.dart
// Inside _BluetoothAppState class
// Define this member variable for storing
// the current device connectivity status
bool _connected = false;
// Inside build method
Switch(
value: _bluetoothState.isEnabled,
onChanged: (bool value) {
future() async {
if (value) {
// Enable Bluetooth
await FlutterBluetoothSerial.instance
.requestEnable();
} else {
// Disable Bluetooth
await FlutterBluetoothSerial.instance
.requestDisable();
}
// In order to update the devices list
await getPairedDevices();
_isButtonUnavailable = false;
// Disconnect from any device before
// turning off Bluetooth
if (_connected) {
_disconnect();
}
}
future().then((_) {
setState(() {});
});
},
)
Here, the Switch will toggle between on/off depending on whether the Bluetooth connection is enabled/disabled. Irrespective of whether Bluetooth is enabled or disabled by the toggle, the getPairedDevices()
method is called in order to refresh the device list (i.e., if Bluetooth is enabled then the device list will be updated, and if it is disabled then the list content will be made empty).
UI for paired devices list
We will be using a DropdownButton widget for displaying the list of paired devices.
The code for it is:
// main.dart
// Inside _BluetoothAppState class
// Define this member variable for storing
// each device from the dropdown items
BluetoothDevice _device;
DropdownButton(
items: _getDeviceItems(),
onChanged: (value) =>
setState(() => _device = value),
value: _devicesList.isNotEmpty ? _device : null,
),
Define the method _getDeviceItems()
for storing the device names in a list. When there are no devices to store in the list, a text value, ‘NONE’, is stored as the only item.
// main.dart
// Inside _BluetoothAppState class
List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
List<DropdownMenuItem<BluetoothDevice>> items = [];
if (_devicesList.isEmpty) {
items.add(DropdownMenuItem(
child: Text('NONE'),
));
} else {
_devicesList.forEach((device) {
items.add(DropdownMenuItem(
child: Text(device.name),
value: device,
));
});
}
return items;
}
Connecting to a device via Bluetooth
Now we have come to the main part of the process: connecting to the Bluetooth device.
- Check if a device is selected from the list.
// main.dart // Inside _BluetoothAppState class void _connect() async { if (_device == null) { show('No device selected'); } else { // If a device is selected from the // dropdown, then use it here } }
- If a device is selected, then try to establish the connection.
// Define the following inside else block // of the previous // Making sure the device is not connected if (!isConnected) { // Trying to connect to the device using // its address await BluetoothConnection.toAddress(_device.address) .then((_connection) { print('Connected to the device'); connection = _connection; // Updating the device connectivity // status to [true] setState(() { _connected = true; }); // This is for tracking when the disconnecting process // is in progress which uses the [isDisconnecting] variable // defined before. // Whenever we make a disconnection call, this [onDone] // method is fired. connection.input.listen(null).onDone(() { if (isDisconnecting) { print('Disconnecting locally!'); } else { print('Disconnected remotely!'); } if (this.mounted) { setState(() {}); } }); }).catchError((error) { print('Cannot connect, exception occurred'); print(error); }); show('Device connected'); }
The listener is set to the connection to track it. This will be helpful while we try to disconnect from the device. TheonDone()
method will be triggered when thedispose()
,finish()
orclose()
method is called from the connection, all of which cause the connection to disconnect.
Disconnecting from the device
To disconnect from the Bluetooth device, we have to call the close()
method on the connection variable.
// main.dart
// Inside _BluetoothAppState class
void _disconnect() async {
// Closing the Bluetooth connection
await connection.close();
show('Device disconnected');
// Update the [_connected] variable
if (!connection.isConnected) {
setState(() {
_connected = false;
});
}
}
UI for the connect/disconnect button
The UI for the connect/disconnect button is very simple. The code for it is available below:
// main.dart
// Inside _BluetoothAppState class
RaisedButton(
onPressed: _isButtonUnavailable
? null
: _connected ? _disconnect : _connect,
child:
Text(_connected ? 'Disconnect' : 'Connect'),
)
Send messages to the Bluetooth device
In this app, we will only be adding on/off functionality, so that a user can turn on or turn off their Bluetooth device from the app.
For turning on the device, we will be sending “1” as the message, and for turning off the device, we will be sending “0” as the message.
The code for this functionality is available below:
// main.dart
// Inside _BluetoothAppState class
// Method to send message
// for turning the Bluetooth device on
void _sendOnMessageToBluetooth() async {
connection.output.add(utf8.encode("1" + "\r\n"));
await connection.output.allSent;
show('Device Turned On');
setState(() {
_deviceState = 1; // device on
});
}
// Method to send message
// for turning the Bluetooth device off
void _sendOffMessageToBluetooth() async {
connection.output.add(utf8.encode("0" + "\r\n"));
await connection.output.allSent;
show('Device Turned Off');
setState(() {
_deviceState = -1; // device off
});
}
UI for sending messages via Bluetooth
We will be using two buttons, one for sending an ON instruction to the device and the other one for sending an OFF instruction to the device.
The code for the UI is available below:
// main.dart
// Inside build method of
// _BluetoothAppState class
// ON button
FlatButton(
onPressed: _connected
? _sendOnMessageToBluetooth
: null,
child: Text("ON"),
),
// OFF button
FlatButton(
onPressed: _connected
? _sendOffMessageToBluetooth
: null,
child: Text("OFF"),
)
With this, all the main functionalities of our Bluetooth app are complete. There can be some UI improvements that can be made in the finished version of the app.
The whole Bluetooth app project is available on GitHub here.
Read full article here:
https://blog.codemagic.io/creating-iot-based-flutter-app/
Provides the list of the opensource Flutter apps collection with GitHub repository.