* 调整接口

* 临时提交

* 重构 Android 平台代码

* 临时提交

* 临时提交

* Android 6.0.0-dev.0

* 临时提交

* 实现 Windows 接口

* windows-6.0.0-dev.0

* Darwin 6.0.0-dev.0

* 临时提交

* 1

* 临时提交

* 调整接口

* windows-6.0.0-dev.1

* 临时提交

* interface-6.0.0-dev.7

* interface-6.0.0-dev.8

* 临时提交

* windows-6.0.0-dev.2

* 删除多余脚本

* interface-6.0.0-dev.9

* 临时提交

* 临时提交

* interface-6.0.0-dev.10

* android-6.0.0-dev.1

* windows-6.0.0-dev.3

* 临时提交

* interface-6.0.0-dev.11

* windows-6.0.0-dev.4

* 更新 pubspec.lock

* 1

* interface-6.0.0-dev.12

* interface-6.0.0-dev.13

* interface-6.0.0-dev.14

* 临时提交

* interface-6.0.0-dev.15

* 临时提交

* interface-6.0.0-dev.16

* android-6.0.0-dev.2

* 临时提交

* windows-6.0.0-dev.5

* 临时提交

* 临时提交

* windows-6.0.0-dev.6

* 优化注释和代码样式

* 优化代码

* 临时提交

* 实现 Dart 接口

* darwin-6.0.0-dev.0

* linux-6.0.0-dev.0

* 修复已知问题

* 修复问题

* 6.0.0-dev.0

* 修改包名

* 更新版本

* 移除原生部分

* 临时提交

* 修复问题

* 更新 pigeon 19.0.0

* 更新 README,添加迁移文档

* linux-6.0.0-dev.1

* 解析扫描回复和扩展广播

* 修复 googletest 版本警告问题

* Use centralArgs instead of addressArgs

* interface-6.0.0-dev.18

* android-6.0.0-dev.4

* linux-6.0.0-dev.2

* windows-6.0.0-dev.8

* darwin-6.0.0-dev.2

* 6.0.0-dev.1

* Update LICENSE

* clang-format

* Combine ADV_IND and SCAN_RES

* TEMP commit: update exampe

* Adjust advertisement combine logic

* Implement `MyPeripheralMananger` on Windows

* Added NuGet auto download and scan for names on peripheral (#67)

* fetch nuget using other technique

* move FetchContent to right location in CMakeLists.txt

* also added hash for googletest

---------

Co-authored-by: Kevin De Keyser <kevin@dekeyser.ch>

* Fix errors.

* Check BluetoothAdapter role supported state and implement PeripheralManager on Flutter side.

* Sort code

* Fix known errors

* interface-6.0.0-dev.19

* windows-6.0.0-dev.9

* Optimize example

* android-6.0.0-dev.5

* Optimize the Adverrtisement BottomSheet.

* linux-6.0.0-dev.3

* Update dependency

* Fix example errors.

* Temp commit.

* darwin-6.0.0-dev.3

* 6.0.0-dev.2

* Update README.md

* 6.0.0

* darwin-6.0.0-dev.4

* android-6.0.0-dev.6

* 6.0.0-dev.3

* Update docs.

* interface-6.0.0

* android-6.0.0

* darwin-6.0.0

* linux-6.0.0

* windows-6.0.0

* 6.0.0

* Update dependency

---------

Co-authored-by: Kevin De Keyser <dekeyser.kevin97@gmail.com>
Co-authored-by: Kevin De Keyser <kevin@dekeyser.ch>
This commit is contained in:
渐渐被你吸引
2024-06-04 00:44:39 +08:00
committed by GitHub
parent 71de531ceb
commit 108b6a804f
380 changed files with 23782 additions and 14127 deletions

View File

@ -27,7 +27,6 @@ migrate_working_dir/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/

View File

@ -1,6 +1,6 @@
# bluetooth_low_energy_example
# bluetooth_low_energy_darwin_example
Demonstrates how to use the bluetooth_low_energy plugin.
Demonstrates how to use the bluetooth_low_energy_darwin plugin.
## Getting Started

View File

@ -6,17 +6,8 @@
// For more information about Flutter integration tests, please see
// https://docs.flutter.dev/cookbook/testing/integration/introduction
// import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// testWidgets('getPlatformVersion test', (WidgetTester tester) async {
// final BluetoothLowEnergy plugin = BluetoothLowEnergy();
// final String? version = await plugin.getPlatformVersion();
// // The version string depends on the host platform running the test, so
// // just assert that some non-empty string is returned.
// expect(version?.isNotEmpty, true);
// });
}

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -1,5 +1,5 @@
PODS:
- bluetooth_low_energy_darwin (2.0.2):
- bluetooth_low_energy_darwin (0.0.1):
- Flutter
- FlutterMacOS
- Flutter (1.0.0)
@ -20,10 +20,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/integration_test/ios"
SPEC CHECKSUMS:
bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
integration_test: 13825b8a9334a850581300559b8839134b124670
bluetooth_low_energy_darwin: 764d8d1ae5abefbcdb839e812b4b25c0061fcf8b
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
COCOAPODS: 1.14.3
COCOAPODS: 1.15.2

View File

@ -9,13 +9,13 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
43A108903466BBFDEDE4985F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C27D70257F13C733066C27B6 /* Pods_Runner.framework */; };
5A4AF0726D433429B5CD6958 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -42,21 +42,19 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2EE38EE18344248FD19187BD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
7BB1125E379070B4AAAFC962 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
7CC3DA72E0F6DAA790F643B9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -64,38 +62,31 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
C27D70257F13C733066C27B6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6B5302E24483403072236BBB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */,
43A108903466BBFDEDE4985F /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D87DCF5A67E56FE2ED284031 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5A4AF0726D433429B5CD6958 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0FAACD3CD9E188E482384358 /* Frameworks */ = {
isa = PBXGroup;
children = (
2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */,
076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@ -104,17 +95,13 @@
path = RunnerTests;
sourceTree = "<group>";
};
3C9A3D0714D61C07F02BFBB4 /* Pods */ = {
711971ED4BF1C4587FC0E521 /* Frameworks */ = {
isa = PBXGroup;
children = (
93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */,
4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */,
03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */,
9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */,
54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */,
710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */,
C27D70257F13C733066C27B6 /* Pods_Runner.framework */,
F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */,
);
path = Pods;
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
@ -135,8 +122,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
3C9A3D0714D61C07F02BFBB4 /* Pods */,
0FAACD3CD9E188E482384358 /* Frameworks */,
E27789BD215E61248833A6B2 /* Pods */,
711971ED4BF1C4587FC0E521 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -164,6 +151,19 @@
path = Runner;
sourceTree = "<group>";
};
E27789BD215E61248833A6B2 /* Pods */ = {
isa = PBXGroup;
children = (
7CC3DA72E0F6DAA790F643B9 /* Pods-Runner.debug.xcconfig */,
7BB1125E379070B4AAAFC962 /* Pods-Runner.release.xcconfig */,
2EE38EE18344248FD19187BD /* Pods-Runner.profile.xcconfig */,
5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */,
D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */,
519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -171,10 +171,10 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */,
7BD6B85155ECEFD741D916A4 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
6B5302E24483403072236BBB /* Frameworks */,
D87DCF5A67E56FE2ED284031 /* Frameworks */,
);
buildRules = (
);
@ -190,14 +190,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */,
07FF42C617DF912EF8EA67AC /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */,
C9B7C0BA95EE7285CDB16832 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -215,7 +215,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
@ -269,40 +269,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */ = {
07FF42C617DF912EF8EA67AC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -324,22 +291,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Run Script";
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */ = {
7BD6B85155ECEFD741D916A4 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -361,6 +329,38 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C9B7C0BA95EE7285CDB16832 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -415,6 +415,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -444,6 +445,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -452,7 +454,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -468,14 +470,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = T5YVNX2NAC;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -485,14 +487,14 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -503,14 +505,14 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -519,14 +521,14 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */;
baseConfigurationReference = 519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -537,6 +539,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -566,6 +569,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -580,7 +584,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -592,6 +596,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -621,6 +626,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -629,7 +635,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -647,14 +653,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = T5YVNX2NAC;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -670,14 +676,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = T5YVNX2NAC;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,5 +1,5 @@
import UIKit
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {

View File

@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Bluetooth Low Energy</string>
<string>Bluetooth Low Energy Darwin</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -15,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>bluetooth_low_energy_example</string>
<string>bluetooth_low_energy_darwin_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@ -27,7 +27,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Hello Bluetooth LowEnergy!</string>
<string>Need to use the bluetooth to communicate with peripherals</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>

View File

@ -2,7 +2,7 @@ import Flutter
import UIKit
import XCTest
@testable import bluetooth_low_energy
@testable import bluetooth_low_energy_darwin
// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
//
@ -11,7 +11,7 @@ import XCTest
class RunnerTests: XCTestCase {
func testGetPlatformVersion() {
let plugin = BluetoothLowEnergyPlugin()
let plugin = BluetoothLowEnergyDarwinPlugin()
let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
export 'models/log.dart';

View File

@ -0,0 +1,10 @@
class Log {
final DateTime time;
final String type;
final String message;
Log({
required this.type,
required this.message,
}) : time = DateTime.now();
}

View File

@ -0,0 +1,85 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:clover/clover.dart';
import 'package:collection/collection.dart';
import 'package:go_router/go_router.dart';
import 'view_models.dart';
import 'views.dart';
final routerConfig = GoRouter(
redirect: (context, state) {
if (state.matchedLocation == '/') {
return '/central';
}
return null;
},
routes: [
StatefulShellRoute(
// builder: (context, state, navigationShell) {
// return HomeView(navigationShell: navigationShell);
// },
builder: (context, state, navigationShell) => navigationShell,
navigatorContainerBuilder: (context, navigationShell, children) {
final navigators = children.mapIndexed(
(index, element) {
if (index == 0) {
return ViewModelBinding(
viewBuilder: (context) => element,
viewModelBuilder: (context) => CentralManagerViewModel(),
);
} else {
return element;
}
},
).toList();
return HomeView(
navigationShell: navigationShell,
navigators: navigators,
);
},
branches: [
StatefulShellBranch(
routes: [
GoRoute(
path: '/central',
builder: (context, state) {
return const CentralManagerView();
},
routes: [
GoRoute(
path: ':uuid',
builder: (context, state) {
final uuidValue = state.pathParameters['uuid']!;
final uuid = UUID.fromString(uuidValue);
final viewModel =
ViewModel.of<CentralManagerViewModel>(context);
final eventArgs = viewModel.discoveries.firstWhere(
(discovery) => discovery.peripheral.uuid == uuid);
return ViewModelBinding(
viewBuilder: (context) => PeripheralView(),
viewModelBuilder: (context) =>
PeripheralViewModel(eventArgs),
);
},
),
],
),
],
),
StatefulShellBranch(
routes: [
GoRoute(
path: '/peripheral',
builder: (context, state) {
return ViewModelBinding(
viewBuilder: (context) => const PeripheralManagerView(),
viewModelBuilder: (context) => PeripheralManagerViewModel(),
);
},
),
],
),
],
),
],
);

View File

@ -0,0 +1,6 @@
export 'view_models/central_manager_view_model.dart';
export 'view_models/peripheral_view_model.dart';
export 'view_models/service_view_model.dart';
export 'view_models/characteristic_view_model.dart';
export 'view_models/descriptor_view_model.dart';
export 'view_models/peripheral_manager_view_model.dart';

View File

@ -0,0 +1,71 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:clover/clover.dart';
import 'package:logging/logging.dart';
class CentralManagerViewModel extends ViewModel {
final CentralManager _manager;
final List<DiscoveredEventArgs> _discoveries;
bool _discovering;
late final StreamSubscription _stateChangedSubscription;
late final StreamSubscription _discoveredSubscription;
CentralManagerViewModel()
: _manager = CentralManager()..logLevel = Level.INFO,
_discoveries = [],
_discovering = false {
_stateChangedSubscription = _manager.stateChanged.listen((eventArgs) {
notifyListeners();
});
_discoveredSubscription = _manager.discovered.listen((eventArgs) {
final peripheral = eventArgs.peripheral;
final index = _discoveries.indexWhere((i) => i.peripheral == peripheral);
if (index < 0) {
_discoveries.add(eventArgs);
} else {
_discoveries[index] = eventArgs;
}
notifyListeners();
});
}
BluetoothLowEnergyState get state => _manager.state;
bool get discovering => _discovering;
List<DiscoveredEventArgs> get discoveries => _discoveries;
Future<void> showAppSettings() async {
await _manager.showAppSettings();
}
Future<void> startDiscovery({
List<UUID>? serviceUUIDs,
}) async {
if (_discovering) {
return;
}
_discoveries.clear();
await _manager.startDiscovery(
serviceUUIDs: serviceUUIDs,
);
_discovering = true;
notifyListeners();
}
Future<void> stopDiscovery() async {
if (!_discovering) {
return;
}
await _manager.stopDiscovery();
_discovering = false;
notifyListeners();
}
@override
void dispose() {
_stateChangedSubscription.cancel();
_discoveredSubscription.cancel();
super.dispose();
}
}

View File

@ -0,0 +1,145 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluetooth_low_energy_darwin_example/models.dart';
import 'package:clover/clover.dart';
import 'descriptor_view_model.dart';
class CharacteristicViewModel extends ViewModel {
final CentralManager _manager;
final Peripheral _peripheral;
final GATTCharacteristic _characteristic;
final List<DescriptorViewModel> _descriptorViewModels;
final List<Log> _logs;
GATTCharacteristicWriteType _writeType;
bool _notifyState;
late final StreamSubscription _characteristicNotifiedSubscription;
CharacteristicViewModel({
required CentralManager manager,
required Peripheral peripheral,
required GATTCharacteristic characteristic,
}) : _manager = manager,
_peripheral = peripheral,
_characteristic = characteristic,
_descriptorViewModels = characteristic.descriptors
.map((descriptor) => DescriptorViewModel(descriptor))
.toList(),
_logs = [],
_writeType = GATTCharacteristicWriteType.withResponse,
_notifyState = false {
if (!canWrite && canWriteWithoutResponse) {
_writeType = GATTCharacteristicWriteType.withoutResponse;
}
_characteristicNotifiedSubscription =
_manager.characteristicNotified.listen((eventArgs) {
if (eventArgs.characteristic != _characteristic) {
return;
}
final log = Log(
type: 'Notified',
message: '[${eventArgs.value.length}] ${eventArgs.value}',
);
_logs.add(log);
notifyListeners();
});
}
UUID get uuid => _characteristic.uuid;
bool get canRead =>
_characteristic.properties.contains(GATTCharacteristicProperty.read);
bool get canWrite =>
_characteristic.properties.contains(GATTCharacteristicProperty.write);
bool get canWriteWithoutResponse => _characteristic.properties
.contains(GATTCharacteristicProperty.writeWithoutResponse);
bool get canNotify =>
_characteristic.properties.contains(GATTCharacteristicProperty.notify) ||
_characteristic.properties.contains(GATTCharacteristicProperty.indicate);
List<DescriptorViewModel> get descriptorViewModels => _descriptorViewModels;
List<Log> get logs => _logs;
GATTCharacteristicWriteType get writeType => _writeType;
bool get notifyState => _notifyState;
Future<void> read() async {
final value = await _manager.readCharacteristic(
_peripheral,
_characteristic,
);
final log = Log(
type: 'Read',
message: '[${value.length}] $value',
);
_logs.add(log);
notifyListeners();
}
void setWriteType(GATTCharacteristicWriteType type) {
if (type == GATTCharacteristicWriteType.withResponse && !canWrite) {
throw ArgumentError.value(type);
}
if (type == GATTCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse) {
throw ArgumentError.value(type);
}
_writeType = type;
notifyListeners();
}
Future<void> write(Uint8List value) async {
// Fragments the value by maximumWriteLength.
final fragmentSize = await _manager.getMaximumWriteLength(
_peripheral,
type: writeType,
);
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue =
end < value.length ? value.sublist(start, end) : value.sublist(start);
final type = writeType;
await _manager.writeCharacteristic(
_peripheral,
_characteristic,
value: fragmentedValue,
type: type,
);
final log = Log(
type: type == GATTCharacteristicWriteType.withResponse
? 'Write'
: 'Write without response',
message: '[${value.length}] $value',
);
_logs.add(log);
notifyListeners();
start = end;
}
}
Future<void> setNotifyState(bool state) async {
await _manager.setCharacteristicNotifyState(
_peripheral,
_characteristic,
state: state,
);
_notifyState = state;
notifyListeners();
}
void clearLogs() {
_logs.clear();
notifyListeners();
}
@override
void dispose() {
_characteristicNotifiedSubscription.cancel();
for (var descriptorViewModel in descriptorViewModels) {
descriptorViewModel.dispose();
}
super.dispose();
}
}

View File

@ -0,0 +1,10 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:clover/clover.dart';
class DescriptorViewModel extends ViewModel {
final GATTDescriptor _descriptor;
DescriptorViewModel(this._descriptor);
UUID get uuid => _descriptor.uuid;
}

View File

@ -0,0 +1,161 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluetooth_low_energy_darwin_example/models.dart';
import 'package:clover/clover.dart';
import 'package:logging/logging.dart';
class PeripheralManagerViewModel extends ViewModel {
final PeripheralManager _manager;
final List<Log> _logs;
bool _advertising;
late final StreamSubscription _stateChangedSubscription;
late final StreamSubscription _characteristicReadRequestedSubscription;
late final StreamSubscription _characteristicWriteRequestedSubscription;
late final StreamSubscription _characteristicNotifyStateChangedSubscription;
PeripheralManagerViewModel()
: _manager = PeripheralManager()..logLevel = Level.INFO,
_logs = [],
_advertising = false {
_stateChangedSubscription = _manager.stateChanged.listen((eventArgs) {
notifyListeners();
});
_characteristicReadRequestedSubscription =
_manager.characteristicReadRequested.listen((eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final request = eventArgs.request;
final offset = request.offset;
final log = Log(
type: 'Characteristic read requested',
message: '${central.uuid}, ${characteristic.uuid}, $offset',
);
_logs.add(log);
notifyListeners();
final elements = List.generate(100, (i) => i % 256);
final value = Uint8List.fromList(elements);
final trimmedValue = value.sublist(offset);
await _manager.respondReadRequestWithValue(
request,
value: trimmedValue,
);
});
_characteristicWriteRequestedSubscription =
_manager.characteristicWriteRequested.listen((eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final request = eventArgs.request;
final offset = request.offset;
final value = request.value;
final log = Log(
type: 'Characteristic write requested',
message:
'[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value',
);
_logs.add(log);
notifyListeners();
await _manager.respondWriteRequest(request);
});
_characteristicNotifyStateChangedSubscription =
_manager.characteristicNotifyStateChanged.listen((eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final state = eventArgs.state;
final log = Log(
type: 'Characteristic notify state changed',
message: '${central.uuid}, ${characteristic.uuid}, $state',
);
_logs.add(log);
notifyListeners();
// Write someting to the central when notify started.
if (state) {
final maximumNotifyLength =
await _manager.getMaximumNotifyLength(central);
final elements = List.generate(maximumNotifyLength, (i) => i % 256);
final value = Uint8List.fromList(elements);
await _manager.notifyCharacteristic(
central,
characteristic,
value: value,
);
}
});
}
BluetoothLowEnergyState get state => _manager.state;
bool get advertising => _advertising;
List<Log> get logs => _logs;
Future<void> showAppSettings() async {
await _manager.showAppSettings();
}
Future<void> startAdvertising() async {
if (_advertising) {
return;
}
await _manager.removeAllServices();
final elements = List.generate(100, (i) => i % 256);
final value = Uint8List.fromList(elements);
final service = GATTService(
uuid: UUID.short(100),
isPrimary: true,
includedServices: [],
characteristics: [
GATTCharacteristic.immutable(
uuid: UUID.short(200),
value: value,
descriptors: [],
),
GATTCharacteristic.mutable(
uuid: UUID.short(201),
properties: [
GATTCharacteristicProperty.read,
GATTCharacteristicProperty.write,
GATTCharacteristicProperty.writeWithoutResponse,
GATTCharacteristicProperty.notify,
GATTCharacteristicProperty.indicate,
],
permissions: [
GATTCharacteristicPermission.read,
GATTCharacteristicPermission.write,
],
descriptors: [],
),
],
);
await _manager.addService(service);
final advertisement = Advertisement(
name: 'BLE-12138',
);
await _manager.startAdvertising(advertisement);
_advertising = true;
notifyListeners();
}
Future<void> stopAdvertising() async {
if (!_advertising) {
return;
}
await _manager.stopAdvertising();
_advertising = false;
notifyListeners();
}
void clearLogs() {
_logs.clear();
notifyListeners();
}
@override
void dispose() {
_stateChangedSubscription.cancel();
_characteristicReadRequestedSubscription.cancel();
_characteristicWriteRequestedSubscription.cancel();
_characteristicNotifyStateChangedSubscription.cancel();
super.dispose();
}
}

View File

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:clover/clover.dart';
import 'package:hybrid_logging/hybrid_logging.dart';
import 'service_view_model.dart';
class PeripheralViewModel extends ViewModel with TypeLogger {
final CentralManager _manager;
final Peripheral _peripheral;
final String? _name;
bool _connected;
List<ServiceViewModel> _serviceViewModels;
late final StreamSubscription _connectionStateChangedSubscription;
PeripheralViewModel(DiscoveredEventArgs eventArgs)
: _manager = CentralManager(),
_peripheral = eventArgs.peripheral,
_name = eventArgs.advertisement.name,
_connected = false,
_serviceViewModels = [] {
_connectionStateChangedSubscription =
_manager.connectionStateChanged.listen((eventArgs) {
if (eventArgs.peripheral != _peripheral) {
return;
}
if (eventArgs.state == ConnectionState.connected) {
_connected = true;
} else {
_connected = false;
_serviceViewModels = [];
}
notifyListeners();
});
}
UUID get uuid => _peripheral.uuid;
String? get name => _name;
bool get connected => _connected;
List<ServiceViewModel> get serviceViewModels => _serviceViewModels;
Future<void> connect() async {
await _manager.connect(_peripheral);
}
Future<void> disconnect() async {
await _manager.disconnect(_peripheral);
}
Future<void> discoverGATT() async {
final services = await _manager.discoverGATT(_peripheral);
_serviceViewModels = services
.map((service) => ServiceViewModel(
manager: _manager,
peripheral: _peripheral,
service: service,
))
.toList();
notifyListeners();
}
@override
void dispose() {
if (connected) {
disconnect();
}
_connectionStateChangedSubscription.cancel();
for (var serviceViewModel in serviceViewModels) {
serviceViewModel.dispose();
}
super.dispose();
}
}

View File

@ -0,0 +1,46 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:clover/clover.dart';
import 'characteristic_view_model.dart';
class ServiceViewModel extends ViewModel {
final GATTService _service;
final List<ServiceViewModel> _includedServiceViewModels;
final List<CharacteristicViewModel> _characteristicViewModels;
ServiceViewModel({
required CentralManager manager,
required Peripheral peripheral,
required GATTService service,
}) : _service = service,
_includedServiceViewModels = service.includedServices
.map((service) => ServiceViewModel(
manager: manager,
peripheral: peripheral,
service: service,
))
.toList(),
_characteristicViewModels = service.characteristics
.map((characteristic) => CharacteristicViewModel(
manager: manager,
peripheral: peripheral,
characteristic: characteristic,
))
.toList();
UUID get uuid => _service.uuid;
bool get isPrimary => _service.isPrimary;
List<ServiceViewModel> get includedServiceViewModels =>
_includedServiceViewModels;
List<CharacteristicViewModel> get characteristicViewModels =>
_characteristicViewModels;
@override
void dispose() {
for (var characteristicViewModel in characteristicViewModels) {
characteristicViewModel.dispose();
}
super.dispose();
}
}

View File

@ -0,0 +1,4 @@
export 'views/home_view.dart';
export 'views/central_manager_view.dart';
export 'views/peripheral_manager_view.dart';
export 'views/peripheral_view.dart';

View File

@ -0,0 +1,69 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:convert/convert.dart';
import 'package:flutter/material.dart';
class AdvertisementView extends StatelessWidget {
final Advertisement advertisement;
const AdvertisementView({
super.key,
required this.advertisement,
});
@override
Widget build(BuildContext context) {
final manufacturerSpecificData = advertisement.manufacturerSpecificData;
return LayoutBuilder(
builder: (context, constraints) {
const idWidth = 100.0;
final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0;
return DataTable(
columnSpacing: 0.0,
horizontalMargin: 16.0,
columns: [
DataColumn(
label: Container(
width: idWidth,
alignment: Alignment.center,
child: const Text('Id'),
),
),
DataColumn(
label: Container(
width: valueWidth,
alignment: Alignment.center,
child: const Text('Value'),
),
),
],
rows: manufacturerSpecificData.map((item) {
final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}';
final value = hex.encode(item.data);
return DataRow(
cells: [
DataCell(
Container(
width: idWidth,
alignment: Alignment.center,
child: Text(id),
),
),
DataCell(
Container(
width: valueWidth,
alignment: Alignment.center,
child: Text(
value,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
],
);
}).toList(),
);
},
);
}
}

View File

@ -0,0 +1,126 @@
import 'dart:io';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:bluetooth_low_energy_darwin_example/widgets.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'advertisement_view.dart';
class CentralManagerView extends StatelessWidget {
const CentralManagerView({super.key});
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<CentralManagerViewModel>(context);
final state = viewModel.state;
final discovering = viewModel.discovering;
return Scaffold(
appBar: AppBar(
title: const Text('Central Manager'),
actions: [
TextButton(
onPressed: state == BluetoothLowEnergyState.poweredOn
? () async {
if (discovering) {
await viewModel.stopDiscovery();
} else {
await viewModel.startDiscovery();
}
}
: null,
child: Text(discovering ? 'END' : 'BEGIN'),
),
],
),
body: buildBody(context),
);
}
Widget buildBody(BuildContext context) {
final viewModel = ViewModel.of<CentralManagerViewModel>(context);
final state = viewModel.state;
if (state == BluetoothLowEnergyState.unauthorized && Platform.isIOS) {
return Center(
child: TextButton(
onPressed: () => viewModel.showAppSettings(),
child: const Text('Go to settings'),
),
);
} else if (state == BluetoothLowEnergyState.poweredOn) {
final discoveries = viewModel.discoveries;
return ListView.separated(
itemBuilder: (context, index) {
final theme = Theme.of(context);
final discovery = discoveries[index];
final uuid = discovery.peripheral.uuid;
final name = discovery.advertisement.name;
final rssi = discovery.rssi;
return ListTile(
onTap: () {
onTapDissovery(context, discovery);
},
onLongPress: () {
onLongPressDiscovery(context, discovery);
},
title: Text(name ?? ''),
subtitle: Text(
'$uuid',
style: theme.textTheme.bodySmall,
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RSSIIndicator(rssi),
Text('$rssi'),
],
),
);
},
separatorBuilder: (context, i) {
return const Divider(
height: 0.0,
);
},
itemCount: discoveries.length,
);
} else {
return Center(
child: Text(
'$state',
style: Theme.of(context).textTheme.titleMedium,
),
);
}
}
void onTapDissovery(
BuildContext context, DiscoveredEventArgs discovery) async {
final viewModel = ViewModel.of<CentralManagerViewModel>(context);
if (viewModel.discovering) {
await viewModel.stopDiscovery();
if (!context.mounted) {
return;
}
}
final uuid = discovery.peripheral.uuid;
context.go('/central/$uuid');
}
void onLongPressDiscovery(
BuildContext context, DiscoveredEventArgs discovery) async {
await showModalBottomSheet(
context: context,
builder: (context) {
return AdvertisementView(
advertisement: discovery.advertisement,
);
},
);
}
}

View File

@ -0,0 +1,18 @@
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
class CharacteristicTreeNodeView extends StatelessWidget {
const CharacteristicTreeNodeView({super.key});
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<CharacteristicViewModel>(context);
return Text(
'${viewModel.uuid}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
);
}
}

View File

@ -0,0 +1,178 @@
import 'dart:convert';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'log_view.dart';
class CharacteristicView extends StatefulWidget {
const CharacteristicView({super.key});
@override
State<CharacteristicView> createState() => _CharacteristicViewState();
}
class _CharacteristicViewState extends State<CharacteristicView> {
late final TextEditingController _textController;
@override
void initState() {
super.initState();
_textController = TextEditingController();
}
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<CharacteristicViewModel>(context);
final logs = viewModel.logs;
return Column(
children: [
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
return OverflowBox(
alignment: Alignment.topCenter,
maxHeight: constraints.maxHeight + 1.0,
child: Row(
children: [
Expanded(
child: InputDecorator(
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(12.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.vertical(
top: const Radius.circular(12.0),
bottom: viewModel.canWrite ||
viewModel.canWriteWithoutResponse
? Radius.zero
: const Radius.circular(12.0),
),
),
),
child: ListView.builder(
itemBuilder: (context, i) {
final log = logs[i];
return LogView(
log: log,
);
},
itemCount: logs.length,
),
),
),
Column(
children: [
Visibility(
visible: viewModel.canNotify,
child: viewModel.notifyState
? IconButton.filled(
onPressed: () async {
await viewModel.setNotifyState(false);
},
icon:
const Icon(Symbols.notifications_active),
)
: IconButton.filledTonal(
onPressed: () async {
await viewModel.setNotifyState(true);
},
icon: const Icon(Symbols.notifications_off),
),
),
Visibility(
visible: viewModel.canRead,
child: IconButton.filled(
onPressed: () async {
await viewModel.read();
},
icon: const Icon(Symbols.arrow_downward),
),
),
Visibility(
visible: viewModel.canWrite ||
viewModel.canWriteWithoutResponse,
child: viewModel.writeType ==
GATTCharacteristicWriteType.withResponse
? IconButton.filled(
onPressed: viewModel.canWriteWithoutResponse
? () {
viewModel.setWriteType(
GATTCharacteristicWriteType
.withoutResponse);
}
: null,
icon: const Icon(Symbols.swap_vert),
)
: IconButton.filledTonal(
onPressed: viewModel.canWrite
? () {
viewModel.setWriteType(
GATTCharacteristicWriteType
.withResponse);
}
: null,
icon: const Icon(Symbols.arrow_upward),
),
),
IconButton.filled(
onPressed: () => viewModel.clearLogs(),
icon: const Icon(Symbols.delete),
),
],
),
],
),
);
},
),
),
Visibility(
visible: viewModel.canWrite || viewModel.canWriteWithoutResponse,
child: Row(
children: [
Expanded(
child: TextField(
controller: _textController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(12.0),
),
),
),
),
),
ValueListenableBuilder(
valueListenable: _textController,
builder: (context, tev, child) {
final text = tev.text;
return IconButton.filled(
onPressed: text.isEmpty
? null
: () async {
final value = utf8.encode(text);
await viewModel.write(value);
},
icon: const Icon(Symbols.pets),
);
},
),
],
),
),
],
);
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,18 @@
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
class DescriptorTreeNodeView extends StatelessWidget {
const DescriptorTreeNodeView({super.key});
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<DescriptorViewModel>(context);
return Text(
'${viewModel.uuid}',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.tertiary,
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class HomeView extends StatefulWidget {
final StatefulNavigationShell navigationShell;
final List<Widget> navigators;
const HomeView({
super.key,
required this.navigationShell,
required this.navigators,
});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
late final PageController _controller;
@override
void initState() {
super.initState();
_controller = PageController(
initialPage: widget.navigationShell.currentIndex,
);
}
@override
Widget build(BuildContext context) {
final navigationShell = widget.navigationShell;
final navigators = widget.navigators;
return Scaffold(
// body: navigationShell,
body: PageView.builder(
controller: _controller,
onPageChanged: (index) {
// Ignore tap events.
if (index == navigationShell.currentIndex) {
return;
}
navigationShell.goBranch(
index,
initialLocation: false,
);
},
itemBuilder: (context, index) {
return navigators[index];
},
itemCount: navigators.length,
),
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
navigationShell.goBranch(
index,
initialLocation: index == navigationShell.currentIndex,
);
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.radar),
label: 'Central',
),
BottomNavigationBarItem(
icon: Icon(Icons.sensors),
label: 'Peripheral',
),
],
currentIndex: widget.navigationShell.currentIndex,
),
);
}
@override
void didUpdateWidget(covariant HomeView oldWidget) {
super.didUpdateWidget(oldWidget);
final navigationShell = widget.navigationShell;
final page = _controller.page ?? _controller.initialPage;
final index = page.round();
// Ignore swipe events.
if (index == navigationShell.currentIndex) {
return;
}
_controller.animateToPage(
navigationShell.currentIndex,
duration: const Duration(milliseconds: 300),
curve: Curves.linear,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,43 @@
import 'package:bluetooth_low_energy_darwin_example/models.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class LogView extends StatelessWidget {
final Log log;
const LogView({
super.key,
required this.log,
});
@override
Widget build(BuildContext context) {
final formatter = DateFormat.Hms();
final time = formatter.format(log.time);
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
text: '[$time] ',
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.error,
),
children: [
TextSpan(
text: log.type,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
)),
],
),
),
Text(
log.message,
style: Theme.of(context).textTheme.bodySmall,
),
],
);
}
}

View File

@ -0,0 +1,78 @@
import 'dart:io';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'log_view.dart';
class PeripheralManagerView extends StatelessWidget {
const PeripheralManagerView({super.key});
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<PeripheralManagerViewModel>(context);
final state = viewModel.state;
final advertising = viewModel.advertising;
return Scaffold(
appBar: AppBar(
title: const Text('Peripheral Manager'),
actions: [
TextButton(
onPressed: state == BluetoothLowEnergyState.poweredOn
? () async {
if (advertising) {
await viewModel.stopAdvertising();
} else {
await viewModel.startAdvertising();
}
}
: null,
child: Text(advertising ? 'END' : 'BEGIN'),
),
],
),
body: buildBody(context),
floatingActionButton: state == BluetoothLowEnergyState.poweredOn
? FloatingActionButton(
onPressed: () => viewModel.clearLogs(),
child: const Icon(Symbols.delete),
)
: null,
);
}
Widget buildBody(BuildContext context) {
final viewModel = ViewModel.of<PeripheralManagerViewModel>(context);
final state = viewModel.state;
if (state == BluetoothLowEnergyState.unauthorized && Platform.isIOS) {
return Center(
child: TextButton(
onPressed: () => viewModel.showAppSettings(),
child: const Text('Go to settings'),
),
);
} else if (state == BluetoothLowEnergyState.poweredOn) {
final logs = viewModel.logs;
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
final log = logs[i];
return LogView(
log: log,
);
},
itemCount: logs.length,
);
} else {
return Center(
child: Text(
'$state',
style: Theme.of(context).textTheme.titleMedium,
),
);
}
}
}

View File

@ -0,0 +1,107 @@
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
import 'package:flutter_simple_treeview/flutter_simple_treeview.dart';
import 'characteristic_tree_node_view.dart';
import 'characteristic_view.dart';
import 'descriptor_tree_node_view.dart';
import 'service_tree_node_view.dart';
class PeripheralView extends StatelessWidget {
final TreeController _treeController;
PeripheralView({super.key})
: _treeController = TreeController(
allNodesExpanded: false,
);
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<PeripheralViewModel>(context);
final connected = viewModel.connected;
final serviceViewModels = viewModel.serviceViewModels;
return Scaffold(
appBar: AppBar(
title: Text(viewModel.name ?? '${viewModel.uuid}'),
actions: [
TextButton(
onPressed: () async {
if (connected) {
await viewModel.disconnect();
} else {
await viewModel.connect();
await viewModel.discoverGATT();
}
},
child: Text(connected ? 'DISCONNECT' : 'CONNECT'),
),
],
),
body: SingleChildScrollView(
child: TreeView(
treeController: _treeController,
indent: 0.0,
nodes: _buildServiceTreeNodes(serviceViewModels),
),
),
);
}
List<TreeNode> _buildServiceTreeNodes(List<ServiceViewModel> viewModels) {
return viewModels.map((viewModel) {
final includedServiceViewModels = viewModel.includedServiceViewModels;
final characteristicViewModels = viewModel.characteristicViewModels;
return TreeNode(
children: [
..._buildServiceTreeNodes(includedServiceViewModels),
..._buildCharacteristicTreeNodes(characteristicViewModels),
],
content: InheritedViewModel(
view: const ServiceTreeNodeView(),
viewModel: viewModel,
),
);
}).toList();
}
List<TreeNode> _buildCharacteristicTreeNodes(
List<CharacteristicViewModel> viewModels) {
return viewModels.map((viewModel) {
final descriptorViewModels = viewModel.descriptorViewModels;
return TreeNode(
children: [
TreeNode(
content: Expanded(
child: Container(
margin: const EdgeInsets.only(right: 40.0),
height: 360.0,
child: InheritedViewModel(
view: const CharacteristicView(),
viewModel: viewModel,
),
),
),
),
..._buildDescriptorTreeNodes(descriptorViewModels),
],
content: InheritedViewModel(
view: const CharacteristicTreeNodeView(),
viewModel: viewModel,
),
);
}).toList();
}
List<TreeNode> _buildDescriptorTreeNodes(
List<DescriptorViewModel> viewModels) {
return viewModels.map((viewModel) {
return TreeNode(
content: InheritedViewModel(
view: const DescriptorTreeNodeView(),
viewModel: viewModel,
),
);
}).toList();
}
}

View File

@ -0,0 +1,18 @@
import 'package:bluetooth_low_energy_darwin_example/view_models.dart';
import 'package:clover/clover.dart';
import 'package:flutter/material.dart';
class ServiceTreeNodeView extends StatelessWidget {
const ServiceTreeNodeView({super.key});
@override
Widget build(BuildContext context) {
final viewModel = ViewModel.of<ServiceViewModel>(context);
return Text(
'${viewModel.uuid}',
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
);
}
}

View File

@ -0,0 +1 @@
export 'widgets/rssi_indicator.dart';

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
class RSSIIndicator extends StatelessWidget {
final int rssi;
const RSSIIndicator(
this.rssi, {
super.key,
});
@override
Widget build(BuildContext context) {
final IconData icon;
if (rssi > -70) {
icon = Icons.wifi_rounded;
} else if (rssi > -100) {
icon = Icons.wifi_2_bar_rounded;
} else {
icon = Icons.wifi_1_bar_rounded;
}
return Icon(icon);
}
}

View File

@ -8,5 +8,5 @@ import Foundation
import bluetooth_low_energy_darwin
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
BluetoothLowEnergyDarwin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwin"))
BluetoothLowEnergyDarwinPlugin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwinPlugin"))
}

View File

@ -1,5 +1,5 @@
PODS:
- bluetooth_low_energy_darwin (2.0.2):
- bluetooth_low_energy_darwin (0.0.1):
- Flutter
- FlutterMacOS
- FlutterMacOS (1.0.0)
@ -15,9 +15,9 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
SPEC CHECKSUMS:
bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7
bluetooth_low_energy_darwin: 764d8d1ae5abefbcdb839e812b4b25c0061fcf8b
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.14.3
COCOAPODS: 1.15.2

View File

@ -21,14 +21,14 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */; };
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */; };
56BC448E9AA045CEEA9F18AE /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */; };
E85B965AEA257F11EDEFD117 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -62,14 +62,11 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bluetooth_low_energy_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bluetooth_low_energy_darwin_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -81,13 +78,16 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
48CF260037ECCE45E257099A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
B2647444F8BBDB6D18347354 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
DCB648C2118B8E673D1A04D0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -95,7 +95,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */,
56BC448E9AA045CEEA9F18AE /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -103,13 +103,27 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */,
E85B965AEA257F11EDEFD117 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
12C065AA4D4AEFAE0CFF1E11 /* Pods */ = {
isa = PBXGroup;
children = (
B2647444F8BBDB6D18347354 /* Pods-Runner.debug.xcconfig */,
48CF260037ECCE45E257099A /* Pods-Runner.release.xcconfig */,
DCB648C2118B8E673D1A04D0 /* Pods-Runner.profile.xcconfig */,
90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */,
498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */,
91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@ -137,14 +151,14 @@
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
5413C8972DC878C53291A3D1 /* Pods */,
12C065AA4D4AEFAE0CFF1E11 /* Pods */,
);
sourceTree = "<group>";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */,
33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
@ -185,24 +199,11 @@
path = Runner;
sourceTree = "<group>";
};
5413C8972DC878C53291A3D1 /* Pods */ = {
isa = PBXGroup;
children = (
2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */,
69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */,
032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */,
42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */,
7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */,
BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */,
12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */,
481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */,
3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -214,7 +215,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */,
58DC822B5511057672D5EA36 /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
@ -233,13 +234,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */,
02F0ADF4750BC0D2334D6BF0 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */,
42F1C37024EDD17206C3254E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -248,7 +249,7 @@
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */;
productReference = 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@ -257,8 +258,9 @@
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
@ -321,7 +323,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */ = {
02F0ADF4750BC0D2334D6BF0 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -336,7 +338,7 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@ -381,29 +383,7 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */ = {
42F1C37024EDD17206C3254E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -420,6 +400,28 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
58DC822B5511057672D5EA36 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -471,46 +473,46 @@
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example";
};
name = Debug;
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = 498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example";
};
name = Release;
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */;
baseConfigurationReference = 91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example";
};
name = Profile;
};
@ -519,6 +521,7 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -542,9 +545,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -592,6 +597,7 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -615,9 +621,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -645,6 +653,7 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@ -668,9 +677,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "bluetooth_low_energy_example.app"
BuildableName = "bluetooth_low_energy_darwin_example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@ -31,7 +31,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "bluetooth_low_energy_example.app"
BuildableName = "bluetooth_low_energy_darwin_example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@ -65,7 +65,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "bluetooth_low_energy_example.app"
BuildableName = "bluetooth_low_energy_darwin_example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@ -82,7 +82,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "bluetooth_low_energy_example.app"
BuildableName = "bluetooth_low_energy_darwin_example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>

View File

@ -5,10 +5,10 @@
// 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = bluetooth_low_energy_example
PRODUCT_NAME = bluetooth_low_energy_darwin_example
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample
PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 dev.yanshouwang. All rights reserved.
PRODUCT_COPYRIGHT = Copyright © 2024 dev.hebei. All rights reserved.

View File

@ -22,8 +22,6 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Hello Bluetooth LowEnergy!</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>

View File

@ -1,8 +1,8 @@
import FlutterMacOS
import Cocoa
import FlutterMacOS
import XCTest
@testable import bluetooth_low_energy
@testable import bluetooth_low_energy_darwin
// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
//
@ -11,7 +11,7 @@ import XCTest
class RunnerTests: XCTestCase {
func testGetPlatformVersion() {
let plugin = BluetoothLowEnergyPlugin()
let plugin = BluetoothLowEnergyDarwinPlugin()
let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])

View File

@ -15,15 +15,15 @@ packages:
path: ".."
relative: true
source: path
version: "5.0.5"
version: "6.0.0"
bluetooth_low_energy_platform_interface:
dependency: "direct main"
description:
name: bluetooth_low_energy_platform_interface
sha256: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7"
sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a
url: "https://pub.dev"
source: hosted
version: "5.0.2"
version: "6.0.0"
boolean_selector:
dependency: transitive
description:
@ -48,8 +48,16 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
clover:
dependency: "direct main"
description:
name: clover
sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487
url: "https://pub.dev"
source: hosted
version: "3.0.0"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
@ -68,10 +76,10 @@ packages:
dependency: "direct main"
description:
name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.6"
version: "1.0.8"
fake_async:
dependency: transitive
description:
@ -102,7 +110,15 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_simple_treeview:
dependency: "direct main"
description:
name: flutter_simple_treeview
sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9
url: "https://pub.dev"
source: hosted
version: "3.0.2"
@ -111,11 +127,32 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
go_router:
dependency: "direct main"
description:
name: go_router
sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86"
url: "https://pub.dev"
source: hosted
version: "14.1.3"
hybrid_logging:
dependency: "direct main"
description:
name: hybrid_logging
sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
integration_test:
dependency: "direct dev"
description: flutter
@ -133,42 +170,34 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
log_service:
dependency: transitive
description:
name: log_service
sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "4.0.0"
logging:
dependency: "direct main"
description:
@ -193,14 +222,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.8.0"
material_symbols_icons:
dependency: "direct main"
description:
name: material_symbols_icons
sha256: b2d3cbc3c42b8a217715b0d97ff03aebb14b2b4592875736e5599c603fb2db7e
url: "https://pub.dev"
source: hosted
version: "4.2758.0"
meta:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.0"
path:
dependency: transitive
description:
@ -290,10 +327,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
typed_data:
dependency: transitive
description:
@ -314,10 +351,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "13.0.0"
version: "14.2.1"
webdriver:
dependency: transitive
description:
@ -327,5 +364,5 @@ packages:
source: hosted
version: "3.0.3"
sdks:
dart: ">=3.2.0-0 <4.0.0"
flutter: ">=3.0.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -1,5 +1,5 @@
name: bluetooth_low_energy_example
description: Demonstrates how to use the bluetooth_low_energy plugin.
name: bluetooth_low_energy_darwin_example
description: "Demonstrates how to use the bluetooth_low_energy_darwin plugin."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@ -17,10 +17,10 @@ dependencies:
flutter:
sdk: flutter
bluetooth_low_energy_platform_interface: ^5.0.0
bluetooth_low_energy_platform_interface: ^6.0.0
bluetooth_low_energy_darwin:
# When depending on this package from a real application you should use:
# bluetooth_low_energy: ^x.y.z
# bluetooth_low_energy_darwin: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
@ -28,10 +28,16 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
convert: ^3.1.1
intl: ^0.19.0
cupertino_icons: ^1.0.6
clover: ^3.0.0
go_router: ^14.1.3
logging: ^1.2.0
hybrid_logging: ^1.0.0
intl: ^0.19.0
collection: ^1.18.0
convert: ^3.1.1
flutter_simple_treeview: ^3.0.2
material_symbols_icons: ^4.2744.0
dev_dependencies:
integration_test:
@ -44,7 +50,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
flutter_lints: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -5,23 +5,4 @@
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bluetooth_low_energy_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}
void main() {}