[Flutter]BLE - 3. 블루투스 기기 연결과 해제

한상욱·2023년 7월 25일
0

Flutter with Bluetooth

목록 보기
3/3
post-thumbnail

들어가며

지난번까지는 ESP32 디바이스의 Advertising, 플러터 앱에서 BLE 디바이스를 검색하는 것까지 했습니다. 이번에는 검색을 했으니, 연결을 해보도록 하겠습니다.

UI 구성

디바이스의 목록이 화면에 뜨죠? 각 목록을 옆으로 스와이프해서 연결할 수 있도록 할겁니다. 제가 제작한 화면은 아래와 같아요.

로직상의 에러때문에 연결 후 바로 연결할 수 있는 장비가 없다고 표시되고 있지만, 조만간 수정하겠습니다.

flutter_slidable 패키지는 아이폰처럼 스와이프되는 ListTile 위젯을 제공해요. 이를 이용할겁니다. 아래의 명령어를 프로젝트 루트에서 입력하면 다운받을 수 있습니다.

$ flutter pub add flutter_slidable

이제 이 패키지를 이용해서 화면에 구성되는 ListTile위젯을 제작할겁니다. connect_item.dart파일을 생성해서 새로운 컴포넌트를 구성했습니다.

import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_app/src/data/model/bluetooth_device_model.dart';
import 'package:flutter_slidable/flutter_slidable.dart';

class ConnectItem extends StatelessWidget {
  final DeviceModel data;

  final void Function(BuildContext)? disconnect;
  final void Function()? move;
  const ConnectItem(
      {super.key, required this.data, this.disconnect, this.move});

  
  Widget build(BuildContext context) {
    return Slidable(
      endActionPane: ActionPane(
        extentRatio: 0.3,
        motion: const DrawerMotion(),
        children: [
          SlidableAction(
            spacing: 2,
            padding: const EdgeInsets.all(8.0),
            onPressed: disconnect,
            backgroundColor: Colors.red,
            foregroundColor: Colors.white,
            icon: Icons.close,
            label: 'disconnect',
            borderRadius: BorderRadius.circular(12.0),
          ),
        ],
      ),
      child: Card(
        color: (data.isConnected) ? Colors.lightGreen : Colors.white,
        elevation: 5.0,
        shape:
            RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
        child: ListTile(
          onTap: move,
          leading: const Icon(
            Icons.bluetooth,
            color: Color(0xff03b6dc),
            size: 40,
          ),
          title: Text(
            (data.name.isEmpty) ? 'Unknown' : data.name,
            style: const TextStyle(
                fontFamily: 'Roboto',
                fontSize: 20,
                fontWeight: FontWeight.w400),
          ),
          subtitle: Text(data.id.toString()),
        ),
      ),
    );
  }
}

이제 이를 이용해서 랜더링되는 아이템들은 옆으로 스와이프해서 원하는 작업을 할 수 있습니다. 스와이프해서 나타나는 옵션은 연결과 해제입니다.

BLE 연결

BLE연결과 해제는 굉장히 간단합니다. 이미 Advertising하는 BLE 디바이스를 앱에서 발견했다면, 해당 디바이스의 device 정보만 알면 연결할 수 있습니다. 처음에 검색해서 발견되는 BLE 디바이스들은 모델을 이용해서 Factory method 패턴으로 구성하였습니다. 다시 한번 이해를 위해서 모델을 보겠습니다.

class DeviceModel {
  BluetoothDevice? device;
  late String name;
  late DeviceIdentifier id;
  late bool isConnected;

  DeviceModel(
      {required this.device,
      required this.name,
      required this.id,
      required this.isConnected});
	// 모델의 Named Constructor를 Factory 키워드로 구성
  factory DeviceModel.fromScan(ScanResult result) {
    return DeviceModel(
      device: result.device,
      name: result.device.name,
      id: result.device.id,
      isConnected: false,
    );
  }
}

이처럼 모델에는 ScanResult 클래스에는 BluetoothDevice클래스인 device 정보를 같이 담습니다. 그리고 이 device에는 connect메소드가 내장되어 있습니다. 해당 메소드를 이용해서 연결할 수 있어요. 해제도 마찬가지입니다. disconnect메소드가 내장되어 있습니다. 해당 기능을 이용해서 BLE와 연결 및 해제를 하는 모듈을 만들겠습니다.

class BluetoothApi {
  static Future<void> connectDevice(DeviceModel deviceModel) async {
    deviceModel.device!.connect();
  }

  static Future<void> disconnect(DeviceModel deviceModel) async {
    await deviceModel.device!.disconnect();
  }
}

이 모듈은 디바이스 모델을 받아서 연결을 수행합니다. 이 모듈 클래스는 컨트롤러에서 직접 호출해서 연결 및 해제를 할겁니다. 그리고 당연히 연결 및 해제를 진행하면서 디바이스 모델의 isConnected를 갱신하면서 사용자가 연결된 것을 인지할 수 있도록 색상을 바꾸는 작업을 추가했습니다. 아래는 연결함수를 호출하는 코드의 일부분입니다.

//컨트롤러에서 호출하는 연결함수
  Future<void> connectDevice(DeviceModel deviceModel) async {
    try {
      BluetoothApi.connectDevice(deviceModel).then((value) {
		...
        deviceModel.isConnected = true;
		...
      });
    } catch (e) {
      //예외 처리
      _showErrorToast();
    }
  }

원래는 가공 로직도 있지만, 편의상 생략하였습니다. 연결 해제도 비슷하게 구성할 수 있어요. 이제 완성입니다 !

profile
자기주도적, 지속 성장하는 모바일앱 개발자가 되기 위해

0개의 댓글