├── ios ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterBluePeripheralPlugin.h │ ├── FlutterBluePeripheralPlugin.m │ └── SwiftFlutterBluePeripheralPlugin.swift ├── .gitignore └── flutter_blue_peripheral.podspec ├── android ├── settings.gradle ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ └── net │ │ └── tsukumijima │ │ └── flutter_blue_peripheral │ │ └── FlutterBluePeripheralPlugin.kt └── build.gradle ├── example ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── GeneratedPluginRegistrant.h │ │ ├── GeneratedPluginRegistrant.m │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── Podfile.lock │ ├── .gitignore │ └── Podfile ├── android │ ├── .gradle │ │ └── buildOutputCleanup │ │ │ └── cache.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── net │ │ │ │ │ │ └── tsukumijima │ │ │ │ │ │ └── flutter_blue_peripheral_example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ ├── java │ │ │ │ │ └── io │ │ │ │ │ │ └── flutter │ │ │ │ │ │ └── plugins │ │ │ │ │ │ └── GeneratedPluginRegistrant.java │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── .gitignore │ ├── .idea │ │ ├── misc.xml │ │ ├── gradle.xml │ │ └── modules.xml │ ├── gradle.properties │ ├── build.gradle │ ├── settings.gradle │ ├── gradlew.bat │ └── gradlew ├── .idea │ ├── runConfigurations │ │ └── main_dart.xml │ ├── modules.xml │ ├── libraries │ │ └── Dart_SDK.xml │ └── workspace.xml ├── README.md ├── .gitignore ├── analysis_options.yaml ├── lib │ └── main.dart ├── pubspec.yaml └── pubspec.lock ├── analysis_options.yaml ├── .editorconfig ├── .gitignore ├── License.txt ├── .metadata ├── CHANGELOG.md ├── lib └── flutter_blue_peripheral.dart ├── pubspec.yaml ├── Readme.md └── pubspec.lock /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_blue_peripheral' 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/android/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon May 26 16:23:52 JST 2025 2 | gradle.version=8.4 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Classes/FlutterBluePeripheralPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FlutterBluePeripheralPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/flutter_blue_peripheral/master/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/net/tsukumijima/flutter_blue_peripheral_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package net.tsukumijima.flutter_blue_peripheral_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | trim_trailing_whitespace = true 10 | indent_size = 4 11 | 12 | [*.dart] 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/GeneratedPluginRegistrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GeneratedPluginRegistrant_h 8 | #define GeneratedPluginRegistrant_h 9 | 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface GeneratedPluginRegistrant : NSObject 15 | + (void)registerWithRegistry:(NSObject*)registry; 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | #endif /* GeneratedPluginRegistrant_h */ 20 | -------------------------------------------------------------------------------- /example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /example/ios/Runner/GeneratedPluginRegistrant.m: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #import "GeneratedPluginRegistrant.h" 8 | 9 | #if __has_include() 10 | #import 11 | #else 12 | @import flutter_blue_peripheral; 13 | #endif 14 | 15 | @implementation GeneratedPluginRegistrant 16 | 17 | + (void)registerWithRegistry:(NSObject*)registry { 18 | [FlutterBluePeripheralPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterBluePeripheralPlugin"]]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_blue_peripheral (0.1.0): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - flutter_blue_peripheral (from `.symlinks/plugins/flutter_blue_peripheral/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | flutter_blue_peripheral: 14 | :path: ".symlinks/plugins/flutter_blue_peripheral/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 18 | flutter_blue_peripheral: c496678d4d17ef206672736158b00435e0afe14e 19 | 20 | PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 21 | 22 | COCOAPODS: 1.16.2 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_blue_peripheral_example 2 | 3 | Demonstrates how to use the flutter_blue_peripheral plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8192M -XX:MaxMetaspaceSize=512m 2 | android.useAndroidX=true 3 | android.enableJetifier=false 4 | 5 | # Enable Gradle build cache for faster builds 6 | org.gradle.caching=true 7 | 8 | # Enable parallel builds 9 | org.gradle.parallel=true 10 | 11 | # Enable configuration on demand to improve build performance 12 | org.gradle.configureondemand=true 13 | 14 | # Use full classpath for dexing transform if needed 15 | android.useFullClasspathForDexingTransform=true 16 | 17 | # Enable R8 optimizations 18 | android.enableR8.fullMode=true 19 | 20 | # Disable unnecessary features for better performance 21 | android.nonTransitiveRClass=true 22 | android.nonFinalResIds=true 23 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Classes/FlutterBluePeripheralPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterBluePeripheralPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "flutter_blue_peripheral-Swift.h" 9 | #endif 10 | 11 | @implementation FlutterBluePeripheralPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftFlutterBluePeripheralPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.21' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.3.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.layout.buildDirectory 31 | } 32 | -------------------------------------------------------------------------------- /example/android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.3.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.21" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | import io.flutter.Log; 6 | 7 | import io.flutter.embedding.engine.FlutterEngine; 8 | 9 | /** 10 | * Generated file. Do not edit. 11 | * This file is generated by the Flutter tool based on the 12 | * plugins that support the Android platform. 13 | */ 14 | @Keep 15 | public final class GeneratedPluginRegistrant { 16 | private static final String TAG = "GeneratedPluginRegistrant"; 17 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 18 | try { 19 | flutterEngine.getPlugins().add(new net.tsukumijima.flutter_blue_peripheral.FlutterBluePeripheralPlugin()); 20 | } catch (Exception e) { 21 | Log.e(TAG, "Error registering plugin flutter_blue_peripheral, net.tsukumijima.flutter_blue_peripheral.FlutterBluePeripheralPlugin", e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ios/flutter_blue_peripheral.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_blue_peripheral.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_blue_peripheral' 7 | s.version = '0.1.0' 8 | s.summary = 'Flutter Bluetooth LE (BLE) Peripheral Plugin.' 9 | s.description = <<-DESC 10 | Flutter Bluetooth LE (BLE) Peripheral Plugin. 11 | DESC 12 | s.homepage = 'https://github.com/tsukumijima/flutter_blue_peripheral' 13 | s.license = { :file => '../License.txt' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '9.0' 19 | 20 | # Flutter.framework does not contain a i386 slice. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 22 | s.swift_version = '5.0' 23 | end 24 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'net.tsukumijima.flutter_blue_peripheral' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.9.21' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:8.3.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | namespace 'net.tsukumijima.flutter_blue_peripheral' 29 | compileSdkVersion 34 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_17 33 | targetCompatibility JavaVersion.VERSION_17 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '17' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | minSdkVersion 21 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2020 frcc00 4 | Copyright (c) 2022 tsukumi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 8 | channel: unknown 9 | 10 | project_type: plugin 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 17 | base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 18 | - platform: android 19 | create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 20 | base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 21 | - platform: ios 22 | create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 23 | base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c 24 | 25 | # User provided section 26 | 27 | # List of Local paths (relative to this file) that should be 28 | # ignored by the migrate tool. 29 | # 30 | # Files that are not part of the templates will be ignored by default. 31 | unmanaged_files: 32 | - 'lib/main.dart' 33 | - 'ios/Runner.xcodeproj/project.pbxproj' 34 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.0 - 2025-05-20 4 | 5 | ### 🚀 Major Updates 6 | - **Flutter 3.32.0 Support**: Updated to support the latest Flutter version 7 | - **Android Gradle Plugin 8.3.0**: Migrated from AGP 7.1.2 to 8.3.0 8 | - **Gradle 8.4**: Updated from Gradle 7.4 to 8.4 9 | - **Java 17**: Migrated from Java 8 to Java 17 10 | - **Kotlin 1.9.21**: Updated from Kotlin 1.6.10 to 1.9.21 11 | 12 | ### 🔧 Technical Improvements 13 | - **New Plugin System**: Migrated to Flutter's declarative plugin system 14 | - **Namespace Migration**: Updated Android configuration to use `namespace` instead of `package` attribute 15 | - **Jetifier Disabled**: Removed Jetifier dependency as it's no longer needed with AndroidX 16 | - **Build Performance**: Optimized Gradle configuration for faster builds 17 | - **Memory Management**: Improved JVM memory settings to prevent build failures 18 | 19 | ### 📱 Platform Updates 20 | - **Android**: Updated compileSdk and targetSdk to 34 21 | - **iOS**: Maintained compatibility with iOS 12.0+ 22 | - **Dependencies**: Updated flutter_lints to 4.0.0 23 | 24 | ### 🛠️ Build Configuration 25 | - Added proper namespace configuration for Android 26 | - Updated gradle.properties with performance optimizations 27 | - Migrated to new Flutter plugin loader system 28 | - Enhanced error handling and build stability 29 | 30 | ### ⚠️ Breaking Changes 31 | - Minimum Flutter version is now 3.16.0 32 | - Minimum Dart version is now 3.2.0 33 | - Android projects must use AGP 8.3.0 or later 34 | - Java 17 is now required for Android development 35 | 36 | ## 0.1.0 37 | 38 | * Initial release with basic BLE peripheral functionality 39 | -------------------------------------------------------------------------------- /example/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/android/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Blue Peripheral 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_blue_peripheral_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | NSBluetoothPeripheralUsageDescription 28 | Lorem ipsum 29 | NSBluetoothAlwaysUsageDescription 30 | Lorem ipsum 31 | UIBackgroundModes 32 | 33 | bluetooth-central 34 | bluetooth-peripheral 35 | 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | CADisableMinimumFrameDurationOnPhone 56 | 57 | UIApplicationSupportsIndirectInputEvents 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /lib/flutter_blue_peripheral.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | import 'package:flutter/services.dart'; 4 | 5 | class FlutterBluePeripheral { 6 | 7 | static const MethodChannel _channel = MethodChannel('flutter_blue_peripheral'); 8 | 9 | static Future init({ 10 | required Function didReceiveRead, 11 | required Function didReceiveWrite, 12 | required Function didSubscribeTo, 13 | required Function didUnsubscribeFrom, 14 | required Function peripheralManagerDidUpdateState, 15 | }) async { 16 | _channel.setMethodCallHandler((MethodCall call) { 17 | if (call.method == 'didReceiveRead') { 18 | return Future(() { 19 | return didReceiveRead(call); 20 | }); 21 | } 22 | if (call.method == 'didReceiveWrite') { 23 | return Future(() { 24 | return didReceiveWrite(call); 25 | }); 26 | } 27 | if (call.method == 'didSubscribeTo') { 28 | return Future(() { 29 | return didSubscribeTo(call); 30 | }); 31 | } 32 | if (call.method == 'didUnsubscribeFrom') { 33 | return Future(() { 34 | return didUnsubscribeFrom(call); 35 | }); 36 | } 37 | if (call.method == 'peripheralManagerDidUpdateState') { 38 | return Future(() { 39 | return peripheralManagerDidUpdateState(call); 40 | }); 41 | } 42 | return Future(() {}); 43 | }); 44 | } 45 | 46 | static Future peripheralUpdateValue({ 47 | required String centralUuid, 48 | required String characteristicUuid, 49 | required Uint8List data, 50 | }) async { 51 | await _channel.invokeMethod('peripheralUpdateValue', { 52 | 'centralUuid': centralUuid, 53 | 'characteristicUuid': characteristicUuid, 54 | 'data': data, 55 | }); 56 | } 57 | 58 | static Future startPeripheral({ 59 | required String serviceUuid, 60 | required String characteristicUuid, 61 | }) async { 62 | await _channel.invokeMethod('startPeripheral', { 63 | 'serviceUuid': serviceUuid, 64 | 'characteristicUuid': characteristicUuid, 65 | }); 66 | } 67 | 68 | static void stopPeripheral(){ 69 | _channel.invokeMethod('stopPeripheral'); 70 | } 71 | 72 | static Future get platformVersion async { 73 | final String version = await _channel.invokeMethod('getPlatformVersion'); 74 | return version; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterRoot = localProperties.getProperty('flutter.sdk') 16 | if (flutterRoot == null) { 17 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 18 | } 19 | 20 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 21 | if (flutterVersionCode == null) { 22 | flutterVersionCode = '1' 23 | } 24 | 25 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 26 | if (flutterVersionName == null) { 27 | flutterVersionName = '1.0' 28 | } 29 | 30 | android { 31 | namespace = "net.tsukumijima.flutter_blue_peripheral_example" 32 | compileSdk flutter.compileSdkVersion 33 | ndkVersion flutter.ndkVersion 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_17 37 | targetCompatibility JavaVersion.VERSION_17 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '17' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | } 47 | 48 | defaultConfig { 49 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 50 | applicationId "net.tsukumijima.flutter_blue_peripheral_example" 51 | // You can update the following values to match your application needs. 52 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 53 | minSdk flutter.minSdkVersion 54 | targetSdk flutter.targetSdkVersion 55 | versionCode flutterVersionCode.toInteger() 56 | versionName flutterVersionName 57 | } 58 | 59 | buildTypes { 60 | release { 61 | // TODO: Add your own signing config for the release build. 62 | // Signing with the debug keys for now, so `flutter run --release` works. 63 | signingConfig signingConfigs.debug 64 | } 65 | } 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 74 | } 75 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_blue_peripheral 2 | description: Flutter Bluetooth LE (BLE) Peripheral Plugin. 3 | version: 0.2.0 4 | homepage: https://github.com/tsukumijima/flutter_blue_peripheral 5 | 6 | environment: 7 | sdk: '>=3.2.0 <4.0.0' 8 | flutter: ">=3.16.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | plugin_platform_interface: ^2.1.3 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | flutter_lints: ^4.0.0 19 | 20 | # For information on the generic Dart part of this file, see the 21 | # following page: https://dart.dev/tools/pub/pubspec 22 | 23 | # The following section is specific to Flutter packages. 24 | flutter: 25 | # This section identifies this Flutter project as a plugin project. 26 | # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) 27 | # which should be registered in the plugin registry. This is required for 28 | # using method channels. 29 | # The Android 'package' specifies package in which the registered class is. 30 | # This is required for using method channels on Android. 31 | # The 'ffiPlugin' specifies that native code should be built and bundled. 32 | # This is required for using `dart:ffi`. 33 | # All these are used by the tooling to maintain consistency when 34 | # adding or updating assets for this project. 35 | plugin: 36 | platforms: 37 | android: 38 | package: net.tsukumijima.flutter_blue_peripheral 39 | pluginClass: FlutterBluePeripheralPlugin 40 | ios: 41 | pluginClass: FlutterBluePeripheralPlugin 42 | 43 | # To add assets to your plugin package, add an assets section, like this: 44 | # assets: 45 | # - images/a_dot_burr.jpeg 46 | # - images/a_dot_ham.jpeg 47 | # 48 | # For details regarding assets in packages, see 49 | # https://flutter.dev/assets-and-images/#from-packages 50 | # 51 | # An image asset can refer to one or more resolution-specific "variants", see 52 | # https://flutter.dev/assets-and-images/#resolution-aware 53 | 54 | # To add custom fonts to your plugin package, add a fonts section here, 55 | # in this "flutter" section. Each entry in this list should have a 56 | # "family" key with the font family name, and a "fonts" key with a 57 | # list giving the asset and other descriptors for the font. For 58 | # example: 59 | # fonts: 60 | # - family: Schyler 61 | # fonts: 62 | # - asset: fonts/Schyler-Regular.ttf 63 | # - asset: fonts/Schyler-Italic.ttf 64 | # style: italic 65 | # - family: Trajan Pro 66 | # fonts: 67 | # - asset: fonts/TrajanPro.ttf 68 | # - asset: fonts/TrajanPro_Bold.ttf 69 | # weight: 700 70 | # 71 | # For details regarding fonts in packages, see 72 | # https://flutter.dev/custom-fonts/#from-packages 73 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_blue_peripheral/flutter_blue_peripheral.dart'; 6 | 7 | void main() { 8 | runApp(const MyApp()); 9 | } 10 | 11 | class MyApp extends StatefulWidget { 12 | const MyApp({super.key}); 13 | 14 | @override 15 | State createState() => _MyAppState(); 16 | } 17 | 18 | class _MyAppState extends State { 19 | String _platformVersion = 'Unknown'; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | initPlatformState(); 25 | } 26 | 27 | // Platform messages are asynchronous, so we initialize in an async method. 28 | Future initPlatformState() async { 29 | String platformVersion; 30 | // Platform messages may fail, so we use a try/catch PlatformException. 31 | // We also handle the message potentially returning null. 32 | try { 33 | platformVersion = await FlutterBluePeripheral.platformVersion ?? 'Unknown platform version'; 34 | } on PlatformException { 35 | platformVersion = 'Failed to get platform version.'; 36 | } 37 | 38 | FlutterBluePeripheral.init(didReceiveRead: (MethodCall call) { 39 | print(call.arguments); 40 | return Uint8List.fromList([11,2,3,4,5,6,7,8,9,10]); 41 | }, didReceiveWrite: (MethodCall call) { 42 | FlutterBluePeripheral.peripheralUpdateValue( 43 | centralUuid: call.arguments["centralUuid"], 44 | characteristicUuid: call.arguments["characteristicUuid"], 45 | data: Uint8List.fromList([11,2,3]), 46 | ); 47 | print(call.arguments); 48 | }, didSubscribeTo: (MethodCall call) { 49 | print(call.arguments); 50 | FlutterBluePeripheral.peripheralUpdateValue( 51 | centralUuid: call.arguments["centralUuid"], 52 | characteristicUuid: call.arguments["characteristicUuid"], 53 | data: Uint8List.fromList([11,2,3,4,5,6,7,8,9,10,11,2,3]), 54 | ); 55 | }, didUnsubscribeFrom: (MethodCall call) { 56 | print(call.arguments); 57 | }, peripheralManagerDidUpdateState: (MethodCall call) { 58 | print(call.arguments); 59 | }); 60 | 61 | FlutterBluePeripheral.startPeripheral( 62 | serviceUuid: "00000000-0000-0000-0000-AAAAAAAAAAA1", 63 | characteristicUuid: "00000000-0000-0000-0000-AAAAAAAAAAA2", 64 | ).then((_){}); 65 | 66 | // If the widget was removed from the tree while the asynchronous platform 67 | // message was in flight, we want to discard the reply rather than calling 68 | // setState to update our non-existent appearance. 69 | if (!mounted) return; 70 | 71 | setState(() { 72 | _platformVersion = platformVersion; 73 | }); 74 | } 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return MaterialApp( 79 | home: Scaffold( 80 | appBar: AppBar( 81 | title: const Text('Plugin example app'), 82 | ), 83 | body: Center( 84 | child: Text('Running on: $_platformVersion\n'), 85 | ), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # flutter_blue_peripheral 2 | 3 | Flutter Bluetooth LE (BLE) Peripheral Plugin. 4 | 5 | ## Supported platforms 6 | 7 | iOS / Android 8 | 9 | ## Requirements 10 | 11 | - Flutter 3.16.0 or later 12 | - Dart 3.2.0 or later 13 | - Android: API level 21 or later, AGP 8.3.0, Gradle 8.4 14 | - iOS: iOS 12.0 or later 15 | 16 | ## How to use 17 | 18 | ```yaml 19 | dependencies: 20 | flutter_blue_peripheral: 21 | git: 22 | url: https://github.com/tsukumijima/flutter_blue_peripheral 23 | ``` 24 | 25 | ```dart 26 | import 'package:flutter_blue_peripheral/flutter_blue_peripheral.dart'; 27 | 28 | // initialize 29 | FlutterBluePeripheral.init(didReceiveRead:(MethodCall call){ 30 | print(call.arguments); 31 | return Uint8List.fromList([11,2,3,4,5,6,7,8,9,10,]); 32 | }, didReceiveWrite:(MethodCall call){ 33 | print(call.arguments); 34 | },didSubscribeTo: (MethodCall call){ 35 | print(call.arguments); 36 | // FlutterBluePeripheral.peripheralUpdateValue() 37 | },didUnsubscribeFrom: (MethodCall call){ 38 | print(call.arguments); 39 | },peripheralManagerDidUpdateState: (MethodCall call){ 40 | print(call.arguments); 41 | }); 42 | 43 | 44 | // start peripheral server 45 | FlutterBluePeripheral.startPeripheral("00000000-0000-0000-0000-AAAAAAAAAAA1", "00000000-0000-0000-0000-AAAAAAAAAAA2").then((_){}); 46 | 47 | // stop peripheral server 48 | FlutterBluePeripheral.stopPeripheral(); 49 | ``` 50 | 51 | ## Migration to Flutter 3.32.0 52 | 53 | This plugin has been updated to support Flutter 3.32.0 and the latest Android development environment: 54 | 55 | ### Updated Dependencies 56 | - Android Gradle Plugin: 8.3.0 57 | - Gradle: 8.4 58 | - Kotlin: 1.9.21 59 | - Java: 17 60 | - compileSdk: 34 61 | - targetSdk: 34 62 | 63 | ### Breaking Changes 64 | - Migrated to the new Flutter plugin system (declarative plugins) 65 | - Updated to use `namespace` instead of `package` attribute in AndroidManifest.xml 66 | - Disabled Jetifier (no longer needed with AndroidX) 67 | 68 | ## More 69 | 70 | ### peripheralManagerDidUpdateState 71 | 72 | iOS UpdateState: 73 | 74 | ```swift 75 | switch peripheral.state { 76 | case .unknown: 77 | print("Unknown") 78 | state = "unknown" 79 | case .resetting: 80 | print("Resetting") 81 | state = "resetting" 82 | case .unsupported: 83 | print("Unsupported") 84 | state = "unsupported" 85 | case .unauthorized: 86 | print("Unauthorized") 87 | state = "unauthorized" 88 | case .poweredOff: 89 | print("Not Started") 90 | state = "poweredOff" 91 | self.peripheralManager?.stopAdvertising() 92 | case .poweredOn: 93 | print("Available") 94 | state = "poweredOn" 95 | ``` 96 | 97 | Android UpdateState: 98 | 99 | ``` 100 | "unknown" 101 | "poweredOff" 102 | "poweredOn" 103 | ``` 104 | 105 | iOS does not have device connect and disconnect callbacks, but Android does. Therefore, the central device should subscribe to the Characteristic. 106 | Then didSubscribeTo means the device is connected and didUnsubscribeFrom means the device is disconnected. didUnsubscribeFrom on the Android side he is triggered twice if the device actively cancels the subscription. 107 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_blue_peripheral_example 2 | description: Demonstrates how to use the flutter_blue_peripheral plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: '>=3.2.0 <4.0.0' 10 | 11 | # Dependencies specify other packages that your package needs in order to work. 12 | # To automatically upgrade your package dependencies to the latest versions 13 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 14 | # dependencies can be manually updated by changing the version numbers below to 15 | # the latest version available on pub.dev. To see which dependencies have newer 16 | # versions available, run `flutter pub outdated`. 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | 21 | flutter_blue_peripheral: 22 | # When depending on this package from a real application you should use: 23 | # flutter_blue_peripheral: ^x.y.z 24 | # See https://dart.dev/tools/pub/dependencies#version-constraints 25 | # The example app is bundled with the plugin so we use a path dependency on 26 | # the parent directory to use the current plugin's version. 27 | path: ../ 28 | 29 | # The following adds the Cupertino Icons font to your application. 30 | # Use with the CupertinoIcons class for iOS style icons. 31 | cupertino_icons: ^1.0.2 32 | 33 | dev_dependencies: 34 | flutter_test: 35 | sdk: flutter 36 | 37 | # The "flutter_lints" package below contains a set of recommended lints to 38 | # encourage good coding practices. The lint set provided by the package is 39 | # activated in the `analysis_options.yaml` file located at the root of your 40 | # package. See that file for information about deactivating specific lint 41 | # rules and activating additional ones. 42 | flutter_lints: ^4.0.0 43 | 44 | # For information on the generic Dart part of this file, see the 45 | # following page: https://dart.dev/tools/pub/pubspec 46 | 47 | # The following section is specific to Flutter packages. 48 | flutter: 49 | 50 | # The following line ensures that the Material Icons font is 51 | # included with your application, so that you can use the icons in 52 | # the material Icons class. 53 | uses-material-design: true 54 | 55 | # To add assets to your application, add an assets section, like this: 56 | # assets: 57 | # - images/a_dot_burr.jpeg 58 | # - images/a_dot_ham.jpeg 59 | 60 | # An image asset can refer to one or more resolution-specific "variants", see 61 | # https://flutter.dev/assets-and-images/#resolution-aware 62 | 63 | # For details regarding adding assets from package dependencies, see 64 | # https://flutter.dev/assets-and-images/#from-packages 65 | 66 | # To add custom fonts to your application, add a fonts section here, 67 | # in this "flutter" section. Each entry in this list should have a 68 | # "family" key with the font family name, and a "fonts" key with a 69 | # list giving the asset and other descriptors for the font. For 70 | # example: 71 | # fonts: 72 | # - family: Schyler 73 | # fonts: 74 | # - asset: fonts/Schyler-Regular.ttf 75 | # - asset: fonts/Schyler-Italic.ttf 76 | # style: italic 77 | # - family: Trajan Pro 78 | # fonts: 79 | # - asset: fonts/TrajanPro.ttf 80 | # - asset: fonts/TrajanPro_Bold.ttf 81 | # weight: 700 82 | # 83 | # For details regarding fonts from package dependencies, 84 | # see https://flutter.dev/custom-fonts/#from-packages 85 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 55 | 57 | 63 | 64 | 65 | 66 | 72 | 74 | 80 | 81 | 82 | 83 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /ios/Classes/SwiftFlutterBluePeripheralPlugin.swift: -------------------------------------------------------------------------------- 1 | 2 | import Flutter 3 | import UIKit 4 | import CoreBluetooth 5 | 6 | public class SwiftFlutterBluePeripheralPlugin: NSObject, FlutterPlugin, CBPeripheralManagerDelegate { 7 | 8 | var TAG = "[FlutterBluePeripheralPlugin] " 9 | 10 | var peripheralManager: CBPeripheralManager? 11 | var c: CBCentralManager? 12 | var channel: FlutterMethodChannel? 13 | 14 | let centralDic: NSMutableDictionary = [:] 15 | let characteristicDic: NSMutableDictionary = [:] 16 | 17 | public static func register(with registrar: FlutterPluginRegistrar) { 18 | let channel = FlutterMethodChannel(name:"flutter_blue_peripheral", binaryMessenger: registrar.messenger()) 19 | let instance = SwiftFlutterBluePeripheralPlugin() 20 | instance.channel = channel 21 | registrar.addMethodCallDelegate(instance, channel: channel) 22 | } 23 | 24 | private var Service_UUID: String = "00000000-0000-0000-0000-AAAAAAAAAAA1" 25 | private var Characteristic_UUID: String = "00000000-0000-0000-0000-AAAAAAAAAAA2" 26 | 27 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 28 | if call.method == "getPlatformVersion" { 29 | result("iOS " + UIDevice.current.systemVersion) 30 | } 31 | if call.method == "startPeripheral" { 32 | print(TAG + "startPeripheral") 33 | let param = call.arguments as! Dictionary 34 | Service_UUID = param["serviceUuid"]! 35 | Characteristic_UUID = param["characteristicUuid"]! 36 | peripheralManager = CBPeripheralManager.init(delegate: self, queue: .main) 37 | } 38 | if call.method == "stopPeripheral" { 39 | print(TAG + "stopPeripheral") 40 | peripheralManager?.stopAdvertising() 41 | } 42 | if call.method == "peripheralUpdateValue" { 43 | let param = call.arguments as! NSDictionary 44 | let centralUuid = param["centralUuid"] as! NSString 45 | // let characteristicUuid = param["characteristicUuid"] as! NSString 46 | let data = param["data"] as! FlutterStandardTypedData 47 | // peripheralManager?.updateValue( 48 | // data.data, 49 | // for: (characteristicDic[characteristicUuid]) as! CBMutableCharacteristic, 50 | // onSubscribedCentrals: [centralDic[centralUuid] as! CBCentral] 51 | // ) 52 | peripheralManager?.updateValue( 53 | data.data, 54 | for: (characteristicDic[0]) as! CBMutableCharacteristic, 55 | onSubscribedCentrals: [centralDic[centralUuid] as! CBCentral] 56 | ) 57 | } 58 | } 59 | 60 | public func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { 61 | var state = "unknown" 62 | switch peripheral.state { 63 | case .unknown: 64 | state = "unknown" 65 | case .resetting: 66 | state = "resetting" 67 | case .unsupported: 68 | state = "unsupported" 69 | case .unauthorized: 70 | state = "unauthorized" 71 | case .poweredOff: 72 | state = "poweredOff" 73 | self.peripheralManager?.stopAdvertising() 74 | case .poweredOn: 75 | state = "poweredOn" 76 | setupServiceAndCharacteristics() 77 | let deviceName = UIDevice.current.name; 78 | self.peripheralManager?.startAdvertising( 79 | [CBAdvertisementDataServiceUUIDsKey: [CBUUID.init(string: Service_UUID)], 80 | CBAdvertisementDataLocalNameKey: deviceName, 81 | ]) 82 | @unknown default: 83 | state = "unknown" 84 | } 85 | print(TAG + "peripheralManagerDidUpdateState:" + state) 86 | channel?.invokeMethod("peripheralManagerDidUpdateState", arguments: state) 87 | } 88 | 89 | private func setupServiceAndCharacteristics() { 90 | let serviceID = CBUUID.init(string: Service_UUID) 91 | let service = CBMutableService.init(type: serviceID, primary: true) 92 | let characteristicID = CBUUID.init(string: Characteristic_UUID) 93 | let characteristic = CBMutableCharacteristic.init( 94 | type: characteristicID, 95 | properties: [.read, .write, .notify], 96 | value: nil, 97 | permissions: [.readable, .writeable] 98 | ) 99 | service.characteristics = [characteristic] 100 | characteristicDic[0] = characteristic 101 | self.peripheralManager?.add(service) 102 | } 103 | 104 | public func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) { 105 | print(TAG + "didSubscribeTo:" + central.identifier.uuidString) 106 | centralDic[central.identifier.uuidString] = central 107 | channel?.invokeMethod("didSubscribeTo", arguments: [ 108 | "centralUuid": central.identifier.uuidString, 109 | "characteristicUuid": characteristic.uuid.uuidString, 110 | ]) 111 | } 112 | 113 | public func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) { 114 | print(TAG + "didUnsubscribeFrom:" + central.identifier.uuidString) 115 | centralDic[central.identifier.uuidString] = nil 116 | channel?.invokeMethod("didUnsubscribeFrom", arguments: [ 117 | "centralUuid": central.identifier.uuidString, 118 | "characteristicUuid": characteristic.uuid.uuidString, 119 | ]) 120 | } 121 | 122 | public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) { 123 | channel?.invokeMethod("didReceiveRead", arguments: [ 124 | "centralUuid": request.central.identifier.uuidString, 125 | "characteristicUuid": request.characteristic.uuid.uuidString, 126 | ], 127 | result: { (data) in 128 | if let da = data as? FlutterStandardTypedData { 129 | request.value = da.data 130 | } 131 | peripheral.respond(to: request, withResult: .success) 132 | }) 133 | } 134 | 135 | public func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) { 136 | for request in requests { 137 | if let data = request.value { 138 | channel?.invokeMethod("didReceiveWrite", arguments: [ 139 | "centralUuid": request.central.identifier.uuidString, 140 | "characteristicUuid": request.characteristic.uuid.uuidString, 141 | "data": FlutterStandardTypedData.init(bytes: data), 142 | ]) 143 | } 144 | request.value = nil 145 | peripheral.respond(to: request, withResult: .success) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.13.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.2" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.4.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.2" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.19.1" 44 | fake_async: 45 | dependency: transitive 46 | description: 47 | name: fake_async 48 | sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.3.3" 52 | flutter: 53 | dependency: "direct main" 54 | description: flutter 55 | source: sdk 56 | version: "0.0.0" 57 | flutter_lints: 58 | dependency: "direct dev" 59 | description: 60 | name: flutter_lints 61 | sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" 62 | url: "https://pub.dev" 63 | source: hosted 64 | version: "4.0.0" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | leak_tracker: 71 | dependency: transitive 72 | description: 73 | name: leak_tracker 74 | sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" 75 | url: "https://pub.dev" 76 | source: hosted 77 | version: "10.0.9" 78 | leak_tracker_flutter_testing: 79 | dependency: transitive 80 | description: 81 | name: leak_tracker_flutter_testing 82 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "3.0.9" 86 | leak_tracker_testing: 87 | dependency: transitive 88 | description: 89 | name: leak_tracker_testing 90 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 91 | url: "https://pub.dev" 92 | source: hosted 93 | version: "3.0.1" 94 | lints: 95 | dependency: transitive 96 | description: 97 | name: lints 98 | sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "4.0.0" 102 | matcher: 103 | dependency: transitive 104 | description: 105 | name: matcher 106 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 107 | url: "https://pub.dev" 108 | source: hosted 109 | version: "0.12.17" 110 | material_color_utilities: 111 | dependency: transitive 112 | description: 113 | name: material_color_utilities 114 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 115 | url: "https://pub.dev" 116 | source: hosted 117 | version: "0.11.1" 118 | meta: 119 | dependency: transitive 120 | description: 121 | name: meta 122 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 123 | url: "https://pub.dev" 124 | source: hosted 125 | version: "1.16.0" 126 | path: 127 | dependency: transitive 128 | description: 129 | name: path 130 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 131 | url: "https://pub.dev" 132 | source: hosted 133 | version: "1.9.1" 134 | plugin_platform_interface: 135 | dependency: "direct main" 136 | description: 137 | name: plugin_platform_interface 138 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 139 | url: "https://pub.dev" 140 | source: hosted 141 | version: "2.1.8" 142 | sky_engine: 143 | dependency: transitive 144 | description: flutter 145 | source: sdk 146 | version: "0.0.0" 147 | source_span: 148 | dependency: transitive 149 | description: 150 | name: source_span 151 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "1.10.1" 155 | stack_trace: 156 | dependency: transitive 157 | description: 158 | name: stack_trace 159 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "1.12.1" 163 | stream_channel: 164 | dependency: transitive 165 | description: 166 | name: stream_channel 167 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "2.1.4" 171 | string_scanner: 172 | dependency: transitive 173 | description: 174 | name: string_scanner 175 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "1.4.1" 179 | term_glyph: 180 | dependency: transitive 181 | description: 182 | name: term_glyph 183 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "1.2.2" 187 | test_api: 188 | dependency: transitive 189 | description: 190 | name: test_api 191 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.7.4" 195 | vector_math: 196 | dependency: transitive 197 | description: 198 | name: vector_math 199 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "2.1.4" 203 | vm_service: 204 | dependency: transitive 205 | description: 206 | name: vm_service 207 | sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "15.0.0" 211 | sdks: 212 | dart: ">=3.7.0-0 <4.0.0" 213 | flutter: ">=3.18.0-18.0.pre.54" 214 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.13.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.2" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.4.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.2" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.19.1" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.8" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.3" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_blue_peripheral: 66 | dependency: "direct main" 67 | description: 68 | path: ".." 69 | relative: true 70 | source: path 71 | version: "0.2.0" 72 | flutter_lints: 73 | dependency: "direct dev" 74 | description: 75 | name: flutter_lints 76 | sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" 77 | url: "https://pub.dev" 78 | source: hosted 79 | version: "4.0.0" 80 | flutter_test: 81 | dependency: "direct dev" 82 | description: flutter 83 | source: sdk 84 | version: "0.0.0" 85 | leak_tracker: 86 | dependency: transitive 87 | description: 88 | name: leak_tracker 89 | sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" 90 | url: "https://pub.dev" 91 | source: hosted 92 | version: "10.0.9" 93 | leak_tracker_flutter_testing: 94 | dependency: transitive 95 | description: 96 | name: leak_tracker_flutter_testing 97 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 98 | url: "https://pub.dev" 99 | source: hosted 100 | version: "3.0.9" 101 | leak_tracker_testing: 102 | dependency: transitive 103 | description: 104 | name: leak_tracker_testing 105 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 106 | url: "https://pub.dev" 107 | source: hosted 108 | version: "3.0.1" 109 | lints: 110 | dependency: transitive 111 | description: 112 | name: lints 113 | sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" 114 | url: "https://pub.dev" 115 | source: hosted 116 | version: "4.0.0" 117 | matcher: 118 | dependency: transitive 119 | description: 120 | name: matcher 121 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 122 | url: "https://pub.dev" 123 | source: hosted 124 | version: "0.12.17" 125 | material_color_utilities: 126 | dependency: transitive 127 | description: 128 | name: material_color_utilities 129 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 130 | url: "https://pub.dev" 131 | source: hosted 132 | version: "0.11.1" 133 | meta: 134 | dependency: transitive 135 | description: 136 | name: meta 137 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 138 | url: "https://pub.dev" 139 | source: hosted 140 | version: "1.16.0" 141 | path: 142 | dependency: transitive 143 | description: 144 | name: path 145 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 146 | url: "https://pub.dev" 147 | source: hosted 148 | version: "1.9.1" 149 | plugin_platform_interface: 150 | dependency: transitive 151 | description: 152 | name: plugin_platform_interface 153 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 154 | url: "https://pub.dev" 155 | source: hosted 156 | version: "2.1.8" 157 | sky_engine: 158 | dependency: transitive 159 | description: flutter 160 | source: sdk 161 | version: "0.0.0" 162 | source_span: 163 | dependency: transitive 164 | description: 165 | name: source_span 166 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 167 | url: "https://pub.dev" 168 | source: hosted 169 | version: "1.10.1" 170 | stack_trace: 171 | dependency: transitive 172 | description: 173 | name: stack_trace 174 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 175 | url: "https://pub.dev" 176 | source: hosted 177 | version: "1.12.1" 178 | stream_channel: 179 | dependency: transitive 180 | description: 181 | name: stream_channel 182 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 183 | url: "https://pub.dev" 184 | source: hosted 185 | version: "2.1.4" 186 | string_scanner: 187 | dependency: transitive 188 | description: 189 | name: string_scanner 190 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 191 | url: "https://pub.dev" 192 | source: hosted 193 | version: "1.4.1" 194 | term_glyph: 195 | dependency: transitive 196 | description: 197 | name: term_glyph 198 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 199 | url: "https://pub.dev" 200 | source: hosted 201 | version: "1.2.2" 202 | test_api: 203 | dependency: transitive 204 | description: 205 | name: test_api 206 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 207 | url: "https://pub.dev" 208 | source: hosted 209 | version: "0.7.4" 210 | vector_math: 211 | dependency: transitive 212 | description: 213 | name: vector_math 214 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 215 | url: "https://pub.dev" 216 | source: hosted 217 | version: "2.1.4" 218 | vm_service: 219 | dependency: transitive 220 | description: 221 | name: vm_service 222 | sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 223 | url: "https://pub.dev" 224 | source: hosted 225 | version: "15.0.0" 226 | sdks: 227 | dart: ">=3.7.0-0 <4.0.0" 228 | flutter: ">=3.18.0-18.0.pre.54" 229 | -------------------------------------------------------------------------------- /android/src/main/kotlin/net/tsukumijima/flutter_blue_peripheral/FlutterBluePeripheralPlugin.kt: -------------------------------------------------------------------------------- 1 | 2 | package net.tsukumijima.flutter_blue_peripheral 3 | 4 | import android.app.Activity 5 | import android.bluetooth.* 6 | import android.bluetooth.le.AdvertiseCallback 7 | import android.bluetooth.le.AdvertiseData 8 | import android.bluetooth.le.AdvertiseSettings 9 | import android.bluetooth.le.BluetoothLeAdvertiser 10 | import android.content.BroadcastReceiver 11 | import android.content.Context 12 | import android.content.Intent 13 | import android.content.IntentFilter 14 | import android.os.Handler 15 | import android.os.Looper 16 | import android.os.ParcelUuid 17 | import android.util.Log 18 | import android.widget.Toast 19 | import androidx.annotation.NonNull 20 | import java.util.* 21 | import kotlin.collections.HashMap 22 | 23 | import io.flutter.embedding.engine.plugins.activity.ActivityAware 24 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding 25 | import io.flutter.embedding.engine.plugins.FlutterPlugin 26 | import io.flutter.plugin.common.MethodCall 27 | import io.flutter.plugin.common.MethodChannel 28 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 29 | import io.flutter.plugin.common.MethodChannel.Result 30 | 31 | /** FlutterBluePeripheralPlugin */ 32 | class FlutterBluePeripheralPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { 33 | /// The MethodChannel that will the communication between Flutter and native Android 34 | /// 35 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 36 | /// when the Flutter Engine is detached from the Activity 37 | private lateinit var channel : MethodChannel 38 | 39 | private var TAG = "FlutterBluePeripheralPlugin"; 40 | 41 | companion object { 42 | var activity: Activity? = null 43 | var registerReceiver: Boolean = false 44 | } 45 | 46 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 47 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_blue_peripheral") 48 | channel.setMethodCallHandler(this) 49 | } 50 | 51 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 52 | channel.setMethodCallHandler(null) 53 | } 54 | 55 | // save activity 56 | // ref: https://qiita.com/ko-izumi/items/216f2fb7dc51d85884e4 57 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 58 | FlutterBluePeripheralPlugin.activity = binding.activity 59 | } 60 | override fun onDetachedFromActivityForConfigChanges() { 61 | TODO("Not yet implemented") 62 | } 63 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 64 | TODO("Not yet implemented") 65 | } 66 | override fun onDetachedFromActivity() { 67 | TODO("Not yet implemented") 68 | } 69 | 70 | override fun onMethodCall(call: MethodCall, result: Result): Unit { 71 | if (call.method.equals("getPlatformVersion")) { 72 | result.success("Android ${android.os.Build.VERSION.RELEASE}") 73 | } 74 | if (call.method.equals("startPeripheral")) { 75 | Log.i(TAG, "startPeripheral") 76 | Service_UUID = call.argument("serviceUuid").toString() 77 | Characteristic_UUID = call.argument("characteristicUuid").toString() 78 | startPeripheral() 79 | } 80 | if (call.method.equals("stopPeripheral")) { 81 | Log.i(TAG, "stopPeripheral") 82 | stopPeripheral() 83 | } 84 | if (call.method.equals("peripheralUpdateValue")) { 85 | var centralUuid = call.argument("centralUuid") 86 | var characteristicUuid = call.argument("characteristicUuid") 87 | var data = call.argument("data") 88 | 89 | val device = centralsDic.get(centralUuid) 90 | // val characteristic = characteristicsDic.get(characteristicUuid) 91 | // characteristic?.setValue(data) 92 | mCharacteristic?.value = data 93 | mGattServer?.notifyCharacteristicChanged(device, mCharacteristic, false) 94 | } 95 | } 96 | 97 | private var mBluetoothManager: BluetoothManager? = null 98 | private var mBluetoothAdapter: BluetoothAdapter? = null 99 | private var mGattServer: BluetoothGattServer? = null 100 | private var Service_UUID: String = UUID.randomUUID().toString() 101 | private var Characteristic_UUID: String = UUID.randomUUID().toString() 102 | private var centralsDic: MutableMap = HashMap() 103 | private var characteristicsDic: MutableMap = HashMap() 104 | private var descriptorsDic: MutableMap = HashMap() 105 | private val handler: Handler = Handler(Looper.getMainLooper()) 106 | 107 | private var mAdvData: AdvertiseData? = null 108 | private var mAdvScanResponse: AdvertiseData? = null 109 | private var mAdvSettings: AdvertiseSettings? = null 110 | private var mBluetoothGattService: BluetoothGattService? = null 111 | private var mAdvertiser: BluetoothLeAdvertiser? = null 112 | private var mCharacteristic: BluetoothGattCharacteristic? = null 113 | 114 | private val mAdvCallback: AdvertiseCallback = object : AdvertiseCallback() { 115 | override fun onStartFailure(errorCode: Int) { 116 | super.onStartFailure(errorCode) 117 | Log.e(TAG, "Not broadcasting:$errorCode") 118 | } 119 | 120 | override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { 121 | super.onStartSuccess(settingsInEffect) 122 | Log.i(TAG, "Broadcasting") 123 | } 124 | } 125 | 126 | open fun startPeripheral() { 127 | 128 | if (FlutterBluePeripheralPlugin.registerReceiver == false) { 129 | FlutterBluePeripheralPlugin.registerReceiver = true 130 | val mR = object : BroadcastReceiver() { 131 | override fun onReceive(context: Context?, intent: Intent?) { 132 | val action = intent?.action 133 | 134 | if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 135 | val state = intent?.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR) 136 | 137 | var statestr = "unknown" 138 | when (state) { 139 | BluetoothAdapter.STATE_OFF -> statestr = "poweredOff" 140 | BluetoothAdapter.STATE_ON -> statestr = "poweredOn" 141 | } 142 | handler.post(Runnable { 143 | channel?.invokeMethod("peripheralManagerDidUpdateState", statestr) 144 | }) 145 | if (statestr == "poweredOn") { 146 | this@FlutterBluePeripheralPlugin.startPeripheral() 147 | } 148 | } 149 | } 150 | } 151 | val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) 152 | FlutterBluePeripheralPlugin.activity?.registerReceiver(mR, filter) 153 | } 154 | 155 | mBluetoothManager = activity?.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager? 156 | mBluetoothAdapter = mBluetoothManager?.adapter 157 | 158 | mAdvSettings = AdvertiseSettings.Builder() 159 | .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) 160 | .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) 161 | .setConnectable(true) 162 | .build() 163 | mAdvData = AdvertiseData.Builder() 164 | .setIncludeTxPowerLevel(true) 165 | .addServiceUuid(ParcelUuid(UUID.fromString(Service_UUID))) 166 | .build() 167 | mAdvScanResponse = AdvertiseData.Builder() 168 | .setIncludeDeviceName(true) 169 | .build() 170 | 171 | mGattServer = mBluetoothManager!!.openGattServer(activity, mGattServerCallback); 172 | if (mGattServer == null) { 173 | ensureBleFeaturesAvailable(); 174 | return; 175 | } 176 | 177 | mBluetoothGattService = BluetoothGattService( 178 | UUID.fromString(Service_UUID), 179 | BluetoothGattService.SERVICE_TYPE_PRIMARY, 180 | ) 181 | 182 | mCharacteristic = BluetoothGattCharacteristic( 183 | UUID.fromString(Characteristic_UUID), 184 | //Read write characteristic, supports notifications 185 | BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY or BluetoothGattCharacteristic.PROPERTY_WRITE, 186 | BluetoothGattCharacteristic.PERMISSION_READ or BluetoothGattCharacteristic.PERMISSION_WRITE or BluetoothGattCharacteristic.PROPERTY_NOTIFY, 187 | ) 188 | 189 | val bluetoothGattDescriptor = BluetoothGattDescriptor( 190 | UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), 191 | BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE, 192 | ) 193 | mCharacteristic!!.addDescriptor(bluetoothGattDescriptor) 194 | 195 | mBluetoothGattService = BluetoothGattService(UUID.fromString(Service_UUID), 196 | BluetoothGattService.SERVICE_TYPE_PRIMARY) 197 | mBluetoothGattService!!.addCharacteristic(mCharacteristic) 198 | 199 | // Add a service for a total of three services (Generic Attribute and Generic Access 200 | // are present by default). 201 | mGattServer!!.addService(mBluetoothGattService); 202 | 203 | if (mBluetoothAdapter!!.isMultipleAdvertisementSupported()) { 204 | mAdvertiser = mBluetoothAdapter!!.getBluetoothLeAdvertiser(); 205 | mAdvertiser!!.startAdvertising(mAdvSettings, mAdvData, mAdvScanResponse, mAdvCallback); 206 | } else { 207 | Toast.makeText(FlutterBluePeripheralPlugin.activity?.applicationContext, "MultipleAdvertisement not Supported", Toast.LENGTH_SHORT).show() 208 | } 209 | } 210 | 211 | private fun ensureBleFeaturesAvailable() { 212 | if (mBluetoothAdapter == null) { 213 | Toast.makeText(FlutterBluePeripheralPlugin.activity?.applicationContext, "Not support host.", Toast.LENGTH_SHORT).show() 214 | Log.e(TAG, "Bluetooth not supported") 215 | } 216 | } 217 | 218 | /* 219 | * Create the GATT server instance, attaching all services and 220 | * characteristics that should be exposed 221 | */ 222 | private fun initServer() { 223 | val service = BluetoothGattService(UUID.fromString(Service_UUID), 224 | BluetoothGattService.SERVICE_TYPE_PRIMARY) 225 | 226 | val elapsedCharacteristic = BluetoothGattCharacteristic(UUID.fromString(Characteristic_UUID), 227 | //Read write characteristic, supports notifications 228 | BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY or BluetoothGattCharacteristic.PROPERTY_WRITE, 229 | BluetoothGattCharacteristic.PERMISSION_READ or BluetoothGattCharacteristic.PERMISSION_WRITE or BluetoothGattCharacteristic.PROPERTY_NOTIFY) 230 | 231 | val bluetoothGattDescriptor = BluetoothGattDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE) 232 | 233 | elapsedCharacteristic.addDescriptor(bluetoothGattDescriptor) 234 | 235 | service.addCharacteristic(elapsedCharacteristic) 236 | 237 | mGattServer!!.addService(service) 238 | } 239 | 240 | /* 241 | * Callback handles all incoming requests from GATT clients. 242 | * From connections to read/write requests. 243 | */ 244 | private val mGattServerCallback = object : BluetoothGattServerCallback() { 245 | override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { 246 | super.onConnectionStateChange(device, status, newState) 247 | 248 | if (newState == BluetoothProfile.STATE_CONNECTED) { 249 | centralsDic.put(device.address, device) 250 | Log.i(TAG, "onConnectionStateChange STATE_CONNECTED:" + device.address) 251 | 252 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 253 | centralsDic.remove(device.address) 254 | Log.i(TAG, "onConnectionStateChange STATE_DISCONNECTED:" + device.address) 255 | handler.post(Runnable { 256 | channel?.invokeMethod("didUnsubscribeFrom", hashMapOf( 257 | "centralUuid" to device?.address!!, 258 | "characteristicUuid" to "", 259 | )) 260 | }) 261 | } 262 | } 263 | 264 | override fun onCharacteristicReadRequest( 265 | device: BluetoothDevice, 266 | requestId: Int, 267 | offset: Int, 268 | characteristic: BluetoothGattCharacteristic 269 | ) { 270 | super.onCharacteristicReadRequest(device, requestId, offset, characteristic) 271 | Log.i(TAG, "onCharacteristicReadRequest:" + characteristic.uuid.toString()) 272 | 273 | if (UUID.fromString(Characteristic_UUID) == characteristic.uuid) { 274 | 275 | val cb = object : MethodChannel.Result { 276 | override fun success(p0: Any?) { 277 | mGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, p0 as ByteArray) 278 | } 279 | 280 | override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { 281 | TODO("Not yet implemented") 282 | } 283 | 284 | override fun notImplemented() { 285 | } 286 | } 287 | handler.post(Runnable { 288 | channel?.invokeMethod("didReceiveRead", hashMapOf( 289 | "centralUuid" to device?.address, 290 | "characteristicUuid" to characteristic.uuid.toString(), 291 | ), cb); 292 | }) 293 | } 294 | } 295 | 296 | override fun onCharacteristicWriteRequest( 297 | device: BluetoothDevice, 298 | requestId: Int, 299 | characteristic: BluetoothGattCharacteristic, 300 | preparedWrite: Boolean, 301 | responseNeeded: Boolean, 302 | offset: Int, 303 | value: ByteArray, 304 | ) { 305 | super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) 306 | Log.i(TAG, "onCharacteristicWriteRequest:" + characteristic.uuid.toString()) 307 | mGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null) 308 | 309 | if (UUID.fromString(Characteristic_UUID) == characteristic.uuid) { 310 | handler.post(Runnable { 311 | channel?.invokeMethod("didReceiveWrite", hashMapOf( 312 | "centralUuid" to device?.address, 313 | "characteristicUuid" to characteristic.uuid.toString(), 314 | "data" to value, 315 | )) 316 | }) 317 | } 318 | } 319 | 320 | override fun onNotificationSent(device: BluetoothDevice?, status: Int) { 321 | super.onNotificationSent(device, status) 322 | } 323 | 324 | override fun onDescriptorReadRequest( 325 | device: BluetoothDevice?, 326 | requestId: Int, 327 | offset: Int, 328 | descriptor: BluetoothGattDescriptor?, 329 | ) { 330 | super.onDescriptorReadRequest(device, requestId, offset, descriptor) 331 | Log.i(TAG, "onDescriptorReadRequest:" + descriptor?.uuid.toString()) 332 | mGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null) 333 | } 334 | 335 | override fun onDescriptorWriteRequest( 336 | device: BluetoothDevice?, 337 | requestId: Int, 338 | descriptor: BluetoothGattDescriptor?, 339 | preparedWrite: Boolean, 340 | responseNeeded: Boolean, 341 | offset: Int, 342 | value: ByteArray?, 343 | ) { 344 | super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) 345 | Log.i(TAG, "onDescriptorWriteRequest:" + descriptor?.uuid.toString() + " preparedWrite:" + preparedWrite + " responseNeeded:" + responseNeeded + "value:" + value) 346 | 347 | mGattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null) 348 | 349 | if (descriptorsDic.containsKey(descriptor?.uuid.toString())) { 350 | descriptorsDic.remove(descriptor?.uuid.toString()) 351 | characteristicsDic.remove(descriptor?.characteristic?.uuid.toString()) 352 | handler.post(Runnable { 353 | channel?.invokeMethod("didUnsubscribeFrom", hashMapOf( 354 | "centralUuid" to device?.address!!, 355 | "characteristicUuid" to descriptor?.characteristic?.uuid.toString(), 356 | )) 357 | }) 358 | } else { 359 | descriptorsDic.put(descriptor?.uuid.toString(), descriptor!!) 360 | characteristicsDic.put(descriptor?.characteristic?.uuid.toString(), descriptor?.characteristic!!) 361 | handler.post(Runnable { 362 | channel?.invokeMethod("didSubscribeTo", hashMapOf( 363 | "centralUuid" to device?.address!!, 364 | "characteristicUuid" to descriptor?.characteristic?.uuid.toString(), 365 | )) 366 | }) 367 | } 368 | } 369 | } 370 | 371 | /* 372 | * Terminate the advertiser 373 | */ 374 | private fun stopPeripheral() { 375 | if (mGattServer != null) { 376 | mGattServer!!.close() 377 | } 378 | 379 | // If stopAdvertising() gets called before close() a null 380 | // pointer exception is raised. 381 | mAdvertiser!!.stopAdvertising(mAdvCallback) 382 | } 383 | 384 | /* 385 | * Callback handles events from the framework describing 386 | * if we were successful in starting the advertisement requests. 387 | */ 388 | private val mAdvertiseCallback = object : AdvertiseCallback() { 389 | override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { 390 | Log.i(TAG, "Peripheral Advertise Started.") 391 | } 392 | 393 | override fun onStartFailure(errorCode: Int) { 394 | Log.i(TAG, "Peripheral Advertise Failed: $errorCode") 395 | } 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 12AE1255DA03174C9621F5B0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 110047F2CF174114F5B0EBDF /* Pods_Runner.framework */; }; 11 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 110047F2CF174114F5B0EBDF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 2ACFAB361698D3D8DD74540E /* 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 = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 7075307EDC386CEED7C25F39 /* 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 = ""; }; 39 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 40 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 42 | 8BBF0874FEDB1139F7BA3627 /* 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 = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 12AE1255DA03174C9621F5B0 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 7BE35CCD0394CF35DCC9960C /* Frameworks */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 110047F2CF174114F5B0EBDF /* Pods_Runner.framework */, 68 | ); 69 | name = Frameworks; 70 | sourceTree = ""; 71 | }; 72 | 9740EEB11CF90186004384FC /* Flutter */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 76 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 77 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 78 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 79 | ); 80 | name = Flutter; 81 | sourceTree = ""; 82 | }; 83 | 97C146E51CF9000F007C117D = { 84 | isa = PBXGroup; 85 | children = ( 86 | 9740EEB11CF90186004384FC /* Flutter */, 87 | 97C146F01CF9000F007C117D /* Runner */, 88 | 97C146EF1CF9000F007C117D /* Products */, 89 | E638C4660D957C1C60A441D0 /* Pods */, 90 | 7BE35CCD0394CF35DCC9960C /* Frameworks */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 97C146EF1CF9000F007C117D /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146EE1CF9000F007C117D /* Runner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 97C146F01CF9000F007C117D /* Runner */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 106 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 107 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 108 | 97C147021CF9000F007C117D /* Info.plist */, 109 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 110 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 111 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 112 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 113 | ); 114 | path = Runner; 115 | sourceTree = ""; 116 | }; 117 | E638C4660D957C1C60A441D0 /* Pods */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 2ACFAB361698D3D8DD74540E /* Pods-Runner.debug.xcconfig */, 121 | 8BBF0874FEDB1139F7BA3627 /* Pods-Runner.release.xcconfig */, 122 | 7075307EDC386CEED7C25F39 /* Pods-Runner.profile.xcconfig */, 123 | ); 124 | name = Pods; 125 | path = Pods; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 97C146ED1CF9000F007C117D /* Runner */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 134 | buildPhases = ( 135 | 9463BBDD856A4CE75AE2A583 /* [CP] Check Pods Manifest.lock */, 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | 332BF1EFE69C5B36B0B416DB /* [CP] Embed Pods Frameworks */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Runner; 149 | productName = Runner; 150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 97C146E61CF9000F007C117D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 1510; 160 | ORGANIZATIONNAME = ""; 161 | TargetAttributes = { 162 | 97C146ED1CF9000F007C117D = { 163 | CreatedOnToolsVersion = 7.3.1; 164 | LastSwiftMigration = 1100; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 169 | compatibilityVersion = "Xcode 9.3"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 97C146E51CF9000F007C117D; 177 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 97C146ED1CF9000F007C117D /* Runner */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 97C146EC1CF9000F007C117D /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 192 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 193 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 332BF1EFE69C5B36B0B416DB /* [CP] Embed Pods Frameworks */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputFileListPaths = ( 207 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 208 | ); 209 | name = "[CP] Embed Pods Frameworks"; 210 | outputFileListPaths = ( 211 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 216 | showEnvVarsInLog = 0; 217 | }; 218 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 219 | isa = PBXShellScriptBuildPhase; 220 | alwaysOutOfDate = 1; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | ); 224 | inputPaths = ( 225 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 226 | ); 227 | name = "Thin Binary"; 228 | outputPaths = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | shellPath = /bin/sh; 232 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 233 | }; 234 | 9463BBDD856A4CE75AE2A583 /* [CP] Check Pods Manifest.lock */ = { 235 | isa = PBXShellScriptBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | ); 239 | inputFileListPaths = ( 240 | ); 241 | inputPaths = ( 242 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 243 | "${PODS_ROOT}/Manifest.lock", 244 | ); 245 | name = "[CP] Check Pods Manifest.lock"; 246 | outputFileListPaths = ( 247 | ); 248 | outputPaths = ( 249 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | shellPath = /bin/sh; 253 | 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"; 254 | showEnvVarsInLog = 0; 255 | }; 256 | 9740EEB61CF901F6004384FC /* Run Script */ = { 257 | isa = PBXShellScriptBuildPhase; 258 | alwaysOutOfDate = 1; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | inputPaths = ( 263 | ); 264 | name = "Run Script"; 265 | outputPaths = ( 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | shellPath = /bin/sh; 269 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 270 | }; 271 | /* End PBXShellScriptBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 97C146EA1CF9000F007C117D /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 279 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | /* End PBXSourcesBuildPhase section */ 284 | 285 | /* Begin PBXVariantGroup section */ 286 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | 97C146FB1CF9000F007C117D /* Base */, 290 | ); 291 | name = Main.storyboard; 292 | sourceTree = ""; 293 | }; 294 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 295 | isa = PBXVariantGroup; 296 | children = ( 297 | 97C147001CF9000F007C117D /* Base */, 298 | ); 299 | name = LaunchScreen.storyboard; 300 | sourceTree = ""; 301 | }; 302 | /* End PBXVariantGroup section */ 303 | 304 | /* Begin XCBuildConfiguration section */ 305 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 347 | MTL_ENABLE_DEBUG_INFO = NO; 348 | SDKROOT = iphoneos; 349 | SUPPORTED_PLATFORMS = iphoneos; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | VALIDATE_PRODUCT = YES; 352 | }; 353 | name = Profile; 354 | }; 355 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 356 | isa = XCBuildConfiguration; 357 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CLANG_ENABLE_MODULES = YES; 361 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 362 | DEVELOPMENT_TEAM = 5J9XUX6XBD; 363 | ENABLE_BITCODE = NO; 364 | INFOPLIST_FILE = Runner/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "@executable_path/Frameworks", 368 | ); 369 | PRODUCT_BUNDLE_IDENTIFIER = net.tsukumijima.flutterBluePeripheralExample; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 372 | SWIFT_VERSION = 5.0; 373 | VERSIONING_SYSTEM = "apple-generic"; 374 | }; 375 | name = Profile; 376 | }; 377 | 97C147031CF9000F007C117D /* Debug */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_COMMA = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 391 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 392 | CLANG_WARN_EMPTY_BODY = YES; 393 | CLANG_WARN_ENUM_CONVERSION = YES; 394 | CLANG_WARN_INFINITE_RECURSION = YES; 395 | CLANG_WARN_INT_CONVERSION = YES; 396 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 398 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 400 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 401 | CLANG_WARN_STRICT_PROTOTYPES = YES; 402 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 403 | CLANG_WARN_UNREACHABLE_CODE = YES; 404 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 405 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 406 | COPY_PHASE_STRIP = NO; 407 | DEBUG_INFORMATION_FORMAT = dwarf; 408 | ENABLE_STRICT_OBJC_MSGSEND = YES; 409 | ENABLE_TESTABILITY = YES; 410 | GCC_C_LANGUAGE_STANDARD = gnu99; 411 | GCC_DYNAMIC_NO_PIC = NO; 412 | GCC_NO_COMMON_BLOCKS = YES; 413 | GCC_OPTIMIZATION_LEVEL = 0; 414 | GCC_PREPROCESSOR_DEFINITIONS = ( 415 | "DEBUG=1", 416 | "$(inherited)", 417 | ); 418 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 419 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 422 | GCC_WARN_UNUSED_FUNCTION = YES; 423 | GCC_WARN_UNUSED_VARIABLE = YES; 424 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 425 | MTL_ENABLE_DEBUG_INFO = YES; 426 | ONLY_ACTIVE_ARCH = YES; 427 | SDKROOT = iphoneos; 428 | TARGETED_DEVICE_FAMILY = "1,2"; 429 | }; 430 | name = Debug; 431 | }; 432 | 97C147041CF9000F007C117D /* Release */ = { 433 | isa = XCBuildConfiguration; 434 | buildSettings = { 435 | ALWAYS_SEARCH_USER_PATHS = NO; 436 | CLANG_ANALYZER_NONNULL = YES; 437 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 438 | CLANG_CXX_LIBRARY = "libc++"; 439 | CLANG_ENABLE_MODULES = YES; 440 | CLANG_ENABLE_OBJC_ARC = YES; 441 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 442 | CLANG_WARN_BOOL_CONVERSION = YES; 443 | CLANG_WARN_COMMA = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_EMPTY_BODY = YES; 448 | CLANG_WARN_ENUM_CONVERSION = YES; 449 | CLANG_WARN_INFINITE_RECURSION = YES; 450 | CLANG_WARN_INT_CONVERSION = YES; 451 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 453 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 456 | CLANG_WARN_STRICT_PROTOTYPES = YES; 457 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 458 | CLANG_WARN_UNREACHABLE_CODE = YES; 459 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 460 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 461 | COPY_PHASE_STRIP = NO; 462 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 463 | ENABLE_NS_ASSERTIONS = NO; 464 | ENABLE_STRICT_OBJC_MSGSEND = YES; 465 | GCC_C_LANGUAGE_STANDARD = gnu99; 466 | GCC_NO_COMMON_BLOCKS = YES; 467 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 468 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 469 | GCC_WARN_UNDECLARED_SELECTOR = YES; 470 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 471 | GCC_WARN_UNUSED_FUNCTION = YES; 472 | GCC_WARN_UNUSED_VARIABLE = YES; 473 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 474 | MTL_ENABLE_DEBUG_INFO = NO; 475 | SDKROOT = iphoneos; 476 | SUPPORTED_PLATFORMS = iphoneos; 477 | SWIFT_COMPILATION_MODE = wholemodule; 478 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | VALIDATE_PRODUCT = YES; 481 | }; 482 | name = Release; 483 | }; 484 | 97C147061CF9000F007C117D /* Debug */ = { 485 | isa = XCBuildConfiguration; 486 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 487 | buildSettings = { 488 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 489 | CLANG_ENABLE_MODULES = YES; 490 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 491 | DEVELOPMENT_TEAM = 5J9XUX6XBD; 492 | ENABLE_BITCODE = NO; 493 | INFOPLIST_FILE = Runner/Info.plist; 494 | LD_RUNPATH_SEARCH_PATHS = ( 495 | "$(inherited)", 496 | "@executable_path/Frameworks", 497 | ); 498 | PRODUCT_BUNDLE_IDENTIFIER = net.tsukumijima.flutterBluePeripheralExample; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 501 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 502 | SWIFT_VERSION = 5.0; 503 | VERSIONING_SYSTEM = "apple-generic"; 504 | }; 505 | name = Debug; 506 | }; 507 | 97C147071CF9000F007C117D /* Release */ = { 508 | isa = XCBuildConfiguration; 509 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 510 | buildSettings = { 511 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 512 | CLANG_ENABLE_MODULES = YES; 513 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 514 | DEVELOPMENT_TEAM = 5J9XUX6XBD; 515 | ENABLE_BITCODE = NO; 516 | INFOPLIST_FILE = Runner/Info.plist; 517 | LD_RUNPATH_SEARCH_PATHS = ( 518 | "$(inherited)", 519 | "@executable_path/Frameworks", 520 | ); 521 | PRODUCT_BUNDLE_IDENTIFIER = net.tsukumijima.flutterBluePeripheralExample; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 524 | SWIFT_VERSION = 5.0; 525 | VERSIONING_SYSTEM = "apple-generic"; 526 | }; 527 | name = Release; 528 | }; 529 | /* End XCBuildConfiguration section */ 530 | 531 | /* Begin XCConfigurationList section */ 532 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 533 | isa = XCConfigurationList; 534 | buildConfigurations = ( 535 | 97C147031CF9000F007C117D /* Debug */, 536 | 97C147041CF9000F007C117D /* Release */, 537 | 249021D3217E4FDB00AE95B9 /* Profile */, 538 | ); 539 | defaultConfigurationIsVisible = 0; 540 | defaultConfigurationName = Release; 541 | }; 542 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 543 | isa = XCConfigurationList; 544 | buildConfigurations = ( 545 | 97C147061CF9000F007C117D /* Debug */, 546 | 97C147071CF9000F007C117D /* Release */, 547 | 249021D4217E4FDB00AE95B9 /* Profile */, 548 | ); 549 | defaultConfigurationIsVisible = 0; 550 | defaultConfigurationName = Release; 551 | }; 552 | /* End XCConfigurationList section */ 553 | }; 554 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 555 | } 556 | --------------------------------------------------------------------------------