Flutter POSIX接口访问插件posix_sdk的使用

Flutter POSIX接口访问插件posix_sdk的使用

简介

Posix SDK 是一个为 Flutter 设计的蓝牙插件,帮助开发者构建现代多平台应用程序。 示例应用的 GitHub 链接:GitHub

跨平台低功耗蓝牙

Posix SDK 的目标是同时提供 iOS 和 Android 平台的最佳功能。

通过 Posix SDK 实例,您可以扫描并检查附近设备的 LED(通过 BluetoothDevice API)。

使用方法

获取实例
PosixSdk posixSdk = PosixSdk.instance;
检查设备的 LED
// 开始扫描
posixSdk.connect(device: result.device);

入门指南

更改 Android 的 minSdkVersion

Posix SDK 只兼容 Android SDK 版本 19 及以上。因此,您需要在 android/app/build.gradle 文件中更改此设置:

Android {
  defaultConfig {
     minSdkVersion 19
  }
}
添加蓝牙权限

我们需要添加使用蓝牙和访问位置的权限。

Android

android/app/src/main/AndroidManifest.xml 文件中添加以下内容:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
iOS

ios/Runner/Info.plist 文件中添加以下内容:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>Need BLE permission</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Need BLE permission</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need Location permission</string>

更多关于 iOS 位置权限的信息,请参阅 Apple 文档

完整示例代码

以下是完整的示例代码,位于 example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:posix_sdk/posix_sdk.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      color: Colors.lightBlue,
      home: StreamBuilder<BluetoothState>(
          stream: FlutterBlue.instance.state,
          initialData: BluetoothState.unknown,
          builder: (c, snapshot) {
            final state = snapshot.data;
            if (state == BluetoothState.on) {
              return ListDevicesScreen();
            }
            return BluetoothOffScreen(state: state);
          }),
    );
  }
}

class ListDevicesScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    PosixSdk posixSdk = PosixSdk.instance;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Find Devices'),
      ),
      body: RefreshIndicator(
        onRefresh: () =>
            FlutterBlue.instance.startScan(scanMode: ScanMode.opportunistic, timeout: const Duration(seconds: 30)),
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              StreamBuilder<List<ScanResult>>(
                stream: FlutterBlue.instance.scanResults,
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data!
                      .map(
                        (r) => ScanResultTile(
                      result: r,
                      onTap: () => {
                        FlutterBlue.instance.stopScan(),
                        posixSdk.connect(device: r.device)
                      },
                    ),
                  )
                      .toList(),
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: StreamBuilder<bool>(
        stream: FlutterBlue.instance.isScanning,
        initialData: false,
        builder: (c, snapshot) {
          if (snapshot.data!) {
            return FloatingActionButton(
              child: const Icon(Icons.stop),
              onPressed: () => FlutterBlue.instance.stopScan(),
              backgroundColor: Colors.red,
            );
          } else {
            return FloatingActionButton(
                child: const Icon(Icons.search),
                onPressed: () => FlutterBlue.instance
                    .startScan(timeout: const Duration(seconds: 60)));
          }
        },
      ),
    );
  }
}

class ScanResultTile extends StatelessWidget {
  const ScanResultTile({Key? key, required this.result, this.onTap})
      : super(key: key);

  final ScanResult result;
  final VoidCallback? onTap;

  Widget _buildTitle(BuildContext context) {
    if (result.device.name.length > 0) {
      return Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            result.device.name,
            overflow: TextOverflow.ellipsis,
          ),
          Text(
            result.device.id.toString(),
            style: Theme.of(context).textTheme.caption,
          )
        ],
      );
    } else {
      return Text(result.device.id.toString());
    }
  }

  Widget _buildAdvRow(BuildContext context, String title, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(title, style: Theme.of(context).textTheme.caption),
          const SizedBox(
            width: 12.0,
          ),
          Expanded(
            child: Text(
              value,
              style: Theme.of(context)
                  .textTheme
                  .caption
                  ?.apply(color: Colors.black),
              softWrap: true,
            ),
          ),
        ],
      ),
    );
  }

  String getNiceHexArray(List<int> bytes) {
    return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
        .toUpperCase();
  }

  String getNiceManufacturerData(Map<int, List<int>> data) {
    if (data.isEmpty) {
      return 'N/A';
    }
    List<String> res = [];
    data.forEach((id, bytes) {
      res.add(
          '${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
    });
    return res.join(', ');
  }

  String getNiceServiceData(Map<String, List<int>> data) {
    if (data.isEmpty) {
      return 'N/A';
    }
    List<String> res = [];
    data.forEach((id, bytes) {
      res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
    });
    return res.join(', ');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return ExpansionTile(
      title: _buildTitle(context),
      leading: Text(result.rssi.toString()),
      trailing: RaisedButton(
        child: const Text('LED'),
        color: Colors.black,
        textColor: Colors.white,
        onPressed: (result.advertisementData.connectable) ? onTap : null,
      ),
      children: <Widget>[
        _buildAdvRow(
            context, 'Complete Local Name', result.advertisementData.localName),
        _buildAdvRow(context, 'Tx Power Level',
            '${result.advertisementData.txPowerLevel ?? 'N/A'}'),
        _buildAdvRow(context, 'Manufacturer Data',
            getNiceManufacturerData(result.advertisementData.manufacturerData)),
        _buildAdvRow(
            context,
            'Service UUIDs',
            (result.advertisementData.serviceUuids.isNotEmpty)
                ? result.advertisementData.serviceUuids.join(', ').toUpperCase()
                : 'N/A'),
        _buildAdvRow(context, 'Service Data',
            getNiceServiceData(result.advertisementData.serviceData)),
      ],
    );
  }
}

class BluetoothOffScreen extends StatelessWidget {
  const BluetoothOffScreen({Key? key, this.state}) : super(key: key);

  final BluetoothState? state;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.lightBlue,
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            const Icon(
              Icons.bluetooth_disabled,
              size: 200.0,
              color: Colors.white54,
            ),
            Text(
              'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
              style: Theme.of(context)
                  .primaryTextTheme
                  .subtitle1
                  ?.copyWith(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter POSIX接口访问插件posix_sdk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter POSIX接口访问插件posix_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,如果你想访问POSIX接口(例如文件操作、进程控制、信号处理等),你可以使用posix_sdk插件。这个插件允许你在Flutter应用中调用POSIX系统调用和函数。以下是如何使用posix_sdk插件的基本步骤:

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加posix_sdk插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  posix_sdk: ^latest_version

然后运行flutter pub get来获取依赖。

2. 导入包

在你的Dart文件中导入posix_sdk包:

import 'package:posix_sdk/posix_sdk.dart';

3. 使用POSIX接口

posix_sdk插件提供了许多POSIX系统调用的封装。以下是一些常见的使用示例:

文件操作

你可以使用posix_sdk进行文件操作,比如打开、读取、写入文件等。

void main() async {
  // 打开文件
  int fd = Posix.open('example.txt', Posix.O_RDWR | Posix.O_CREAT, 0644);
  if (fd == -1) {
    print('Failed to open file');
    return;
  }

  // 写入文件
  String data = 'Hello, POSIX!';
  int bytesWritten = Posix.write(fd, data.codeUnits);
  print('$bytesWritten bytes written');

  // 读取文件
  var buffer = List<int>.filled(100, 0);
  int bytesRead = Posix.read(fd, buffer, 100);
  print('$bytesRead bytes read: ${String.fromCharCodes(buffer.sublist(0, bytesRead))}');

  // 关闭文件
  Posix.close(fd);
}

进程控制

你可以使用posix_sdk进行进程控制,比如创建子进程。

void main() async {
  int pid = Posix.fork();
  if (pid == 0) {
    // 子进程
    print('Child process');
    Posix.exit(0);
  } else if (pid > 0) {
    // 父进程
    print('Parent process');
    int status = 0;
    Posix.waitpid(pid, status, 0);
    print('Child process exited with status $status');
  } else {
    print('Fork failed');
  }
}

信号处理

你可以使用posix_sdk处理信号,比如捕获SIGINT信号。

void main() async {
  Posix.signal(Posix.SIGINT, (signal) {
    print('Received SIGINT signal');
    Posix.exit(0);
  });

  print('Waiting for SIGINT...');
  while (true) {
    Posix.sleep(1);
  }
}

4. 处理错误

在使用posix_sdk时,可能会遇到错误。你可以通过检查返回值来处理错误。例如,open函数在失败时会返回-1,你可以通过errno来获取错误代码。

int fd = Posix.open('example.txt', Posix.O_RDWR | Posix.O_CREAT, 0644);
if (fd == -1) {
  int err = Posix.errno;
  print('Failed to open file: ${Posix.strerror(err)}');
  return;
}
回到顶部