Flutter 为 NativeScript 提供了另一个用于创意视图开发的选项。让我们看看如何使用 @nativescript-community/ble 来丰富 Flutter 的蓝牙功能。
👉 演示仓库
这篇文章的灵感来自 Sovik Biswas 的探索 这里。
先决条件
无论您是否有现有的 Flutter 应用程序,或者只是想在整个 NativeScript 应用程序中混合使用不同的 Flutter 视图,我们都可以将它们用作 Flutter 模块。
您可以在任何现有的 NativeScript 应用程序中使用 Flutter,或者使用 ns create
创建一个新的应用程序。
然后,我们可以在项目目录的根目录下创建一个 Flutter 模块
flutter create --template module flutter_views
注意:您可以从这个 flutter_views
文件夹中运行 flutter run --debug
或 flutter build ios
,就像任何普通的 Flutter 项目一样进行开发。
从 Flutter 文档这里了解更多信息。
命名入口点允许我们通过将入口点与视图 ID 匹配,在我们的 NativeScript 应用程序中使用不同的 Flutter 视图,例如:<Flutter id="myFlutterView" />
main.dart
@pragma('vm:entry-point')
void myFlutterView() => runApp(const MyFlutterView());
为了简洁起见,我们将在本文中演示 iOS,但是您可以看到包含 Android 的 完整示例库。
App_Resources/iOS/Podfile
应该包含以下内容,以引用我们的 Flutter 模块。
platform :ios, '14.0'
flutter_application_path = '../../flutter_views'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
install_all_flutter_pods(flutter_application_path)
post_install do |installer|
flutter_post_install(installer) if defined?(flutter_post_install)
end
将 Flutter 调试权限添加到 App_Resources/iOS/Info.plist
<key>NSLocalNetworkUsageDescription</key>
<string>Allow Flutter tools to debug your views.</string>
<key>NSBonjourServices</key>
<array>
<string>_dartobservatory._tcp</string>
</array>
npm install @nativescript/flutter
Flutter
确保在引导您的应用程序之前初始化 Flutter 引擎,通常是在 app.ts
或 main.ts
中
import { init } from '@nativescript/flutter';
init();
// bootstrap app...
在任何地方使用 Flutter
。
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:ui="@nativescript/flutter">
<ui:Flutter id="myFlutterView"></ui:Flutter>
</Page>
当使用 flavors 时,您只需为您的标记注册元素以供使用
import { Flutter } from '@nativescript/flutter'
// Angular
import { registerElement } from '@nativescript/angular'
registerElement('Flutter', () => Flutter)
// Solid
import { registerElement } from 'dominative';
registerElement('flutter', Flutter);
// Svelte
import { registerNativeViewElement } from 'svelte-native/dom'
registerNativeViewElement('flutter', () => Flutter);
// React
import { registerElement } from 'react-nativescript';
registerElement('flutter', () => Flutter);
// Vue
import Vue from 'nativescript-vue'
Vue.registerElement('Flutter', () => Flutter)
我们将使用 平台通道 来设置 Flutter 和 NativeScript 之间的双向通信。
在我们的 Dart 代码中,我们只需要一个 nativescript
通道来处理我们可能想要的 任何 平台行为。对于 json 负载消息,我们将使用 BasicMessageChannel 和 StringCodec。我们也可以在我们的状态构造函数中设置消息处理程序。
main.dart
import 'package:flutter/services.dart';
import 'dart:convert';
class _MyPageState extends State<MyPage> {
static const platform = BasicMessageChannel('nativescript', StringCodec());
_MyPageState() {
// Receive platform messages from NativeScript
platform.setMessageHandler((String? message) async {
Map<String, dynamic> platformMessage = jsonDecode(message!);
switch (platformMessage['type']) {
case 'salutation':
// use any data from the platform
String hello = platformMessage['data'];
print(hello);
break;
}
return 'success';
});
}
}
ns-view.html
<Flutter id="myFlutterView" loaded="loadedFlutter"></Flutter>
ns-view.ts
let flutter: Flutter;
export function loadedFlutter(args) {
flutter = args.object;
// Send Flutter messages from NativeScript
flutter.sendMessage('salutation', 'hello');
}
npm install @nativescript-community/ble
将蓝牙权限添加到 App_Resources/iOS/Info.plist
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>bluetooth-le</string>
<string>location-services</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
<string>location</string>
</array>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Use Bluetooth to connect to your devices</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Use Bluetooth to connect to your devices</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Use location with Bluetooth devices</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Use location with Bluetooth devices</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Use location with Bluetooth devices</string>
设置蓝牙插件以开始/停止扫描,同时将发现的设备发送到 Flutter。
import { Utils } from "@nativescript/core";
import { Bluetooth, Peripheral } from "@nativescript-community/ble";
import { Flutter, FlutterChannelType } from "@nativescript/flutter";
let flutter: Flutter;
// Flutter can call NativeScript through these channel types
const channel: FlutterChannelType = {
startScanning: _startScanning,
stopScanning: _stopScanning,
};
const bluetooth: Bluetooth = new Bluetooth();
const discoveredPeripherals: Array<{ name: string; address: string }> = [];
// reduce extraneous bluetooth scan results when emitting to Flutter
const throttleScanResults = Utils.throttle(_throttleScanResults, 600);
function _throttleScanResults() {
flutter.sendMessage('scanResults', discoveredPeripherals);
}
function _startScanning() {
bluetooth.on(Bluetooth.device_discovered_event, result => {
const peripheral = <Peripheral>result.data;
if (peripheral?.name) {
if (!discoveredPeripherals.find((p) => p.address === peripheral.UUID)) {
discoveredPeripherals.push({
name: peripheral.name.trim(),
address: peripheral.UUID?.trim(),
});
}
throttleScanResults();
}
});
bluetooth.startScanning({});
}
function _stopScanning() {
bluetooth.stopScanning();
bluetooth.off(Bluetooth.device_discovered_event);
}
我们可以将 channel
绑定到我们的 Flutter 组件。这在 NativeScript 和 Flutter 之间创建了一个消息类型的契约以进行通信。
<Flutter id="myFlutterView" channel="{{channel}}" />
在我们的 main.dart
文件中,我们可以设置接收数据的消息类型契约。
_MyPageState() {
platform.setMessageHandler((String? message) async {
Map<String, dynamic> platformMessage = jsonDecode(message!);
switch (platformMessage['type']) {
case 'scanResults':
// prepare results for presentation in a ListView
List<BluetoothDevice> devices = platformMessage['data']
.map<BluetoothDevice>((i) => BluetoothDevice.fromJson(i))
.toList();
setState(() {
_devicesList = devices;
});
break;
}
return 'success';
});
}
// Bindable methods for Flutter to communicate to NativeScript
void stopScanning() {
Map<String, dynamic> message = {'type': 'stopScanning'};
platform.send(jsonEncode(message));
}
void startScanning() {
// List<BluetoothDevice> devices = [];
Map<String, dynamic> message = {'type': 'startScanning'};
platform.send(jsonEncode(message));
}
我们现在可以设置我们的 Widget 来绑定到我们的数据。而不是显示整个 Flutter Widget 树,您可以看到 完整的示例这里。
这篇文章的灵感很大程度上来自 Sovik Biswas 的出色探索 这里,您可以在其中挑战自己 与 Arduino 设备对话,以获得更有趣的功能。
就像 Open Native 为框架和平台开发者跨生态系统结合各自优势创造了可能性一样,Flutter 开发者也可以利用 NativeScript 插件来丰富项目可能性。
与同行合作是富有成效和有益的;无论他们的背景或技能如何。
向一个充满欢快合作的友好科技社区致敬 🌸