Scan, pair, connect, dan kirim/terima data via Bluetooth Classic di Flutter.
Terminalflutter create bt_led_control
cd bt_led_control
YAML — pubspec.yamldependencies:
flutter:
sdk: flutter
flutter_bluetooth_serial: ^0.4.0
AndroidManifest.xml:XML<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
BLUETOOTH_CONNECT dan BLUETOOTH_SCAN. Untuk Android < 12, butuh ACCESS_FINE_LOCATION.
Dart — lib/main.dartimport 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: const Color(0xFF0F172A)),
home: const BluetoothPage(),
);
}
}
class BluetoothPage extends StatefulWidget {
const BluetoothPage({super.key});
@override
State<BluetoothPage> createState() => _BluetoothPageState();
}
class _BluetoothPageState extends State<BluetoothPage> {
BluetoothConnection? _connection;
List<BluetoothDevice> _devices = [];
bool _isConnected = false;
bool _isLedOn = false;
String _status = 'Disconnected';
String _response = '';
@override
void initState() {
super.initState();
_getBondedDevices();
}
// Ambil daftar device yang sudah di-pair
Future<void> _getBondedDevices() async {
List<BluetoothDevice> devices =
await FlutterBluetoothSerial.instance.getBondedDevices();
setState(() => _devices = devices);
}
// Connect ke device
Future<void> _connect(BluetoothDevice device) async {
setState(() => _status = 'Connecting...');
try {
BluetoothConnection connection =
await BluetoothConnection.toAddress(device.address);
setState(() {
_connection = connection;
_isConnected = true;
_status = 'Connected to ${device.name}';
});
// Listen for incoming data
connection.input?.listen((Uint8List data) {
String incoming = String.fromCharCodes(data).trim();
if (incoming.isNotEmpty) {
setState(() => _response = incoming);
_parseResponse(incoming);
}
}).onDone(() {
setState(() {
_isConnected = false;
_status = 'Disconnected';
});
});
} catch (e) {
setState(() => _status = 'Error: $e');
}
}
void _parseResponse(String data) {
try {
var json = jsonDecode(data);
if (json['led'] != null) {
setState(() => _isLedOn = json['led'] == 'ON');
}
} catch (_) {}
}
void _sendCommand(String command) {
if (_connection == null) return;
String json = '{"command":"$command"}\n';
_connection!.output.add(Uint8List.fromList(json.codeUnits));
_connection!.output.allSent;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BT LED Controller'),
backgroundColor: const Color(0xFF1E293B),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _getBondedDevices,
),
],
),
body: _isConnected ? _buildControlView() : _buildDeviceList(),
);
}
// Daftar device bluetooth
Widget _buildDeviceList() {
return Column(children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(_status,
style: const TextStyle(color: Colors.grey)),
),
Expanded(
child: ListView.builder(
itemCount: _devices.length,
itemBuilder: (context, index) {
var device = _devices[index];
return ListTile(
leading: const Icon(Icons.bluetooth,
color: Colors.blueAccent),
title: Text(device.name ?? 'Unknown'),
subtitle: Text(device.address),
trailing: ElevatedButton(
onPressed: () => _connect(device),
child: const Text('Connect'),
),
);
},
),
),
]);
}
// Kontrol LED setelah connected
Widget _buildControlView() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_status,
style: const TextStyle(color: Colors.greenAccent)),
const SizedBox(height: 30),
Icon(Icons.lightbulb, size: 100,
color: _isLedOn ? Colors.amber : Colors.grey[700]),
const SizedBox(height: 20),
Text(_isLedOn ? 'LED: ON' : 'LED: OFF',
style: TextStyle(fontSize: 24,
fontWeight: FontWeight.bold,
color: _isLedOn
? Colors.greenAccent
: Colors.redAccent)),
const SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => _sendCommand('ON'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.greenAccent),
child: const Text('ON',
style: TextStyle(color: Colors.black)),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () => _sendCommand('OFF'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent),
child: const Text('OFF'),
),
],
),
const SizedBox(height: 20),
Text('Response: $_response',
style: const TextStyle(
color: Colors.grey, fontSize: 11)),
const SizedBox(height: 20),
TextButton(
onPressed: () {
_connection?.close();
setState(() {
_isConnected = false;
_status = 'Disconnected';
});
},
child: const Text('Disconnect',
style: TextStyle(color: Colors.redAccent)),
),
],
),
);
}
}
App flow: Device List → Connect → Control LED via Bluetooth