├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ └── flutter_format.yml
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── dev
│ └── steenbakker
│ └── flutter_ble_peripheral
│ ├── FlutterBlePeripheralManager.kt
│ ├── FlutterBlePeripheralPlugin.kt
│ ├── callbacks
│ ├── PeripheralAdvertisingCallback.kt
│ └── PeripheralAdvertisingSetCallback.kt
│ ├── exceptions
│ ├── PeripheralException.kt
│ └── PermissionNotFoundException.kt
│ ├── handlers
│ ├── DataReceivedHandler.kt
│ ├── MtuChangedHandler.kt
│ └── StateChangedHandler.kt
│ └── models
│ ├── PeripheralState.kt
│ ├── PermissionState.kt
│ └── State.kt
├── example
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── 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
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── proguard-rules.pro
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── 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-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── ble_status_screen.dart
│ └── main.dart
├── macos
│ ├── .gitignore
│ ├── Podfile
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_256.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_512.png
│ │ │ └── app_icon_64.png
│ │ ├── Base.lproj
│ │ └── MainMenu.xib
│ │ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
├── pubspec.yaml
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── FlutterBlePeripheralManager.swift
│ ├── FlutterBlePeripheralPlugin.h
│ ├── FlutterBlePeripheralPlugin.m
│ ├── Models
│ │ ├── PeripheralData.swift
│ │ └── PeripheralState.swift
│ ├── SwiftFlutterBlePeripheralPlugin.swift
│ ├── callbacks
│ │ └── PeripheralManagerDelegate.swift
│ └── handlers
│ │ ├── DataReceivedHandler.swift
│ │ ├── MtuChangedHandler.swift
│ │ └── StateChangedHandler.swift
└── flutter_ble_peripheral.podspec
├── lib
├── flutter_ble_peripheral.dart
└── src
│ ├── flutter_ble_peripheral.dart
│ └── models
│ ├── advertise_data.dart
│ ├── advertise_data.g.dart
│ ├── advertise_set_parameters.dart
│ ├── advertise_set_parameters.g.dart
│ ├── advertise_settings.dart
│ ├── advertise_settings.g.dart
│ ├── constants.dart
│ ├── enums
│ ├── advertise_mode.dart
│ ├── advertise_tx_power.dart
│ └── bluetooth_peripheral_state.dart
│ ├── map_uint8list_converter.dart
│ ├── periodic_advertise_settings.dart
│ ├── periodic_advertise_settings.g.dart
│ ├── peripheral_state.dart
│ ├── permission_state.dart
│ ├── uint8list_converter.dart
│ └── uint8list_map_string_converter.dart
├── macos
├── Assets
│ └── .gitkeep
├── Classes
│ ├── FlutterBlePeripheralManager.swift
│ ├── FlutterBlePeripheralPlugin.h
│ ├── FlutterBlePeripheralPlugin.m
│ ├── Models
│ │ ├── PeripheralData.swift
│ │ └── PeripheralState.swift
│ ├── SwiftFlutterBlePeripheralPlugin.swift
│ ├── callbacks
│ │ └── PeripheralManagerDelegate.swift
│ └── handlers
│ │ ├── DataReceivedHandler.swift
│ │ ├── MtuChangedHandler.swift
│ │ └── StateChangedHandler.swift
└── flutter_ble_peripheral.podspec
├── pubspec.yaml
├── test
└── flutter_ble_peripheral_test.dart
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter_ble_peripheral_plugin.cpp
├── flutter_ble_peripheral_plugin.h
├── flutter_ble_peripheral_plugin_c_api.cpp
└── include
└── flutter_ble_peripheral
└── flutter_ble_peripheral_plugin_c_api.h
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [juliansteenbakker]
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | reviewers:
8 | - "juliansteenbakker"
9 | - package-ecosystem: gradle
10 | directory: "/android"
11 | schedule:
12 | interval: "weekly"
13 | reviewers:
14 | - "juliansteenbakker"
15 | - package-ecosystem: gradle
16 | directory: "/example/android"
17 | schedule:
18 | interval: "weekly"
19 | reviewers:
20 | - "juliansteenbakker"
21 |
--------------------------------------------------------------------------------
/.github/workflows/flutter_format.yml:
--------------------------------------------------------------------------------
1 | name: code analysis & formatting
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | analysis:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - uses: actions/setup-java@v4
11 | with:
12 | distribution: 'temurin'
13 | java-version: '11'
14 | - uses: subosito/flutter-action@v2.16.0
15 | - name: Version
16 | run: flutter doctor -v
17 | - name: Install dependencies
18 | run: flutter pub get
19 | - name: Linter
20 | run: flutter analyze
21 | formatting:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v4
25 | - uses: actions/setup-java@v4
26 | with:
27 | distribution: 'temurin'
28 | java-version: '11'
29 | - uses: subosito/flutter-action@v2.16.0
30 | - name: Format
31 | run: dart format --set-exit-if-changed .
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 | .last_build_id
13 | /.cxx/
14 | example/android/app/.cxx
15 |
16 | # IntelliJ related
17 | *.iml
18 | *.ipr
19 | *.iws
20 | .idea/
21 |
22 | # Visual Studio Code related
23 | .classpath
24 | .project
25 | .settings/
26 | .vscode/
27 |
28 | # Flutter repo-specific
29 | /bin/cache/
30 | /bin/mingit/
31 | /dev/benchmarks/mega_gallery/
32 | /dev/bots/.recipe_deps
33 | /dev/bots/android_tools/
34 | /dev/docs/doc/
35 | /dev/docs/flutter.docs.zip
36 | /dev/docs/lib/
37 | /dev/docs/pubspec.yaml
38 | /dev/integration_tests/**/xcuserdata
39 | /dev/integration_tests/**/Pods
40 | /packages/flutter/coverage/
41 | version
42 |
43 | # packages file containing multi-root paths
44 | .packages.generated
45 |
46 | # Flutter/Dart/Pub related
47 | **/doc/api/
48 | .dart_tool/
49 | .flutter-plugins
50 | .flutter-plugins-dependencies
51 | .packages
52 | .pub-cache/
53 | .pub/
54 | build/
55 | flutter_*.png
56 | linked_*.ds
57 | unlinked.ds
58 | unlinked_spec.ds
59 |
60 | # Android related
61 | **/android/**/gradle-wrapper.jar
62 | **/android/.gradle
63 | **/android/captures/
64 | **/android/gradlew
65 | **/android/gradlew.bat
66 | **/android/local.properties
67 | **/android/**/GeneratedPluginRegistrant.java
68 | **/android/key.properties
69 | **/android/property*
70 | *.jks
71 |
72 | # iOS/XCode related
73 | **/ios/**/*.mode1v3
74 | **/ios/**/*.mode2v3
75 | **/ios/**/*.moved-aside
76 | **/ios/**/*.pbxuser
77 | **/ios/**/*.perspectivev3
78 | **/ios/**/*sync/
79 | **/ios/**/.sconsign.dblite
80 | **/ios/**/.tags*
81 | **/ios/**/.vagrant/
82 | **/ios/**/DerivedData/
83 | **/ios/**/Icon?
84 | **/ios/**/Pods/
85 | **/ios/**/.symlinks/
86 | **/ios/**/profile
87 | **/ios/**/xcuserdata
88 | **/ios/.generated/
89 | **/ios/Flutter/App.framework
90 | **/ios/Flutter/Flutter.framework
91 | **/ios/Flutter/Flutter.podspec
92 | **/ios/Flutter/Generated.xcconfig
93 | **/ios/Flutter/app.flx
94 | **/ios/Flutter/app.zip
95 | **/ios/Flutter/flutter_assets/
96 | **/ios/Flutter/flutter_export_environment.sh
97 | **/ios/ServiceDefinitions.json
98 | **/ios/Runner/GeneratedPluginRegistrant.*
99 |
100 | # macOS
101 | **/macos/Flutter/GeneratedPluginRegistrant.swift
102 | **/macos/Flutter/Flutter-Debug.xcconfig
103 | **/macos/Flutter/Flutter-Release.xcconfig
104 | **/macos/Flutter/Flutter-Profile.xcconfig
105 |
106 | # Coverage
107 | coverage/
108 |
109 | # Symbols
110 | app.*.symbols
111 |
112 | # Exceptions to above rules.
113 | !**/ios/**/default.mode1v3
114 | !**/ios/**/default.mode2v3
115 | !**/ios/**/default.pbxuser
116 | !**/ios/**/default.perspectivev3
117 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
118 | !/dev/ci/**/Gemfile.lock
--------------------------------------------------------------------------------
/.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: 2ad6cd72c040113b47ee9055e722606a490ef0da
8 | channel: stable
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
17 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
18 | - platform: android
19 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
20 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
21 | - platform: ios
22 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
23 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
24 | - platform: macos
25 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
26 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
27 | - platform: windows
28 | create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
29 | base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da
30 |
31 | # User provided section
32 |
33 | # List of Local paths (relative to this file) that should be
34 | # ignored by the migrate tool.
35 | #
36 | # Files that are not part of the templates will be ignored by default.
37 | unmanaged_files:
38 | - 'lib/main.dart'
39 | - 'ios/Runner.xcodeproj/project.pbxproj'
40 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.2.6
2 | - [Android] Fixes error on start broadcasting
3 |
4 | ## 1.2.5
5 | - [Android] Added advertiseSet parameter to AdvertiseSettings to enable advertiseSet on Android o and higher devices.
6 |
7 | ## 1.2.4
8 | - [iOS & macOS] Fixed an issue which caused the first advertisement not to be broadcast.
9 |
10 | ## 1.2.3
11 | - [Android] Fixed requestPermission not working correctly.
12 |
13 | ## 1.2.2
14 | - [Android] Fixed serviceUuid not working. (thanks @Shik1266 !)
15 | - [Android] Updated compileSdk to 34.
16 |
17 | ## 1.2.1
18 | - Fix build errors & crash on Windows
19 | - Upgrade gradle to 8.1
20 |
21 | ## 1.2.0
22 | Improvements:
23 | - Added support for windows
24 | - Updated bluetooth permissions system for Android, no need for permission handler anymore.
25 | - Updated dependencies and several other small improvements.
26 |
27 | ## 1.1.1
28 | Bugs fixed:
29 | - Fixed an issue which caused the enableBluetooth function to reply twice.
30 | - Fixed analyzer issues
31 | - Upgraded dependencies
32 |
33 | ## 1.1.0
34 | Upgraded android sdk to 33.
35 | Added permission check on enableBluetooth function.
36 |
37 | ## 1.0.0
38 | Stable release including the changes noted in the beta releases.
39 | This release also updates Android dependencies.
40 |
41 | ## 1.0.0-beta.2
42 | Fixed macOS version not working
43 |
44 | ## 1.0.0-beta.1
45 | BREAKING CHANGES:
46 | You now define the data to be advertised using the AdvertiseData() constructor.
47 | AdvertiseData is the only supported object in iOS. AdvertiseSettings and other objects are only
48 | supported on Android.
49 |
50 | NEW:
51 | * You can now make use of the new startAdvertisingSet parameter on Android 26+
52 |
53 | ## 0.6.0
54 | * Refactored large parts of the code for both Android & iOS.
55 | * Upgraded Android to Android 12 permission system.
56 | * Other minor improvements
57 |
58 | ## 0.5.0+1
59 | Changes of 0.5.0 weren't visible on pub.dev
60 |
61 | ## 0.5.0
62 | Added isSupported function to check if BLE advertising is supported by the device.
63 |
64 | ## 0.4.2
65 | Fixed typo causing deviceName not to broadcast on iOS
66 |
67 | ## 0.4.1
68 | Fixed bug on iOS which led to crash
69 | Added local name to advertising in iOS
70 | Updated Android dependencies
71 |
72 | ## 0.4.0
73 | Added new options to AdvertiseData
74 | Removed embedding V1 for Android
75 |
76 | ## 0.3.0
77 | Upgraded to null-safety
78 | Updated dependencies
79 | Changed to pedantic
80 |
81 | Bug fixes
82 | * Fixed null-pointer when bluetooth adapter isn't found
83 |
84 | ## 0.2.0
85 | Add support for MacOS
86 |
87 | ## 0.1.0
88 | Fixed several parts for Android:
89 | * Advertising local name
90 | * Advertising Manufacturer Data
91 | * Advertising Service Data
92 |
93 | ## 0.0.4
94 | Fixed iOS advertising not working
95 |
96 | ## 0.0.3
97 | Fixed callback on Android
98 |
99 | ## 0.0.2
100 | Fixed flutter v2 embedding
101 |
102 | ## 0.0.1
103 | Initial version of the library. This version includes:
104 | * broadcasting a custom UUID
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright 2020 Julian Steenbakker
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://pub.dartlang.org/packages/flutter_ble_peripheral)
3 | [](https://discord.gg/XeyJZhaczm)
4 | [](https://github.com/juliansteenbakker/flutter_ble_peripheral/actions)
5 | [](https://pub.dev/packages/lint)
6 | [](https://github.com/sponsors/juliansteenbakker)
7 |
8 | # FlutterBlePeripheral
9 |
10 | This Flutter plugin allows a device to be used in Peripheral mode, and advertise data over BLE to central devices.
11 |
12 | ## Help develop this plugin!
13 |
14 | If you want to contribute to this plugin, feel free to make issues and pull-requests.
15 |
16 | ### Not stable
17 |
18 | Since this plugin is currently being developed, limited functionality will be available. Check the release page for the most recent release.
19 |
20 | | Functionality | Android | iOS | Description |
21 | |----------------------------|:------------------:|:------------------:|------------------------------------------------------|
22 | | Advertise UUID | :white_check_mark: | :white_check_mark: | Set and advertise a custom uuid. |
23 | | Advertise ManufacturerData | :white_check_mark: | :x: | Set and advertise custom data. *not possible on iOS* |
24 | | Advertise custom service | :white_check_mark: | :x: | Advertise a custom service. *not possible on iOS* |
25 |
26 | ## How to use
27 | Please check the example app to see how to broadcast data.
28 | Note that iOS does not support a lot of options. Please see `AdvertiseData` to see which options are supported by iOS & Android.
29 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lint/analysis_options_package.yaml
2 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'dev.steenbakker.flutter_ble_peripheral'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.9.20'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:8.6.1'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.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 | compileSdk 35
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_17
32 | targetCompatibility JavaVersion.VERSION_17
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '17'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 | defaultConfig {
43 | minSdkVersion 21
44 | }
45 | lintOptions {
46 | disable 'InvalidPackage'
47 | }
48 | namespace 'dev.steenbakker.flutter_ble_peripheral'
49 | }
50 |
51 | dependencies {
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Mar 18 13:29:08 CET 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_ble_peripheral'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
11 |
12 |
13 |
16 |
19 |
20 |
21 |
22 |
23 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/callbacks/PeripheralAdvertisingCallback.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.callbacks
2 |
3 | import android.bluetooth.le.AdvertiseCallback
4 | import android.bluetooth.le.AdvertiseSettings
5 | import dev.steenbakker.flutter_ble_peripheral.handlers.StateChangedHandler
6 | import dev.steenbakker.flutter_ble_peripheral.models.PeripheralState
7 | import io.flutter.Log
8 | import io.flutter.plugin.common.MethodChannel
9 |
10 | class PeripheralAdvertisingCallback(private val result: MethodChannel.Result, private val stateChangedHandler: StateChangedHandler): AdvertiseCallback() {
11 | override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
12 | super.onStartSuccess(settingsInEffect)
13 | Log.i("FlutterBlePeripheral", "onStartSuccess() mode: ${settingsInEffect.mode}, txPOWER ${settingsInEffect.txPowerLevel}")
14 | result.success(null)
15 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
16 | }
17 |
18 | override fun onStartFailure(errorCode: Int) {
19 | super.onStartFailure(errorCode)
20 | val statusText: String
21 | when (errorCode) {
22 | ADVERTISE_FAILED_ALREADY_STARTED -> {
23 | statusText = "ADVERTISE_FAILED_ALREADY_STARTED"
24 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
25 | }
26 | ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> {
27 | statusText = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED"
28 | stateChangedHandler.publishPeripheralState(PeripheralState.unsupported)
29 | }
30 | ADVERTISE_FAILED_INTERNAL_ERROR -> {
31 | statusText = "ADVERTISE_FAILED_INTERNAL_ERROR"
32 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
33 | }
34 | ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> {
35 | statusText = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"
36 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
37 | }
38 | ADVERTISE_FAILED_DATA_TOO_LARGE -> {
39 | statusText = "ADVERTISE_FAILED_DATA_TOO_LARGE"
40 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
41 | }
42 | else -> {
43 | statusText = "UNDOCUMENTED"
44 | stateChangedHandler.publishPeripheralState(PeripheralState.unknown)
45 | }
46 | }
47 | result.error(errorCode.toString(), statusText, "startAdvertising")
48 | }
49 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/callbacks/PeripheralAdvertisingSetCallback.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.callbacks
2 |
3 | import android.bluetooth.le.AdvertisingSet
4 | import android.bluetooth.le.AdvertisingSetCallback
5 | import android.bluetooth.le.BluetoothLeAdvertiser
6 | import android.os.Build
7 | import androidx.annotation.RequiresApi
8 | import dev.steenbakker.flutter_ble_peripheral.handlers.StateChangedHandler
9 | import dev.steenbakker.flutter_ble_peripheral.models.PeripheralState
10 | import io.flutter.Log
11 | import io.flutter.plugin.common.MethodChannel
12 |
13 | @RequiresApi(Build.VERSION_CODES.O)
14 | class PeripheralAdvertisingSetCallback(private val result: MethodChannel.Result, private val stateChangedHandler: StateChangedHandler): AdvertisingSetCallback() {
15 | /**
16 | * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
17 | * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet
18 | * contains the started set and it is advertising. If error occurred, advertisingSet is
19 | * null, and status will be set to proper error code.
20 | *
21 | * @param advertisingSet The advertising set that was started or null if error.
22 | * @param txPower tx power that will be used for this set.
23 | * @param status Status of the operation.
24 | */
25 |
26 | override fun onAdvertisingSetStarted(
27 | advertisingSet: AdvertisingSet?,
28 | txPower: Int,
29 | status: Int
30 | ) {
31 | Log.i("FlutterBlePeripheral", "onAdvertisingSetStarted() status: $advertisingSet, txPOWER $txPower, status $status")
32 | super.onAdvertisingSetStarted(advertisingSet, txPower, status)
33 | var statusText = ""
34 | when (status) {
35 | ADVERTISE_SUCCESS -> {
36 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
37 | }
38 | ADVERTISE_FAILED_ALREADY_STARTED -> {
39 | statusText = "ADVERTISE_FAILED_ALREADY_STARTED"
40 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
41 | }
42 | ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> {
43 | statusText = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED"
44 | stateChangedHandler.publishPeripheralState(PeripheralState.unsupported)
45 | }
46 | ADVERTISE_FAILED_INTERNAL_ERROR -> {
47 | statusText = "ADVERTISE_FAILED_INTERNAL_ERROR"
48 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
49 | }
50 | ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> {
51 | statusText = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"
52 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
53 | }
54 | ADVERTISE_FAILED_DATA_TOO_LARGE -> {
55 | statusText = "ADVERTISE_FAILED_DATA_TOO_LARGE"
56 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
57 | }
58 | else -> {
59 | statusText = "UNDOCUMENTED"
60 | stateChangedHandler.publishPeripheralState(PeripheralState.unknown)
61 | }
62 |
63 | }
64 | if (status != ADVERTISE_SUCCESS) {
65 | result.error(status.toString(), statusText, "startAdvertisingSet")
66 | } else {
67 | result.success(0)
68 | }
69 |
70 | }
71 |
72 | /**
73 | * Callback triggered in response to [BluetoothLeAdvertiser.stopAdvertisingSet]
74 | * indicating advertising set is stopped.
75 | *
76 | * @param advertisingSet The advertising set.
77 | */
78 | override fun onAdvertisingSetStopped(advertisingSet: AdvertisingSet?) {
79 | Log.i("FlutterBlePeripheral", "onAdvertisingSetStopped() status: $advertisingSet")
80 | super.onAdvertisingSetStopped(advertisingSet)
81 | stateChangedHandler.publishPeripheralState(PeripheralState.idle)
82 | }
83 |
84 | /**
85 | * Callback triggered in response to [BluetoothLeAdvertiser.startAdvertisingSet]
86 | * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is
87 | * advertising.
88 | *
89 | * @param advertisingSet The advertising set.
90 | * @param status Status of the operation.
91 | */
92 | override fun onAdvertisingEnabled(
93 | advertisingSet: AdvertisingSet?,
94 | enable: Boolean,
95 | status: Int
96 | ) {
97 | Log.i("FlutterBlePeripheral", "onAdvertisingEnabled() status: $advertisingSet, enable $enable, status $status")
98 | super.onAdvertisingEnabled(advertisingSet, enable, status)
99 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
100 | }
101 |
102 | /**
103 | * Callback triggered in response to [AdvertisingSet.setAdvertisingData] indicating
104 | * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed.
105 | *
106 | * @param advertisingSet The advertising set.
107 | * @param status Status of the operation.
108 | */
109 | override fun onAdvertisingDataSet(advertisingSet: AdvertisingSet?, status: Int) {
110 | Log.i("FlutterBlePeripheral", "onAdvertisingDataSet() status: $advertisingSet, status $status")
111 | super.onAdvertisingDataSet(advertisingSet, status)
112 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
113 | }
114 |
115 | /**
116 | * Callback triggered in response to [AdvertisingSet.setAdvertisingData] indicating
117 | * result of the operation.
118 | *
119 | * @param advertisingSet The advertising set.
120 | * @param status Status of the operation.
121 | */
122 | override fun onScanResponseDataSet(advertisingSet: AdvertisingSet?, status: Int) {
123 | Log.i("FlutterBlePeripheral", "onScanResponseDataSet() status: $advertisingSet, status $status")
124 | super.onAdvertisingDataSet(advertisingSet, status)
125 | stateChangedHandler.publishPeripheralState(PeripheralState.advertising)
126 | }
127 |
128 | /**
129 | * Callback triggered in response to [AdvertisingSet.setAdvertisingParameters]
130 | * indicating result of the operation.
131 | *
132 | * @param advertisingSet The advertising set.
133 | * @param txPower tx power that will be used for this set.
134 | * @param status Status of the operation.
135 | */
136 | override fun onAdvertisingParametersUpdated(
137 | advertisingSet: AdvertisingSet?,
138 | txPower: Int, status: Int
139 | ) {
140 | Log.i("FlutterBlePeripheral", "onAdvertisingParametersUpdated() status: $advertisingSet, txPOWER $txPower, status $status")
141 | }
142 |
143 | /**
144 | * Callback triggered in response to [AdvertisingSet.setPeriodicAdvertisingParameters]
145 | * indicating result of the operation.
146 | *
147 | * @param advertisingSet The advertising set.
148 | * @param status Status of the operation.
149 | */
150 | override fun onPeriodicAdvertisingParametersUpdated(
151 | advertisingSet: AdvertisingSet?,
152 | status: Int
153 | ) {
154 | Log.i("FlutterBlePeripheral", "onPeriodicAdvertisingParametersUpdated() status: $advertisingSet, status $status")
155 | }
156 |
157 | /**
158 | * Callback triggered in response to [AdvertisingSet.setPeriodicAdvertisingData]
159 | * indicating result of the operation.
160 | *
161 | * @param advertisingSet The advertising set.
162 | * @param status Status of the operation.
163 | */
164 | override fun onPeriodicAdvertisingDataSet(
165 | advertisingSet: AdvertisingSet?,
166 | status: Int
167 | ) {
168 | Log.i("FlutterBlePeripheral", "onPeriodicAdvertisingDataSet() status: $advertisingSet, status $status")
169 | }
170 |
171 | /**
172 | * Callback triggered in response to [AdvertisingSet.setPeriodicAdvertisingEnabled]
173 | * indicating result of the operation.
174 | *
175 | * @param advertisingSet The advertising set.
176 | * @param status Status of the operation.
177 | */
178 | override fun onPeriodicAdvertisingEnabled(
179 | advertisingSet: AdvertisingSet?, enable: Boolean,
180 | status: Int
181 | ) {
182 | Log.i("FlutterBlePeripheral", "onPeriodicAdvertisingEnabled() status: $advertisingSet, enable $enable, status $status")
183 | }
184 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/exceptions/PeripheralException.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.exceptions
2 |
3 | import dev.steenbakker.flutter_ble_peripheral.models.PeripheralState
4 | import java.lang.Exception
5 |
6 | class PeripheralException(val state: PeripheralState) : Exception(state.name)
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/exceptions/PermissionNotFoundException.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.exceptions
2 |
3 | import java.lang.Exception
4 |
5 | class PermissionNotFoundException(message: String) : Exception(message)
6 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/handlers/DataReceivedHandler.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.handlers
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import dev.steenbakker.flutter_ble_peripheral.FlutterBlePeripheralManager
6 | import io.flutter.embedding.engine.plugins.FlutterPlugin
7 | import io.flutter.plugin.common.EventChannel
8 |
9 | class DataReceivedHandler : EventChannel.StreamHandler {
10 | private var eventSink: EventChannel.EventSink? = null
11 |
12 | fun register(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, flutterBlePeripheralManager: FlutterBlePeripheralManager) {
13 | val eventChannel = EventChannel(
14 | flutterPluginBinding.binaryMessenger,
15 | "dev.steenbakker.flutter_ble_peripheral/ble_data_received"
16 | )
17 |
18 | eventChannel.setStreamHandler(this)
19 |
20 | // flutterBlePeripheralManager.onDataReceived = {
21 | //
22 | // Handler(Looper.getMainLooper()).post {
23 | // eventSink?.success(it)
24 | // }
25 | // }
26 | }
27 |
28 | override fun onListen(event: Any?, eventSink: EventChannel.EventSink?) {
29 | this.eventSink = eventSink
30 | }
31 |
32 | override fun onCancel(event: Any?) {
33 | this.eventSink = null
34 | }
35 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/handlers/MtuChangedHandler.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.handlers
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import dev.steenbakker.flutter_ble_peripheral.FlutterBlePeripheralManager
6 | import io.flutter.embedding.engine.plugins.FlutterPlugin
7 | import io.flutter.plugin.common.EventChannel
8 |
9 | class MtuChangedHandler(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, flutterBlePeripheralManager: FlutterBlePeripheralManager) : EventChannel.StreamHandler {
10 | private var eventSink: EventChannel.EventSink? = null
11 |
12 |
13 | init {
14 | val eventChannel = EventChannel(
15 | flutterPluginBinding.binaryMessenger,
16 | "dev.steenbakker.flutter_ble_peripheral/ble_mtu_changed"
17 | )
18 |
19 | eventChannel.setStreamHandler(this)
20 |
21 | // flutterBlePeripheralManager.onMtuChanged = { mtu ->
22 | // Handler(Looper.getMainLooper()).post {
23 | // eventSink?.success(mtu)
24 | // }
25 | // }
26 | }
27 |
28 | fun register(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, flutterBlePeripheralManager: FlutterBlePeripheralManager) {
29 | val eventChannel = EventChannel(
30 | flutterPluginBinding.binaryMessenger,
31 | "dev.steenbakker.flutter_ble_peripheral/ble_mtu_changed"
32 | )
33 |
34 | eventChannel.setStreamHandler(this)
35 |
36 | // flutterBlePeripheralManager.onMtuChanged = { mtu ->
37 | // Handler(Looper.getMainLooper()).post {
38 | // eventSink?.success(mtu)
39 | // }
40 | // }
41 | }
42 |
43 | override fun onListen(event: Any?, eventSink: EventChannel.EventSink?) {
44 | this.eventSink = eventSink
45 | }
46 |
47 | override fun onCancel(event: Any?) {
48 | this.eventSink = null
49 | }
50 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/handlers/StateChangedHandler.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.handlers
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import dev.steenbakker.flutter_ble_peripheral.models.PeripheralState
6 | import io.flutter.Log
7 | import io.flutter.embedding.engine.plugins.FlutterPlugin
8 | import io.flutter.plugin.common.EventChannel
9 |
10 | class StateChangedHandler(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) : EventChannel.StreamHandler {
11 | private val tag: String = "BLE Peripheral state "
12 |
13 | private var eventSink: EventChannel.EventSink? = null
14 |
15 | var state = PeripheralState.idle
16 |
17 | private val eventChannel = EventChannel(
18 | flutterPluginBinding.binaryMessenger,
19 | "dev.steenbakker.flutter_ble_peripheral/ble_state_changed"
20 | )
21 |
22 | init {
23 | eventChannel.setStreamHandler(this)
24 | }
25 |
26 | // fun register(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
27 | // val eventChannel = EventChannel(
28 | // flutterPluginBinding.binaryMessenger,
29 | // "dev.steenbakker.flutter_ble_peripheral/ble_state_changed"
30 | // )
31 | // eventChannel.setStreamHandler(this)
32 | // }
33 |
34 | fun publishPeripheralState(state: PeripheralState) {
35 | Log.i(tag, state.name)
36 | this.state = state
37 | Handler(Looper.getMainLooper()).post {
38 | eventSink?.success(state.ordinal)
39 | }
40 | }
41 |
42 | override fun onListen(event: Any?, eventSink: EventChannel.EventSink?) {
43 | this.eventSink = eventSink
44 | publishPeripheralState(state)
45 | }
46 |
47 | override fun onCancel(event: Any?) {
48 | this.eventSink = null
49 | }
50 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/models/PeripheralState.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.models
2 |
3 | enum class PeripheralState {
4 | /// Status is not (yet) determined.
5 | unknown,
6 |
7 | /// BLE is not supported on this device.
8 | unsupported,
9 |
10 | /// BLE usage is not authorized for this app.
11 | unauthorized,
12 |
13 | /// BLE is turned off.
14 | poweredOff,
15 |
16 | // /// Android only: Location services are disabled.
17 | // locationServicesDisabled,
18 |
19 | /// BLE is fully operating for this app.
20 | idle,
21 |
22 | /// BLE is advertising data.
23 | advertising,
24 |
25 | /// BLE is connected to a device.
26 | connected,
27 |
28 | shouldShowRequestPermissionRationale,
29 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/models/PermissionState.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.models
2 |
3 | enum class PermissionState(val nr: Int) {
4 | /// Status is not (yet) determined.
5 | Granted(0),
6 |
7 | /// BLE is not supported on this device.
8 | Denied(1),
9 |
10 | /// BLE usage is not authorized for this app.
11 | PermanentlyDenied(2),
12 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/dev/steenbakker/flutter_ble_peripheral/models/State.kt:
--------------------------------------------------------------------------------
1 | package dev.steenbakker.flutter_ble_peripheral.models
2 |
3 | /*
4 | * Copyright (c) 2023. Julian Steenbakker.
5 | * All rights reserved. Use of this source code is governed by a
6 | * BSD-style license that can be found in the LICENSE file.
7 | */
8 |
9 | enum class State(val value: Int) {
10 | /// The user granted access to the requested feature.
11 | Granted(1), /// The user denied access to the requested feature, permission needs to be asked first.
12 | Denied(2), /// Permission to the requested feature is permanently denied,
13 |
14 | /// the permission dialog will not be shown when requesting this permission.
15 | /// The user may still change the permission status in the settings.
16 | PermanentlyDenied(3), /// The status is unknown
17 |
18 |
19 | /// The user cannot change this app's status, possibly due to active restrictions such as parental controls being in place.
20 | ///
21 | /// Only supported on iOS.
22 | Restricted(4), /// User has authorized this application for limited access.
23 |
24 | /// Only supported on iOS (iOS14+).
25 | Limited(5), /// Bluetooth is turned off
26 | TurnedOff(6),
27 | Unsupported(7),
28 | Unknown(8),
29 | Ready(9),
30 | }
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
--------------------------------------------------------------------------------
/example/.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 and should not be manually edited.
5 |
6 | version:
7 | revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_ble_peripheral_example
2 |
3 | Demonstrates how to use the flutter_ble_peripheral plugin.
4 |
5 | ## Example
6 |
7 | ```dart
8 | /*
9 | * Copyright (c) 2020. Julian Steenbakker.
10 | * All rights reserved. Use of this source code is governed by a
11 | * BSD-style license that can be found in the LICENSE file.
12 | */
13 |
14 | import 'package:flutter/material.dart';
15 | import 'package:flutter_ble_peripheral/flutter_ble_peripheral.dart';
16 |
17 | void main() => runApp(FlutterBlePeripheralExample());
18 |
19 | class FlutterBlePeripheralExample extends StatefulWidget {
20 | @override
21 | _FlutterBlePeripheralExampleState createState() =>
22 | _FlutterBlePeripheralExampleState();
23 | }
24 |
25 | class _FlutterBlePeripheralExampleState
26 | extends State {
27 | final FlutterBlePeripheral blePeripheral = FlutterBlePeripheral();
28 | final AdvertiseData _data = AdvertiseData();
29 | bool _isBroadcasting = false;
30 |
31 | @override
32 | void initState() {
33 | super.initState();
34 | setState(() {
35 | _data.includeDeviceName = false;
36 | _data.uuid = 'bf27730d-860a-4e09-889c-2d8b6a9e0fe7';
37 | _data.manufacturerId = 1234;
38 | _data.manufacturerData = [1, 2, 3, 4, 5, 6];
39 | _data.txPowerLevel = AdvertisePower.ADVERTISE_TX_POWER_ULTRA_LOW;
40 | _data.advertiseMode = AdvertiseMode.ADVERTISE_MODE_LOW_LATENCY;
41 | });
42 | initPlatformState();
43 | }
44 |
45 | Future initPlatformState() async {
46 | var isAdvertising = await blePeripheral.isAdvertising();
47 | setState(() {
48 | _isBroadcasting = isAdvertising;
49 | });
50 | }
51 |
52 | void _toggleAdvertise() async {
53 | if (await blePeripheral.isAdvertising()) {
54 | await blePeripheral.stop();
55 | setState(() {
56 | _isBroadcasting = false;
57 | });
58 | } else {
59 | await blePeripheral.start(_data);
60 | setState(() {
61 | _isBroadcasting = true;
62 | });
63 | }
64 | }
65 |
66 | @override
67 | Widget build(BuildContext context) {
68 | return MaterialApp(
69 | home: Scaffold(
70 | appBar: AppBar(
71 | title: const Text('Flutter BLE Peripheral'),
72 | ),
73 | body: Center(
74 | child: Column(
75 | mainAxisAlignment: MainAxisAlignment.center,
76 | crossAxisAlignment: CrossAxisAlignment.center,
77 | children: [
78 | Text('Is advertising: $_isBroadcasting'),
79 | StreamBuilder(
80 | stream: blePeripheral.getAdvertisingStateChange(),
81 | initialData: 'Advertisement not started.',
82 | builder:
83 | (BuildContext context, AsyncSnapshot snapshot) {
84 | return Text('Is advertising stream: ${snapshot.data}');
85 | }),
86 | Text('Current uuid is ${_data.uuid}'),
87 | MaterialButton(
88 | onPressed: _toggleAdvertise,
89 | child: Text(
90 | 'Toggle advertising',
91 | style: Theme.of(context)
92 | .primaryTextTheme
93 | .button!
94 | .copyWith(color: Colors.blue),
95 | )),
96 | ])),
97 | ),
98 | );
99 | }
100 | }
101 | ```
102 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lint/analysis_options.yaml
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/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 flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 |
26 |
27 | android {
28 | compileSdkVersion 35
29 |
30 | sourceSets {
31 | main.java.srcDirs += 'src/main/kotlin'
32 | }
33 |
34 | defaultConfig {
35 | applicationId "dev.steenbakker.flutter_ble_peripheral_example"
36 | minSdkVersion 21
37 | targetSdkVersion 35
38 | versionCode flutterVersionCode.toInteger()
39 | versionName flutterVersionName
40 | }
41 |
42 | buildTypes {
43 | release {
44 | // TODO: Add your own signing config for the release build.
45 | // Signing with the debug keys for now, so `flutter run --release` works.
46 | signingConfig signingConfigs.debug
47 | }
48 | }
49 | lint {
50 | disable 'InvalidPackage'
51 | }
52 | namespace 'dev.steenbakker.flutter_ble_peripheral_example'
53 | }
54 |
55 | flutter {
56 | source '../..'
57 | }
58 |
59 | dependencies {
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
8 |
11 |
12 |
13 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
36 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
51 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/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/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.defaults.buildfeatures.buildconfig=true
5 | android.nonTransitiveRClass=false
6 | android.nonFinalResIds=false
7 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Mar 13 13:19:01 CET 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/example/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class android.bluetooth.le.** { *; }
--------------------------------------------------------------------------------
/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.9.0' apply false
22 | id "org.jetbrains.kotlin.android" version "1.9.20" apply false
23 | }
24 |
25 | include ":app"
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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/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 |
--------------------------------------------------------------------------------
/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/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.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/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/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/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/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/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/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/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/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/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_ble_peripheral_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | NSBluetoothAlwaysUsageDescription
26 | Bluetooth is required in order to advertise via BLE
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/ble_status_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_ble_peripheral/flutter_ble_peripheral.dart';
3 |
4 | class BleStatusScreen extends StatelessWidget {
5 | const BleStatusScreen({required this.status, Key? key}) : super(key: key);
6 |
7 | final PeripheralState status;
8 | // idle, advertising, connected, unsupported, unauthorized }
9 | String determineText(PeripheralState status) {
10 | switch (status) {
11 | case PeripheralState.unsupported:
12 | return "This device does not support Bluetooth";
13 | case PeripheralState.unauthorized:
14 | return "Authorize the BlePeripheral example app to use Bluetooth and location";
15 | case PeripheralState.poweredOff:
16 | return "Bluetooth is powered off on your device turn it on";
17 | // case PeripheralState.unauthorized:
18 | // return "Enable location services";
19 | case PeripheralState.idle:
20 | return "Bluetooth is up and running";
21 | default:
22 | return "Waiting to fetch Bluetooth status $status";
23 | }
24 | }
25 |
26 | @override
27 | Widget build(BuildContext context) => Scaffold(
28 | body: Center(
29 | child: Text(determineText(status)),
30 | ),
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/example/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/xcuserdata/
7 |
--------------------------------------------------------------------------------
/example/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.14'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | end
35 |
36 | post_install do |installer|
37 | installer.pods_project.targets.each do |target|
38 | flutter_additional_macos_build_settings(target)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = flutter_ble_peripheral_example
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.flutterBlePeripheralExample
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2020 dev.steenbakker. All rights reserved.
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/example/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.device.bluetooth
10 |
11 | com.apple.security.network.server
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSBluetoothAlwaysUsageDescription
6 | This app needs bluetooth in order to advertise data.
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIconFile
12 |
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | $(PRODUCT_COPYRIGHT)
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController.init()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.device.bluetooth
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_ble_peripheral_example
2 | description: Demonstrates how to use the flutter_ble_peripheral plugin.
3 | publish_to: 'none'
4 |
5 | environment:
6 | sdk: '>=2.15.0 <3.0.0'
7 | flutter: ">=1.10.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | flutter_ble_peripheral:
13 | path: ../
14 |
15 | dev_dependencies:
16 | flutter_test:
17 | sdk: flutter
18 | lint: ^2.0.1
19 |
20 | flutter:
21 | uses-material-design: true
--------------------------------------------------------------------------------
/example/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/example/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Project-level configuration.
2 | cmake_minimum_required(VERSION 3.14)
3 | project(flutter_ble_peripheral_example LANGUAGES CXX)
4 |
5 | # The name of the executable created for the application. Change this to change
6 | # the on-disk name of your application.
7 | set(BINARY_NAME "flutter_ble_peripheral_example")
8 |
9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
10 | # versions of CMake.
11 | cmake_policy(SET CMP0063 NEW)
12 |
13 | # Define build configuration option.
14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
15 | if(IS_MULTICONFIG)
16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
17 | CACHE STRING "" FORCE)
18 | else()
19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
20 | set(CMAKE_BUILD_TYPE "Debug" CACHE
21 | STRING "Flutter build mode" FORCE)
22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
23 | "Debug" "Profile" "Release")
24 | endif()
25 | endif()
26 | # Define settings for the Profile build mode.
27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
31 |
32 | # Use Unicode for all projects.
33 | add_definitions(-DUNICODE -D_UNICODE)
34 |
35 | # Compilation settings that should be applied to most targets.
36 | #
37 | # Be cautious about adding new options here, as plugins use this function by
38 | # default. In most cases, you should add new options to specific targets instead
39 | # of modifying this function.
40 | function(APPLY_STANDARD_SETTINGS TARGET)
41 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
43 | target_compile_options(${TARGET} PRIVATE /EHsc)
44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
46 | endfunction()
47 |
48 | # Flutter library and tool build rules.
49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
50 | add_subdirectory(${FLUTTER_MANAGED_DIR})
51 |
52 | # Application build; see runner/CMakeLists.txt.
53 | add_subdirectory("runner")
54 |
55 | # Generated plugin build rules, which manage building the plugins and adding
56 | # them to the application.
57 | include(flutter/generated_plugins.cmake)
58 |
59 |
60 | # === Installation ===
61 | # Support files are copied into place next to the executable, so that it can
62 | # run in place. This is done instead of making a separate bundle (as on Linux)
63 | # so that building and running from within Visual Studio will work.
64 | set(BUILD_BUNDLE_DIR "$")
65 | # Make the "install" step default, as it's required to run.
66 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
67 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
68 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
69 | endif()
70 |
71 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
72 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
73 |
74 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
75 | COMPONENT Runtime)
76 |
77 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
78 | COMPONENT Runtime)
79 |
80 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
81 | COMPONENT Runtime)
82 |
83 | if(PLUGIN_BUNDLED_LIBRARIES)
84 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
85 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
86 | COMPONENT Runtime)
87 | endif()
88 |
89 | # Fully re-copy the assets directory on each build to avoid having stale files
90 | # from a previous install.
91 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
92 | install(CODE "
93 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
94 | " COMPONENT Runtime)
95 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
96 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
97 |
98 | # Install the AOT library on non-Debug builds only.
99 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
100 | CONFIGURATIONS Profile;Release
101 | COMPONENT Runtime)
102 |
--------------------------------------------------------------------------------
/example/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file controls Flutter-level build steps. It should not be edited.
2 | cmake_minimum_required(VERSION 3.14)
3 |
4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
5 |
6 | # Configuration provided via flutter tool.
7 | include(${EPHEMERAL_DIR}/generated_config.cmake)
8 |
9 | # TODO: Move the rest of this into files in ephemeral. See
10 | # https://github.com/flutter/flutter/issues/57146.
11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
12 |
13 | # === Flutter Library ===
14 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
15 |
16 | # Published to parent scope for install step.
17 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
18 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
19 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
20 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
21 |
22 | list(APPEND FLUTTER_LIBRARY_HEADERS
23 | "flutter_export.h"
24 | "flutter_windows.h"
25 | "flutter_messenger.h"
26 | "flutter_plugin_registrar.h"
27 | "flutter_texture_registrar.h"
28 | )
29 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
30 | add_library(flutter INTERFACE)
31 | target_include_directories(flutter INTERFACE
32 | "${EPHEMERAL_DIR}"
33 | )
34 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
35 | add_dependencies(flutter flutter_assemble)
36 |
37 | # === Wrapper ===
38 | list(APPEND CPP_WRAPPER_SOURCES_CORE
39 | "core_implementations.cc"
40 | "standard_codec.cc"
41 | )
42 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
43 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
44 | "plugin_registrar.cc"
45 | )
46 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
47 | list(APPEND CPP_WRAPPER_SOURCES_APP
48 | "flutter_engine.cc"
49 | "flutter_view_controller.cc"
50 | )
51 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
52 |
53 | # Wrapper sources needed for a plugin.
54 | add_library(flutter_wrapper_plugin STATIC
55 | ${CPP_WRAPPER_SOURCES_CORE}
56 | ${CPP_WRAPPER_SOURCES_PLUGIN}
57 | )
58 | apply_standard_settings(flutter_wrapper_plugin)
59 | set_target_properties(flutter_wrapper_plugin PROPERTIES
60 | POSITION_INDEPENDENT_CODE ON)
61 | set_target_properties(flutter_wrapper_plugin PROPERTIES
62 | CXX_VISIBILITY_PRESET hidden)
63 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
64 | target_include_directories(flutter_wrapper_plugin PUBLIC
65 | "${WRAPPER_ROOT}/include"
66 | )
67 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
68 |
69 | # Wrapper sources needed for the runner.
70 | add_library(flutter_wrapper_app STATIC
71 | ${CPP_WRAPPER_SOURCES_CORE}
72 | ${CPP_WRAPPER_SOURCES_APP}
73 | )
74 | apply_standard_settings(flutter_wrapper_app)
75 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
76 | target_include_directories(flutter_wrapper_app PUBLIC
77 | "${WRAPPER_ROOT}/include"
78 | )
79 | add_dependencies(flutter_wrapper_app flutter_assemble)
80 |
81 | # === Flutter tool backend ===
82 | # _phony_ is a non-existent file to force this command to run every time,
83 | # since currently there's no way to get a full input/output list from the
84 | # flutter tool.
85 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
86 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
87 | add_custom_command(
88 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
89 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
90 | ${CPP_WRAPPER_SOURCES_APP}
91 | ${PHONY_OUTPUT}
92 | COMMAND ${CMAKE_COMMAND} -E env
93 | ${FLUTTER_TOOL_ENVIRONMENT}
94 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
95 | windows-x64 $
96 | VERBATIM
97 | )
98 | add_custom_target(flutter_assemble DEPENDS
99 | "${FLUTTER_LIBRARY}"
100 | ${FLUTTER_LIBRARY_HEADERS}
101 | ${CPP_WRAPPER_SOURCES_CORE}
102 | ${CPP_WRAPPER_SOURCES_PLUGIN}
103 | ${CPP_WRAPPER_SOURCES_APP}
104 | )
105 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 |
11 | void RegisterPlugins(flutter::PluginRegistry* registry) {
12 | FlutterBlePeripheralPluginCApiRegisterWithRegistrar(
13 | registry->GetRegistrarForPlugin("FlutterBlePeripheralPluginCApi"));
14 | }
15 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | flutter_ble_peripheral
7 | )
8 |
9 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
10 | )
11 |
12 | set(PLUGIN_BUNDLED_LIBRARIES)
13 |
14 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
19 | endforeach(plugin)
20 |
21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
24 | endforeach(ffi_plugin)
25 |
--------------------------------------------------------------------------------
/example/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.14)
2 | project(runner LANGUAGES CXX)
3 |
4 | # Define the application target. To change its name, change BINARY_NAME in the
5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
6 | # work.
7 | #
8 | # Any new source files that you add to the application should be added here.
9 | add_executable(${BINARY_NAME} WIN32
10 | "flutter_window.cpp"
11 | "main.cpp"
12 | "utils.cpp"
13 | "win32_window.cpp"
14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
15 | "Runner.rc"
16 | "runner.exe.manifest"
17 | )
18 |
19 | # Apply the standard set of build settings. This can be removed for applications
20 | # that need different build settings.
21 | apply_standard_settings(${BINARY_NAME})
22 |
23 | # Add preprocessor definitions for the build version.
24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
29 |
30 | # Disable Windows macros that collide with C++ standard library functions.
31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
32 |
33 | # Add dependency libraries and include directories. Add any application-specific
34 | # dependencies here.
35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
38 |
39 | # Run the Flutter tool portions of the build. This must not be removed.
40 | add_dependencies(${BINARY_NAME} flutter_assemble)
41 |
--------------------------------------------------------------------------------
/example/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 | #include "resource.h"
5 |
6 | #define APSTUDIO_READONLY_SYMBOLS
7 | /////////////////////////////////////////////////////////////////////////////
8 | //
9 | // Generated from the TEXTINCLUDE 2 resource.
10 | //
11 | #include "winres.h"
12 |
13 | /////////////////////////////////////////////////////////////////////////////
14 | #undef APSTUDIO_READONLY_SYMBOLS
15 |
16 | /////////////////////////////////////////////////////////////////////////////
17 | // English (United States) resources
18 |
19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 |
22 | #ifdef APSTUDIO_INVOKED
23 | /////////////////////////////////////////////////////////////////////////////
24 | //
25 | // TEXTINCLUDE
26 | //
27 |
28 | 1 TEXTINCLUDE
29 | BEGIN
30 | "resource.h\0"
31 | END
32 |
33 | 2 TEXTINCLUDE
34 | BEGIN
35 | "#include ""winres.h""\r\n"
36 | "\0"
37 | END
38 |
39 | 3 TEXTINCLUDE
40 | BEGIN
41 | "\r\n"
42 | "\0"
43 | END
44 |
45 | #endif // APSTUDIO_INVOKED
46 |
47 |
48 | /////////////////////////////////////////////////////////////////////////////
49 | //
50 | // Icon
51 | //
52 |
53 | // Icon with lowest ID value placed first to ensure application icon
54 | // remains consistent on all systems.
55 | IDI_APP_ICON ICON "resources\\app_icon.ico"
56 |
57 |
58 | /////////////////////////////////////////////////////////////////////////////
59 | //
60 | // Version
61 | //
62 |
63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
65 | #else
66 | #define VERSION_AS_NUMBER 1,0,0,0
67 | #endif
68 |
69 | #if defined(FLUTTER_VERSION)
70 | #define VERSION_AS_STRING FLUTTER_VERSION
71 | #else
72 | #define VERSION_AS_STRING "1.0.0"
73 | #endif
74 |
75 | VS_VERSION_INFO VERSIONINFO
76 | FILEVERSION VERSION_AS_NUMBER
77 | PRODUCTVERSION VERSION_AS_NUMBER
78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
79 | #ifdef _DEBUG
80 | FILEFLAGS VS_FF_DEBUG
81 | #else
82 | FILEFLAGS 0x0L
83 | #endif
84 | FILEOS VOS__WINDOWS32
85 | FILETYPE VFT_APP
86 | FILESUBTYPE 0x0L
87 | BEGIN
88 | BLOCK "StringFileInfo"
89 | BEGIN
90 | BLOCK "040904e4"
91 | BEGIN
92 | VALUE "CompanyName", "dev.steenbakker" "\0"
93 | VALUE "FileDescription", "flutter_ble_peripheral_example" "\0"
94 | VALUE "FileVersion", VERSION_AS_STRING "\0"
95 | VALUE "InternalName", "flutter_ble_peripheral_example" "\0"
96 | VALUE "LegalCopyright", "Copyright (C) 2023 dev.steenbakker. All rights reserved." "\0"
97 | VALUE "OriginalFilename", "flutter_ble_peripheral_example.exe" "\0"
98 | VALUE "ProductName", "flutter_ble_peripheral_example" "\0"
99 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
100 | END
101 | END
102 | BLOCK "VarFileInfo"
103 | BEGIN
104 | VALUE "Translation", 0x409, 1252
105 | END
106 | END
107 |
108 | #endif // English (United States) resources
109 | /////////////////////////////////////////////////////////////////////////////
110 |
111 |
112 |
113 | #ifndef APSTUDIO_INVOKED
114 | /////////////////////////////////////////////////////////////////////////////
115 | //
116 | // Generated from the TEXTINCLUDE 3 resource.
117 | //
118 |
119 |
120 | /////////////////////////////////////////////////////////////////////////////
121 | #endif // not APSTUDIO_INVOKED
122 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project)
8 | : project_(project) {}
9 |
10 | FlutterWindow::~FlutterWindow() {}
11 |
12 | bool FlutterWindow::OnCreate() {
13 | if (!Win32Window::OnCreate()) {
14 | return false;
15 | }
16 |
17 | RECT frame = GetClientArea();
18 |
19 | // The size here must match the window dimensions to avoid unnecessary surface
20 | // creation / destruction in the startup path.
21 | flutter_controller_ = std::make_unique(
22 | frame.right - frame.left, frame.bottom - frame.top, project_);
23 | // Ensure that basic setup of the controller was successful.
24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
25 | return false;
26 | }
27 | RegisterPlugins(flutter_controller_->engine());
28 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
29 |
30 | flutter_controller_->engine()->SetNextFrameCallback([&]() {
31 | this->Show();
32 | });
33 |
34 | // Flutter can complete the first frame before the "show window" callback is
35 | // registered. The following call ensures a frame is pending to ensure the
36 | // window is shown. It is a no-op if the first frame hasn't completed yet.
37 | flutter_controller_->ForceRedraw();
38 |
39 | return true;
40 | }
41 |
42 | void FlutterWindow::OnDestroy() {
43 | if (flutter_controller_) {
44 | flutter_controller_ = nullptr;
45 | }
46 |
47 | Win32Window::OnDestroy();
48 | }
49 |
50 | LRESULT
51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
52 | WPARAM const wparam,
53 | LPARAM const lparam) noexcept {
54 | // Give Flutter, including plugins, an opportunity to handle window messages.
55 | if (flutter_controller_) {
56 | std::optional result =
57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
58 | lparam);
59 | if (result) {
60 | return *result;
61 | }
62 | }
63 |
64 | switch (message) {
65 | case WM_FONTCHANGE:
66 | flutter_controller_->engine()->ReloadSystemFonts();
67 | break;
68 | }
69 |
70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
71 | }
72 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "win32_window.h"
10 |
11 | // A window that does nothing but host a Flutter view.
12 | class FlutterWindow : public Win32Window {
13 | public:
14 | // Creates a new FlutterWindow hosting a Flutter view running |project|.
15 | explicit FlutterWindow(const flutter::DartProject& project);
16 | virtual ~FlutterWindow();
17 |
18 | protected:
19 | // Win32Window:
20 | bool OnCreate() override;
21 | void OnDestroy() override;
22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
23 | LPARAM const lparam) noexcept override;
24 |
25 | private:
26 | // The project to run.
27 | flutter::DartProject project_;
28 |
29 | // The Flutter instance hosted by this window.
30 | std::unique_ptr flutter_controller_;
31 | };
32 |
33 | #endif // RUNNER_FLUTTER_WINDOW_H_
34 |
--------------------------------------------------------------------------------
/example/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "utils.h"
7 |
8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
9 | _In_ wchar_t *command_line, _In_ int show_command) {
10 | // Attach to console when present (e.g., 'flutter run') or create a
11 | // new console when running with a debugger.
12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
13 | CreateAndAttachConsole();
14 | }
15 |
16 | // Initialize COM, so that it is available for use in the library and/or
17 | // plugins.
18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
19 |
20 | flutter::DartProject project(L"data");
21 |
22 | std::vector command_line_arguments =
23 | GetCommandLineArguments();
24 |
25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
26 |
27 | FlutterWindow window(project);
28 | Win32Window::Point origin(10, 10);
29 | Win32Window::Size size(1280, 720);
30 | if (!window.Create(L"flutter_ble_peripheral_example", origin, size)) {
31 | return EXIT_FAILURE;
32 | }
33 | window.SetQuitOnClose(true);
34 |
35 | ::MSG msg;
36 | while (::GetMessage(&msg, nullptr, 0, 0)) {
37 | ::TranslateMessage(&msg);
38 | ::DispatchMessage(&msg);
39 | }
40 |
41 | ::CoUninitialize();
42 | return EXIT_SUCCESS;
43 | }
44 |
--------------------------------------------------------------------------------
/example/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/example/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/example/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/example/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
24 | std::vector GetCommandLineArguments() {
25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
26 | int argc;
27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
28 | if (argv == nullptr) {
29 | return std::vector();
30 | }
31 |
32 | std::vector command_line_arguments;
33 |
34 | // Skip the first argument as it's the binary name.
35 | for (int i = 1; i < argc; i++) {
36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
37 | }
38 |
39 | ::LocalFree(argv);
40 |
41 | return command_line_arguments;
42 | }
43 |
44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) {
45 | if (utf16_string == nullptr) {
46 | return std::string();
47 | }
48 | int target_length = ::WideCharToMultiByte(
49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
50 | -1, nullptr, 0, nullptr, nullptr);
51 | std::string utf8_string;
52 | if (target_length == 0 || target_length > utf8_string.max_size()) {
53 | return utf8_string;
54 | }
55 | utf8_string.resize(target_length);
56 | int converted_length = ::WideCharToMultiByte(
57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
58 | -1, utf8_string.data(),
59 | target_length, nullptr, nullptr);
60 | if (converted_length == 0) {
61 | return std::string();
62 | }
63 | return utf8_string;
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_WIN32_WINDOW_H_
2 | #define RUNNER_WIN32_WINDOW_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be
11 | // inherited from by classes that wish to specialize with custom
12 | // rendering and input handling
13 | class Win32Window {
14 | public:
15 | struct Point {
16 | unsigned int x;
17 | unsigned int y;
18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
19 | };
20 |
21 | struct Size {
22 | unsigned int width;
23 | unsigned int height;
24 | Size(unsigned int width, unsigned int height)
25 | : width(width), height(height) {}
26 | };
27 |
28 | Win32Window();
29 | virtual ~Win32Window();
30 |
31 | // Creates a win32 window with |title| that is positioned and sized using
32 | // |origin| and |size|. New windows are created on the default monitor. Window
33 | // sizes are specified to the OS in physical pixels, hence to ensure a
34 | // consistent size this function will scale the inputted width and height as
35 | // as appropriate for the default monitor. The window is invisible until
36 | // |Show| is called. Returns true if the window was created successfully.
37 | bool Create(const std::wstring& title, const Point& origin, const Size& size);
38 |
39 | // Show the current window. Returns true if the window was successfully shown.
40 | bool Show();
41 |
42 | // Release OS resources associated with window.
43 | void Destroy();
44 |
45 | // Inserts |content| into the window tree.
46 | void SetChildContent(HWND content);
47 |
48 | // Returns the backing Window handle to enable clients to set icon and other
49 | // window properties. Returns nullptr if the window has been destroyed.
50 | HWND GetHandle();
51 |
52 | // If true, closing this window will quit the application.
53 | void SetQuitOnClose(bool quit_on_close);
54 |
55 | // Return a RECT representing the bounds of the current client area.
56 | RECT GetClientArea();
57 |
58 | protected:
59 | // Processes and route salient window messages for mouse handling,
60 | // size change and DPI. Delegates handling of these to member overloads that
61 | // inheriting classes can handle.
62 | virtual LRESULT MessageHandler(HWND window,
63 | UINT const message,
64 | WPARAM const wparam,
65 | LPARAM const lparam) noexcept;
66 |
67 | // Called when CreateAndShow is called, allowing subclass window-related
68 | // setup. Subclasses should return false if setup fails.
69 | virtual bool OnCreate();
70 |
71 | // Called when Destroy is called.
72 | virtual void OnDestroy();
73 |
74 | private:
75 | friend class WindowClassRegistrar;
76 |
77 | // OS callback called by message pump. Handles the WM_NCCREATE message which
78 | // is passed when the non-client area is being created and enables automatic
79 | // non-client DPI scaling so that the non-client area automatically
80 | // responsponds to changes in DPI. All other messages are handled by
81 | // MessageHandler.
82 | static LRESULT CALLBACK WndProc(HWND const window,
83 | UINT const message,
84 | WPARAM const wparam,
85 | LPARAM const lparam) noexcept;
86 |
87 | // Retrieves a class instance pointer for |window|
88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept;
89 |
90 | // Update the window frame's theme to match the system theme.
91 | static void UpdateTheme(HWND const window);
92 |
93 | bool quit_on_close_ = false;
94 |
95 | // window handle for top level window.
96 | HWND window_handle_ = nullptr;
97 |
98 | // window handle for hosted content.
99 | HWND child_content_ = nullptr;
100 | };
101 |
102 | #endif // RUNNER_WIN32_WINDOW_H_
103 |
--------------------------------------------------------------------------------
/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/flutter_export_environment.sh
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliansteenbakker/flutter_ble_peripheral/ac20d56287487cf2576daa1c4886bf05e1e84a7b/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/FlutterBlePeripheralManager.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 |
8 | import Foundation
9 | import CoreBluetooth
10 | import CoreLocation
11 |
12 | class FlutterBlePeripheralManager : NSObject {
13 |
14 | let stateChangedHandler: StateChangedHandler
15 | var peripheralManager : CBPeripheralManager!
16 |
17 | init(stateChangedHandler: StateChangedHandler) {
18 | self.stateChangedHandler = stateChangedHandler
19 | super.init()
20 | peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: [CBPeripheralManagerOptionShowPowerAlertKey : true])
21 | }
22 |
23 | // var peripheralData: NSDictionary!
24 |
25 | // min MTU before iOS 10
26 | // var mtu: Int = 158 {
27 | // didSet {
28 | // onMtuChanged?(mtu)
29 | // }
30 | // }
31 |
32 | // var dataToBeAdvertised: [String: Any]!
33 | //
34 | // var txCharacteristic: CBMutableCharacteristic?
35 | // var txSubscribed = false {
36 | // didSet {
37 | // if txSubscribed {
38 | // state = .connected
39 | // } else if isAdvertising() {
40 | // state = .advertising
41 | // }
42 | // }
43 | // }
44 | // var rxCharacteristic: CBMutableCharacteristic?
45 | //
46 | // var txSubscriptions = Set()
47 |
48 | func start(advertiseData: PeripheralData) {
49 | var dataToBeAdvertised: [String: Any]! = [:]
50 | if (advertiseData.uuids != nil) {
51 | dataToBeAdvertised[CBAdvertisementDataServiceUUIDsKey] = advertiseData.uuids!.map { CBUUID(string: $0) }
52 | } else if (advertiseData.uuid != nil) {
53 | dataToBeAdvertised[CBAdvertisementDataServiceUUIDsKey] = [CBUUID(string: advertiseData.uuid!)]
54 | }
55 |
56 | if (advertiseData.localName != nil) {
57 | dataToBeAdvertised[CBAdvertisementDataLocalNameKey] = advertiseData.localName
58 | }
59 |
60 | print("[flutter_ble_peripheral] start advertising data: \(String(describing: dataToBeAdvertised))")
61 |
62 | peripheralManager.startAdvertising(dataToBeAdvertised)
63 |
64 | // TODO: Add service to advertise
65 | // if peripheralManager.state == .poweredOn {
66 | // addService()
67 | // }
68 | }
69 |
70 | // TODO: Add service to advertise
71 | // private func addService() {
72 | // // Add service and characteristics if needed
73 | // if txCharacteristic == nil || rxCharacteristic == nil {
74 | //
75 | // let mutableTxCharacteristic = CBMutableCharacteristic(type: CBUUID(string: PeripheralData.txCharacteristicUUID), properties: [.read, .write, .notify], value: nil, permissions: [.readable, .writeable])
76 | // let mutableRxCharacteristic = CBMutableCharacteristic(type: CBUUID(string: PeripheralData.rxCharacteristicUUID), properties: [.read, .write, .notify], value: nil, permissions: [.readable, .writeable])
77 | //
78 | // let service = CBMutableService(type: CBUUID(string: PeripheralData.serviceUUID), primary: true)
79 | // service.characteristics = [mutableTxCharacteristic, mutableRxCharacteristic];
80 | //
81 | // peripheralManager.add(service)
82 | //
83 | // self.txCharacteristic = mutableTxCharacteristic
84 | // self.rxCharacteristic = mutableRxCharacteristic
85 | // }
86 | //
87 | // peripheralManager.startAdvertising(dataToBeAdvertised)
88 | // }
89 | //
90 | // func send(data: Data) {
91 | //
92 | // print("[flutter_ble_peripheral] Send data: \(data)")
93 | //
94 | // guard let characteristic = txCharacteristic else {
95 | // return
96 | // }
97 | //
98 | // peripheralManager.updateValue(data, for: characteristic, onSubscribedCentrals: nil)
99 | // }
100 | }
101 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterBlePeripheralPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterBlePeripheralPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterBlePeripheralPlugin.m:
--------------------------------------------------------------------------------
1 | #import "FlutterBlePeripheralPlugin.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_ble_peripheral-Swift.h"
9 | #endif
10 |
11 | @implementation FlutterBlePeripheralPlugin
12 | + (void)registerWithRegistrar:(NSObject*)registrar {
13 | [SwiftFlutterBlePeripheralPlugin registerWithRegistrar:registrar];
14 | }
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/Classes/Models/PeripheralData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PeripheralData.swift
3 | // flutter_ble_peripheral
4 | //
5 | // Created by Julian Steenbakker on 06/12/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | class PeripheralData {
11 | var uuid: String?
12 | var uuids: [String]?
13 | var localName: String? //CBAdvertisementDataLocalNameKey
14 |
15 |
16 | // TODO: add service data
17 | static let serviceUUID: String = "8ebdb2f3-7817-45c9-95c5-c5e9031aaa47"
18 | static let txCharacteristicUUID: String = "08590F7E-DB05-467E-8757-72F6FAEB13D4"
19 | static let rxCharacteristicUUID: String = "08590F7E-DB05-467E-8757-72F6FAEB13D5"
20 |
21 | init(uuid: String?, localName: String?, uuids: [String]?) {
22 | self.uuid = uuid //uuid;
23 | self.localName = localName
24 | self.uuids = uuids
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ios/Classes/Models/PeripheralState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PeripheralStatus.swift
3 | // flutter_ble_peripheral
4 | //
5 | // Created by Julian Steenbakker on 26/11/2021.
6 | //
7 |
8 | import Foundation
9 |
10 | enum PeripheralState : Int{
11 | // case idle, unauthorized, unsupported, advertising, connected
12 | /// Status is not (yet) determined.
13 | case unknown
14 |
15 | /// BLE is not supported on this device.
16 | case unsupported
17 |
18 | /// BLE usage is not authorized for this app.
19 | case unauthorized
20 |
21 | /// BLE is turned off.
22 | case poweredOff
23 |
24 | // /// Android only: Location services are disabled.
25 | // locationServicesDisabled,
26 |
27 | /// BLE is fully operating for this app.
28 | case idle
29 |
30 | /// BLE is advertising data.
31 | case advertising
32 |
33 | /// BLE is connected to a device.
34 | case connected
35 |
36 | // var index: Int { PeripheralState..firstIndex(of: self) ?? 0 }
37 | }
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ios/Classes/SwiftFlutterBlePeripheralPlugin.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import Flutter
8 | import UIKit
9 | import CoreLocation
10 |
11 | public class SwiftFlutterBlePeripheralPlugin: NSObject, FlutterPlugin {
12 |
13 | private let flutterBlePeripheralManager: FlutterBlePeripheralManager
14 |
15 | private let stateChangedHandler: StateChangedHandler
16 | // private let mtuChangedHandler = MtuChangedHandler()
17 | // private let dataReceivedHandler = DataReceivedHandler()
18 | init(stateChangedHandler: StateChangedHandler) {
19 | self.stateChangedHandler = stateChangedHandler
20 | flutterBlePeripheralManager = FlutterBlePeripheralManager(stateChangedHandler: stateChangedHandler)
21 | super.init()
22 | }
23 |
24 | public static func register(with registrar: FlutterPluginRegistrar) {
25 | let instance = SwiftFlutterBlePeripheralPlugin(stateChangedHandler: StateChangedHandler(registrar: registrar))
26 |
27 | // Method channel
28 | let methodChannel = FlutterMethodChannel(name: "dev.steenbakker.flutter_ble_peripheral/ble_state", binaryMessenger: registrar.messenger())
29 | registrar.addMethodCallDelegate(instance, channel: methodChannel)
30 |
31 | // Event channels
32 | // instance.mtuChangedHandler.register(with: registrar, peripheral: instance.flutterBlePeripheralManager)
33 | // instance.dataReceivedHandler.register(with: registrar, peripheral: instance.flutterBlePeripheralManager)
34 | }
35 |
36 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
37 | switch (call.method) {
38 | case "start":
39 | startPeripheral(call, result)
40 | case "stop":
41 | stopPeripheral(result)
42 | case "isAdvertising":
43 | result(stateChangedHandler.state == PeripheralState.advertising)
44 | case "isSupported":
45 | isSupported(result)
46 | case "isConnected":
47 | result(stateChangedHandler.state == PeripheralState.connected)
48 | case "openBluetoothSettings":
49 | openAppSettings()
50 | result(nil)
51 | // case "sendData":
52 | // sendData(call, result)
53 | default:
54 | result(FlutterMethodNotImplemented)
55 | }
56 | }
57 |
58 | private func startPeripheral(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
59 | let map = call.arguments as? Dictionary
60 | let advertiseData = PeripheralData(
61 | uuid: map?["serviceUuid"] as? String ,
62 | localName: map?["localName"] as? String,
63 | uuids: map?["serviceUuids"] as? [String] ,
64 | )
65 | flutterBlePeripheralManager.start(advertiseData: advertiseData)
66 | result(nil)
67 | }
68 |
69 | private func stopPeripheral(_ result: @escaping FlutterResult) {
70 | flutterBlePeripheralManager.peripheralManager.stopAdvertising()
71 | stateChangedHandler.publishPeripheralState(state: PeripheralState.idle)
72 | result(nil)
73 | }
74 |
75 | // We can check if advertising is supported by checking if the ios device supports iBeacons since that uses BLE.
76 | private func isSupported(_ result: @escaping FlutterResult) {
77 | if (CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self)){
78 | result(true)
79 | } else {
80 | result(false)
81 | }
82 | }
83 |
84 | private func openAppSettings() {
85 | if let url = URL(string: UIApplication.openSettingsURLString) {
86 | if UIApplication.shared.canOpenURL(url) {
87 | UIApplication.shared.open(url, options: [:], completionHandler: nil)
88 | }
89 | }
90 | }
91 |
92 | // private func sendData(_ call: FlutterMethodCall,
93 | // _ result: @escaping FlutterResult) {
94 | //
95 | // if let flutterData = call.arguments as? FlutterStandardTypedData {
96 | // flutterBlePeripheralManager.send(data: flutterData.data)
97 | // }
98 | // result(nil)
99 | // }
100 | }
101 |
--------------------------------------------------------------------------------
/ios/Classes/callbacks/PeripheralManagerDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PeripheralManagerDelegate.swift
3 | // flutter_ble_peripheral
4 | //
5 | // Created by Julian Steenbakker on 25/03/2022.
6 | //
7 |
8 | import Foundation
9 | import CoreBluetooth
10 | import CoreLocation
11 |
12 | extension FlutterBlePeripheralManager: CBPeripheralManagerDelegate {
13 |
14 | func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
15 | var state: PeripheralState
16 | switch peripheral.state {
17 | case .poweredOn:
18 | state = .idle
19 | // addService() TODO: add service
20 | case .poweredOff:
21 | state = .poweredOff
22 | case .resetting:
23 | state = .idle
24 | case .unsupported:
25 | state = .unsupported
26 | case .unauthorized:
27 | state = .unauthorized
28 | case .unknown:
29 | state = .unknown
30 | @unknown default:
31 | state = .unknown
32 | }
33 | stateChangedHandler.publishPeripheralState(state: state)
34 | }
35 |
36 | func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: (any Error)?) {
37 | print("[flutter_ble_peripheral] didStartAdvertising:", error ?? "success")
38 |
39 | guard error == nil else {
40 | return
41 | }
42 |
43 | stateChangedHandler.publishPeripheralState(state: .advertising)
44 |
45 | // Immediately set to connected if the tx Characteristic is already subscribed
46 | // if txSubscribed {
47 | // state = .connected
48 | // }
49 | }
50 |
51 | // func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
52 | // print("[flutter_ble_peripheral] didReceiveRead:", request)
53 | //
54 | // // Only answer to requests if not idle
55 | // guard state != .idle else {
56 | // print("[flutter_ble_peripheral] state = .idle -> not answering read request")
57 | // return
58 | // }
59 | //
60 | // // Not supported
61 | // peripheralManager.respond(to: request, withResult: .requestNotSupported)
62 | // }
63 | //
64 | // func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
65 | // print("[flutter_ble_peripheral] didReceiveWrite:", requests)
66 | //
67 | // // Only answer to requests if not idle
68 | // guard state != .idle else {
69 | // print("[flutter_ble_peripheral] state = .idle -> not answering write request")
70 | // return
71 | // }
72 | //
73 | // for request in requests {
74 | //
75 | // print("[flutter_ble_peripheral] write request:", request);
76 | //
77 | // let characteristic = request.characteristic
78 | // guard let data = request.value else {
79 | // print("[flutter_ble_peripheral] request.value is nil");
80 | // return
81 | // }
82 | //
83 | // // Write only supported in rxCharacteristic
84 | // guard characteristic == self.rxCharacteristic else {
85 | // peripheralManager.respond(to: request, withResult: .requestNotSupported)
86 | // print("[flutter_ble_peripheral] respond requestNotSupported (only supported in rxCharacteristic)")
87 | // return
88 | // }
89 | //
90 | // print("[flutter_ble_peripheral] request.value:", request.value!)
91 | // print("[flutter_ble_peripheral] characteristic.value:", characteristic.value!)
92 | //
93 | // if data.count > 0 {
94 | // print("[flutter_ble_peripheral] Receive data: \(data)")
95 | // onDataReceived?(data)
96 | // }
97 | //
98 | // // Respond with success
99 | // peripheralManager.respond(to: request, withResult: .success)
100 | // }
101 | // }
102 | //
103 | // func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
104 | //
105 | // if characteristic == txCharacteristic {
106 | //
107 | // print("[flutter_ble_peripheral] didSubscribeTo:", central, characteristic)
108 | //
109 | // // Update MTU
110 | // self.mtu = central.maximumUpdateValueLength;
111 | //
112 | // // Add to subscriptions
113 | // txSubscriptions.insert(central.identifier)
114 | //
115 | // txSubscribed = !txSubscriptions.isEmpty
116 | //
117 | // print("[flutter_ble_peripheral] txSubscriptions:", txSubscriptions)
118 | // }
119 | // }
120 | //
121 | // func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
122 | //
123 | // if characteristic == txCharacteristic {
124 | //
125 | // print("[flutter_ble_peripheral] didUnsubscribeFrom:", central, characteristic)
126 | //
127 | // // Remove from txSubscriptions
128 | // txSubscriptions.remove(central.identifier)
129 | //
130 | // txSubscribed = !txSubscriptions.isEmpty
131 | //
132 | // print("[flutter_ble_peripheral] txSubscriptions:", txSubscriptions)
133 | // }
134 | // }
135 | }
136 |
--------------------------------------------------------------------------------
/ios/Classes/handlers/DataReceivedHandler.swift:
--------------------------------------------------------------------------------
1 | ////
2 | //// StateChangedHandler.swift
3 | //// flutter_ble_peripheral
4 | ////
5 | //// Created by Julian Steenbakker on 25/03/2022.
6 | ////
7 | //
8 | //import Foundation
9 | //
10 | //public class DataReceivedHandler: NSObject, FlutterStreamHandler {
11 | //
12 | // private var eventSink: FlutterEventSink?
13 | //
14 | // fileprivate func register(with registrar: FlutterPluginRegistrar, peripheral: FlutterBlePeripheralManager) {
15 | //
16 | // let eventChannel = FlutterEventChannel(name: "dev.steenbakker.flutter_ble_peripheral/ble_data_received",
17 | // binaryMessenger: registrar.messenger())
18 | // eventChannel.setStreamHandler(self)
19 | //
20 | // peripheral.onDataReceived = { data in
21 | // if let eventSink = self.eventSink {
22 | // eventSink(FlutterStandardTypedData(bytes: data))
23 | // }
24 | // }
25 | // }
26 | //
27 | // public func onListen(withArguments arguments: Any?,
28 | // eventSink: @escaping FlutterEventSink) -> FlutterError? {
29 | // self.eventSink = eventSink
30 | // return nil
31 | // }
32 | //
33 | // public func onCancel(withArguments arguments: Any?) -> FlutterError? {
34 | // eventSink = nil
35 | // return nil
36 | // }
37 | //}
38 |
--------------------------------------------------------------------------------
/ios/Classes/handlers/MtuChangedHandler.swift:
--------------------------------------------------------------------------------
1 | ////
2 | //// StateChangedHandler.swift
3 | //// flutter_ble_peripheral
4 | ////
5 | //// Created by Julian Steenbakker on 25/03/2022.
6 | ////
7 | //
8 | //import Foundation
9 | //
10 | //public class MtuChangedHandler: NSObject, FlutterStreamHandler {
11 | //
12 | // private var eventSink: FlutterEventSink?
13 | //
14 | // fileprivate func register(with registrar: FlutterPluginRegistrar, peripheral: FlutterBlePeripheralManager) {
15 | //
16 | // let eventChannel = FlutterEventChannel(name: "dev.steenbakker.flutter_ble_peripheral/ble_mtu_changed",
17 | // binaryMessenger: registrar.messenger())
18 | // eventChannel.setStreamHandler(self)
19 | //
20 | // peripheral.onMtuChanged = { mtuSize in
21 | // if let eventSink = self.eventSink {
22 | // eventSink(mtuSize)
23 | // }
24 | // }
25 | // }
26 | //
27 | // public func onListen(withArguments arguments: Any?,
28 | // eventSink: @escaping FlutterEventSink) -> FlutterError? {
29 | // self.eventSink = eventSink
30 | // return nil
31 | // }
32 | //
33 | // public func onCancel(withArguments arguments: Any?) -> FlutterError? {
34 | // eventSink = nil
35 | // return nil
36 | // }
37 | //}
38 |
--------------------------------------------------------------------------------
/ios/Classes/handlers/StateChangedHandler.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StateChangedHandler.swift
3 | // flutter_ble_peripheral
4 | //
5 | // Created by Julian Steenbakker on 25/03/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | public class StateChangedHandler: NSObject, FlutterStreamHandler {
11 |
12 | private var eventSink: FlutterEventSink?
13 |
14 | var state: PeripheralState = PeripheralState.idle
15 |
16 | private let eventChannel: FlutterEventChannel
17 |
18 | init(registrar: FlutterPluginRegistrar) {
19 | eventChannel = FlutterEventChannel(name: "dev.steenbakker.flutter_ble_peripheral/ble_state_changed",
20 | binaryMessenger: registrar.messenger())
21 | super.init()
22 | eventChannel.setStreamHandler(self)
23 | }
24 |
25 | func publishPeripheralState(state: PeripheralState) {
26 | self.state = state
27 | if let eventSink = self.eventSink {
28 | eventSink(state.rawValue)
29 | }
30 | }
31 |
32 | public func onListen(withArguments arguments: Any?,
33 | eventSink: @escaping FlutterEventSink) -> FlutterError? {
34 | self.eventSink = eventSink
35 | if let eventSink = self.eventSink {
36 | eventSink(state.rawValue)
37 | }
38 | return nil
39 | }
40 |
41 | public func onCancel(withArguments arguments: Any?) -> FlutterError? {
42 | eventSink = nil
43 | return nil
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ios/flutter_ble_peripheral.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint flutter_ble_peripheral.podspec' to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'flutter_ble_peripheral'
7 | s.version = '0.0.3'
8 | s.summary = 'This plugin enables a device to be set into peripheral mode, and advertise custom
9 | services and characteristics.'
10 | s.description = <<-DESC
11 | This plugin enables a device to be set into peripheral mode, and advertise custom
12 | services and characteristics.
13 | DESC
14 | s.homepage = 'https://steenbakker.dev'
15 | s.license = { :file => '../LICENSE' }
16 | s.author = { 'Your Company' => 'email@example.com' }
17 | s.source = { :path => '.' }
18 | s.source_files = 'Classes/**/*'
19 | s.dependency 'Flutter'
20 | s.platform = :ios, '8.0'
21 |
22 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
23 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
24 | s.swift_version = '5.0'
25 | end
26 |
--------------------------------------------------------------------------------
/lib/flutter_ble_peripheral.dart:
--------------------------------------------------------------------------------
1 | export 'src/flutter_ble_peripheral.dart';
2 | export 'src/models/advertise_data.dart';
3 | export 'src/models/advertise_set_parameters.dart';
4 | export 'src/models/advertise_settings.dart';
5 | export 'src/models/constants.dart';
6 | export 'src/models/enums/advertise_mode.dart';
7 | export 'src/models/enums/advertise_tx_power.dart';
8 | export 'src/models/enums/bluetooth_peripheral_state.dart';
9 | export 'src/models/peripheral_state.dart';
10 | export 'src/models/permission_state.dart';
11 |
--------------------------------------------------------------------------------
/lib/src/flutter_ble_peripheral.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2022. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import 'dart:async';
8 | import 'dart:io';
9 | // ignore: unnecessary_import
10 | import 'dart:typed_data';
11 |
12 | import 'package:flutter/services.dart';
13 | import 'package:flutter_ble_peripheral/src/models/advertise_data.dart';
14 | import 'package:flutter_ble_peripheral/src/models/advertise_set_parameters.dart';
15 | import 'package:flutter_ble_peripheral/src/models/advertise_settings.dart';
16 | import 'package:flutter_ble_peripheral/src/models/enums/bluetooth_peripheral_state.dart';
17 | import 'package:flutter_ble_peripheral/src/models/periodic_advertise_settings.dart';
18 | import 'package:flutter_ble_peripheral/src/models/peripheral_state.dart';
19 |
20 | class FlutterBlePeripheral {
21 | /// Singleton instance
22 | static final FlutterBlePeripheral _instance =
23 | FlutterBlePeripheral._internal();
24 |
25 | /// Singleton factory
26 | factory FlutterBlePeripheral() {
27 | return _instance;
28 | }
29 |
30 | /// Singleton constructor
31 | FlutterBlePeripheral._internal();
32 |
33 | /// Method Channel used to communicate state with
34 | static const MethodChannel _methodChannel =
35 | MethodChannel('dev.steenbakker.flutter_ble_peripheral/ble_state');
36 |
37 | /// Event Channel for MTU state
38 | final EventChannel _mtuChangedEventChannel = const EventChannel(
39 | 'dev.steenbakker.flutter_ble_peripheral/ble_mtu_changed',
40 | );
41 |
42 | /// Event Channel used to changed state
43 | final EventChannel _stateChangedEventChannel = const EventChannel(
44 | 'dev.steenbakker.flutter_ble_peripheral/ble_state_changed',
45 | );
46 |
47 | Stream? _mtuState;
48 | Stream? _peripheralState;
49 |
50 | //TODO Event Channel used to received data
51 | // final EventChannel _dataReceivedEventChannel = const EventChannel(
52 | // 'dev.steenbakker.flutter_ble_peripheral/ble_data_received');
53 |
54 | /// Start advertising. Takes [AdvertiseData] as an input.
55 | Future start({
56 | required AdvertiseData advertiseData,
57 | AdvertiseSettings? advertiseSettings,
58 | AdvertiseSetParameters? advertiseSetParameters,
59 | AdvertiseData? advertiseResponseData,
60 | AdvertiseData? advertisePeriodicData,
61 | PeriodicAdvertiseSettings? periodicAdvertiseSettings,
62 | }) async {
63 | final parameters = advertiseData.toJson();
64 | parameters["manufacturerDataBytes"] = advertiseData.manufacturerData;
65 | final settings = advertiseSettings ?? AdvertiseSettings();
66 | final jsonSettings = settings.toJson();
67 | for (final key in jsonSettings.keys) {
68 | parameters[key] = jsonSettings[key];
69 | }
70 |
71 | if (advertiseData.serviceUuids != null) {
72 | parameters['serviceUuids'] = advertiseData.serviceUuids;
73 | }
74 |
75 | // ignore: deprecated_member_use_from_same_package
76 | // if (advertiseData.serviceUuid == null &&
77 | // advertiseData.serviceUuids != null) {
78 | // try {
79 | // final firstUuid = advertiseData.serviceUuids!.first;
80 | // parameters['serviceUuid'] = firstUuid;
81 | // } catch (e) {
82 | // // no service uuid present
83 | // }
84 | // }
85 | parameters.addAll(advertiseData.toJson());
86 |
87 | if (advertiseSetParameters != null) {
88 | final json = advertiseSetParameters.toJson();
89 | for (final key in json.keys) {
90 | parameters['set$key'] = json[key];
91 | }
92 | parameters.addAll(advertiseData.toJson());
93 | }
94 |
95 | if (advertiseResponseData != null) {
96 | final json = advertiseData.toJson();
97 | for (final key in json.keys) {
98 | parameters['response$key'] = json[key];
99 | }
100 | parameters.addAll(advertiseData.toJson());
101 | }
102 |
103 | final response =
104 | await _methodChannel.invokeMethod('start', parameters);
105 | return response == null
106 | ? BluetoothPeripheralState.unknown
107 | : BluetoothPeripheralState.values[response];
108 | }
109 |
110 | /// Stop advertising
111 | Future stop() async {
112 | final response = await _methodChannel.invokeMethod('stop');
113 | return response == null
114 | ? BluetoothPeripheralState.unknown
115 | : BluetoothPeripheralState.values[response];
116 | }
117 |
118 | /// Returns `true` if advertising or false if not advertising
119 | Future get isAdvertising async {
120 | return await _methodChannel.invokeMethod('isAdvertising') ?? false;
121 | }
122 |
123 | /// Returns `true` if advertising over BLE is supported
124 | Future get isSupported async =>
125 | await _methodChannel.invokeMethod('isSupported') ?? false;
126 |
127 | /// Returns `true` if device is connected
128 | Future get isConnected async =>
129 | await _methodChannel.invokeMethod('isConnected') ?? false;
130 |
131 | /// Start advertising. Takes [AdvertiseData] as an input.
132 | Future sendData(Uint8List data) async {
133 | await _methodChannel.invokeMethod('sendData', data);
134 | }
135 |
136 | /// Stop advertising
137 | ///
138 | /// [askUser] ONLY AVAILABLE ON ANDROID SDK < 33
139 | /// If set to false, it will enable bluetooth without asking user.
140 | Future enableBluetooth({bool askUser = true}) async {
141 | if (!Platform.isAndroid) return false;
142 | return await _methodChannel.invokeMethod(
143 | 'enableBluetooth',
144 | askUser,
145 | ) ??
146 | false;
147 | }
148 |
149 | Future requestPermission() async {
150 | if (!Platform.isAndroid) return BluetoothPeripheralState.unknown;
151 | final response =
152 | await _methodChannel.invokeMethod('requestPermission');
153 | return response == null
154 | ? BluetoothPeripheralState.unknown
155 | : BluetoothPeripheralState.values[response];
156 | }
157 |
158 | Future hasPermission() async {
159 | if (!Platform.isAndroid) return BluetoothPeripheralState.unknown;
160 | final response = await _methodChannel.invokeMethod('hasPermission');
161 | return response == null
162 | ? BluetoothPeripheralState.unknown
163 | : BluetoothPeripheralState.values[response];
164 | }
165 |
166 | Future openBluetoothSettings() async {
167 | await _methodChannel.invokeMethod('openBluetoothSettings');
168 | }
169 |
170 | Future openAppSettings() async {
171 | await _methodChannel.invokeMethod('openAppSettings');
172 | }
173 |
174 | /// Returns Stream of MTU updates.
175 | Stream get onMtuChanged {
176 | _mtuState ??= _mtuChangedEventChannel
177 | .receiveBroadcastStream()
178 | .cast()
179 | .distinct()
180 | .map((dynamic event) => event as int);
181 | return _mtuState!;
182 | }
183 |
184 | /// Returns Stream of state.
185 | ///
186 | /// After listening to this Stream, you'll be notified about changes in peripheral state.
187 | Stream? get onPeripheralStateChanged {
188 | if (Platform.isWindows) return null;
189 | _peripheralState ??= _stateChangedEventChannel
190 | .receiveBroadcastStream()
191 | .map((dynamic event) => PeripheralState.values[event as int]);
192 | return _peripheralState!;
193 | }
194 |
195 | // /// Returns Stream of data.
196 | // ///
197 | // ///
198 | // Stream getDataReceived() {
199 | // return _dataReceivedEventChannel.receiveBroadcastStream().cast();
200 | // }
201 | }
202 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_data.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import 'dart:typed_data';
8 |
9 | import 'package:flutter_ble_peripheral/src/models/uint8list_converter.dart';
10 | import 'package:json_annotation/json_annotation.dart';
11 |
12 | part 'advertise_data.g.dart';
13 |
14 | /// Model of the data to be advertised.
15 | @JsonSerializable()
16 | class AdvertiseData {
17 | /// Android & iOS
18 | ///
19 | /// Specifies a single service UUIDs to be advertised
20 | // @Deprecated(
21 | // 'Please use serviceUuids, where you can also define a single service uuid.',
22 | // )
23 | final String? serviceUuid;
24 |
25 | /// Android & iOS
26 | ///
27 | /// Specifies multiple service UUIDs to be advertised
28 | /// Multiple serviceUuids is only supported on iOS for now.
29 | /// If specified, [serviceUuid] will not be used.
30 | final List? serviceUuids;
31 |
32 | /// Android only
33 | ///
34 | /// Specifies a manufacturer id
35 | /// Manufacturer ID assigned by Bluetooth SIG.
36 | final int? manufacturerId;
37 |
38 | /// Android only
39 | ///
40 | /// Specifies manufacturer data.
41 | @Uint8ListConverter()
42 | final Uint8List? manufacturerData;
43 |
44 | /// Android only
45 | ///
46 | /// Specifies service data UUID
47 | final String? serviceDataUuid;
48 |
49 | /// Android only
50 | ///
51 | /// Specifies service data
52 | final List? serviceData;
53 |
54 | /// Android only
55 | ///
56 | /// Set to true if device name needs to be included with advertisement
57 | /// Default: false
58 | final bool includeDeviceName;
59 |
60 | /// iOS only
61 | ///
62 | /// Set the deviceName to be broadcasted. Can be 10 bytes.
63 | final String? localName;
64 |
65 | /// Android only
66 | ///
67 | /// set to true if you want to include the power level in the advertisement
68 | /// Default: false
69 | final bool? includePowerLevel;
70 |
71 | /// Android > SDK 31 only
72 | ///
73 | /// A service solicitation UUID to advertise data.
74 | final String? serviceSolicitationUuid;
75 |
76 | AdvertiseData({
77 | // @Deprecated(
78 | // 'Please use serviceUuids, where you can also define a single service uuid.',
79 | // )
80 | this.serviceUuid,
81 | this.serviceUuids,
82 | this.manufacturerId,
83 | this.manufacturerData,
84 | this.serviceDataUuid,
85 | this.serviceData,
86 | this.includeDeviceName = false,
87 | this.localName,
88 | this.includePowerLevel = false,
89 | this.serviceSolicitationUuid,
90 | });
91 |
92 | factory AdvertiseData.fromJson(Map json) =>
93 | _$AdvertiseDataFromJson(json);
94 |
95 | Map toJson() => _$AdvertiseDataToJson(this);
96 | }
97 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_data.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'advertise_data.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | AdvertiseData _$AdvertiseDataFromJson(Map json) =>
10 | AdvertiseData(
11 | serviceUuid: json['serviceUuid'] as String?,
12 | manufacturerId: (json['manufacturerId'] as num?)?.toInt(),
13 | manufacturerData: const Uint8ListConverter()
14 | .fromJson(json['manufacturerData'] as List?),
15 | serviceDataUuid: json['serviceDataUuid'] as String?,
16 | serviceData: (json['serviceData'] as List?)
17 | ?.map((e) => (e as num).toInt())
18 | .toList(),
19 | includeDeviceName: json['includeDeviceName'] as bool? ?? false,
20 | localName: json['localName'] as String?,
21 | includePowerLevel: json['includePowerLevel'] as bool? ?? false,
22 | serviceSolicitationUuid: json['serviceSolicitationUuid'] as String?,
23 | );
24 |
25 | Map _$AdvertiseDataToJson(AdvertiseData instance) =>
26 | {
27 | 'serviceUuid': instance.serviceUuid,
28 | 'manufacturerId': instance.manufacturerId,
29 | 'manufacturerData':
30 | const Uint8ListConverter().toJson(instance.manufacturerData),
31 | 'serviceDataUuid': instance.serviceDataUuid,
32 | 'serviceData': instance.serviceData,
33 | 'includeDeviceName': instance.includeDeviceName,
34 | 'localName': instance.localName,
35 | 'includePowerLevel': instance.includePowerLevel,
36 | 'serviceSolicitationUuid': instance.serviceSolicitationUuid,
37 | };
38 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_set_parameters.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_ble_peripheral/flutter_ble_peripheral.dart';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | part 'advertise_set_parameters.g.dart';
5 |
6 | /// Model of the data to be advertised.
7 | @JsonSerializable()
8 | class AdvertiseSetParameters {
9 | final int? anonymous;
10 |
11 | /// Android only
12 | ///
13 | /// Set whether the advertisement type should be connectable or non-connectable.
14 | /// Default: false
15 | final bool connectable;
16 |
17 | final bool? includeTxPowerLevel;
18 |
19 | final int? interval;
20 |
21 | final bool? legacyMode;
22 |
23 | final int? primaryPhy;
24 |
25 | final bool? scannable;
26 |
27 | final int? secondaryPhy;
28 |
29 | /// Android only
30 | ///
31 | /// Set advertise TX power level to control the transmission power level for the advertising.
32 | /// Default: AdvertisePower.ADVERTISE_TX_POWER_HIGH
33 | final int txPowerLevel;
34 |
35 | final int? duration;
36 |
37 | final int? maxExtendedAdvertisingEvents;
38 |
39 | AdvertiseSetParameters({
40 | this.connectable = false,
41 | this.txPowerLevel = txPowerHigh,
42 | this.interval = intervalHigh,
43 | this.legacyMode = false,
44 | this.primaryPhy,
45 | this.scannable,
46 | this.secondaryPhy,
47 | this.anonymous,
48 | this.includeTxPowerLevel = false,
49 | this.duration,
50 | this.maxExtendedAdvertisingEvents,
51 | });
52 |
53 | factory AdvertiseSetParameters.fromJson(Map json) =>
54 | _$AdvertiseSetParametersFromJson(json);
55 |
56 | Map toJson() => _$AdvertiseSetParametersToJson(this);
57 | }
58 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_set_parameters.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'advertise_set_parameters.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | AdvertiseSetParameters _$AdvertiseSetParametersFromJson(
10 | Map json,
11 | ) =>
12 | AdvertiseSetParameters(
13 | connectable: json['connectable'] as bool? ?? false,
14 | txPowerLevel: (json['txPowerLevel'] as num?)?.toInt() ?? txPowerHigh,
15 | interval: (json['interval'] as num?)?.toInt() ?? intervalHigh,
16 | legacyMode: json['legacyMode'] as bool? ?? false,
17 | primaryPhy: (json['primaryPhy'] as num?)?.toInt(),
18 | scannable: json['scannable'] as bool?,
19 | secondaryPhy: (json['secondaryPhy'] as num?)?.toInt(),
20 | anonymous: (json['anonymous'] as num?)?.toInt(),
21 | includeTxPowerLevel: json['includeTxPowerLevel'] as bool? ?? false,
22 | duration: (json['duration'] as num?)?.toInt(),
23 | maxExtendedAdvertisingEvents:
24 | (json['maxExtendedAdvertisingEvents'] as num?)?.toInt(),
25 | );
26 |
27 | Map _$AdvertiseSetParametersToJson(
28 | AdvertiseSetParameters instance,
29 | ) =>
30 | {
31 | 'anonymous': instance.anonymous,
32 | 'connectable': instance.connectable,
33 | 'includeTxPowerLevel': instance.includeTxPowerLevel,
34 | 'interval': instance.interval,
35 | 'legacyMode': instance.legacyMode,
36 | 'primaryPhy': instance.primaryPhy,
37 | 'scannable': instance.scannable,
38 | 'secondaryPhy': instance.secondaryPhy,
39 | 'txPowerLevel': instance.txPowerLevel,
40 | 'duration': instance.duration,
41 | 'maxExtendedAdvertisingEvents': instance.maxExtendedAdvertisingEvents,
42 | };
43 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_ble_peripheral/src/models/enums/advertise_mode.dart';
2 | import 'package:flutter_ble_peripheral/src/models/enums/advertise_tx_power.dart';
3 | import 'package:json_annotation/json_annotation.dart';
4 |
5 | part 'advertise_settings.g.dart';
6 |
7 | /// Model of the data to be advertised.
8 | @JsonSerializable()
9 | class AdvertiseSettings {
10 | /// Android only
11 | ///
12 | /// Set the advertise mode to use when using android >= o
13 | final bool advertiseSet;
14 |
15 | /// Android only
16 | ///
17 | /// Set advertise mode to control the advertising power and latency.
18 | /// Default: AdvertiseMode.ADVERTISE_MODE_LOW_LATENCY
19 | final AdvertiseMode advertiseMode;
20 |
21 | /// Android only
22 | ///
23 | /// Set whether the advertisement type should be connectable or non-connectable.
24 | /// Default: false
25 | final bool connectable;
26 |
27 | /// Android only
28 | ///
29 | /// Limit advertising to a given amount of time.
30 | /// May not exceed 180000 milliseconds.
31 | /// Default: 400 milliseconds
32 | final int timeout;
33 |
34 | /// Android only
35 | ///
36 | /// Set advertise TX power level to control the transmission power level for the advertising.
37 | /// Default: AdvertisePower.ADVERTISE_TX_POWER_HIGH
38 | final AdvertiseTxPower txPowerLevel;
39 |
40 | AdvertiseSettings({
41 | this.advertiseSet = true,
42 | this.connectable = false,
43 | this.timeout = 400,
44 | this.advertiseMode = AdvertiseMode.advertiseModeLowLatency,
45 | this.txPowerLevel = AdvertiseTxPower.advertiseTxPowerLow,
46 | });
47 |
48 | factory AdvertiseSettings.fromJson(Map json) =>
49 | _$AdvertiseSettingsFromJson(json);
50 |
51 | Map toJson() => _$AdvertiseSettingsToJson(this);
52 | }
53 |
--------------------------------------------------------------------------------
/lib/src/models/advertise_settings.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'advertise_settings.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | AdvertiseSettings _$AdvertiseSettingsFromJson(Map json) =>
10 | AdvertiseSettings(
11 | advertiseSet: json['advertiseSet'] as bool? ?? true,
12 | connectable: json['connectable'] as bool? ?? false,
13 | timeout: (json['timeout'] as num?)?.toInt() ?? 400,
14 | advertiseMode:
15 | $enumDecodeNullable(_$AdvertiseModeEnumMap, json['advertiseMode']) ??
16 | AdvertiseMode.advertiseModeLowLatency,
17 | txPowerLevel: $enumDecodeNullable(
18 | _$AdvertiseTxPowerEnumMap,
19 | json['txPowerLevel'],
20 | ) ??
21 | AdvertiseTxPower.advertiseTxPowerLow,
22 | );
23 |
24 | Map _$AdvertiseSettingsToJson(AdvertiseSettings instance) =>
25 | {
26 | 'advertiseSet': instance.advertiseSet,
27 | 'advertiseMode': _$AdvertiseModeEnumMap[instance.advertiseMode],
28 | 'connectable': instance.connectable,
29 | 'timeout': instance.timeout,
30 | 'txPowerLevel': _$AdvertiseTxPowerEnumMap[instance.txPowerLevel],
31 | };
32 |
33 | const _$AdvertiseModeEnumMap = {
34 | AdvertiseMode.advertiseModeLowPower: 0,
35 | AdvertiseMode.advertiseModeBalanced: 1,
36 | AdvertiseMode.advertiseModeLowLatency: 2,
37 | };
38 |
39 | const _$AdvertiseTxPowerEnumMap = {
40 | AdvertiseTxPower.advertiseTxPowerUltraLow: 0,
41 | AdvertiseTxPower.advertiseTxPowerLow: 1,
42 | AdvertiseTxPower.advertiseTxPowerMedium: 2,
43 | AdvertiseTxPower.advertiseTxPowerHigh: 3,
44 | };
45 |
--------------------------------------------------------------------------------
/lib/src/models/constants.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2022. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 | // TODO: Docs
7 | const int intervalLow = 160;
8 | const int intervalHigh = 1600;
9 | const int intervalMax = 16777215;
10 | const int intervalMedium = 400;
11 | const int intervalMin = 160;
12 |
13 | const int txPowerHigh = 1;
14 | const int txPowerLow = -15;
15 | const int txPowerMax = 1;
16 | const int txPowerMedium = -7;
17 | const int txPowerMin = -127;
18 | const int txPowerUltraLow = -21;
19 |
--------------------------------------------------------------------------------
/lib/src/models/enums/advertise_mode.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2022. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import 'package:json_annotation/json_annotation.dart';
8 |
9 | enum AdvertiseMode {
10 | /// Perform Bluetooth LE advertising in low power mode. This is the default and preferred
11 | /// advertising mode as it consumes the least power.
12 | @JsonValue(0)
13 | advertiseModeLowPower,
14 |
15 | /// Perform Bluetooth LE advertising in balanced power mode. This is balanced between advertising
16 | /// frequency and power consumption.
17 | @JsonValue(1)
18 | advertiseModeBalanced,
19 |
20 | /// Perform Bluetooth LE advertising in low latency, high power mode. This has the highest power
21 | /// consumption and should not be used for continuous background advertising.
22 | @JsonValue(2)
23 | advertiseModeLowLatency
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/models/enums/advertise_tx_power.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2022. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import 'package:json_annotation/json_annotation.dart';
8 |
9 | enum AdvertiseTxPower {
10 | /// Advertise using the lowest transmission (TX) power level. Low transmission power can be used
11 | /// to restrict the visibility range of advertising packets.
12 | @JsonValue(0)
13 | advertiseTxPowerUltraLow,
14 |
15 | /// Advertise using low TX power level.
16 | @JsonValue(1)
17 | advertiseTxPowerLow,
18 |
19 | /// Advertise using medium TX power level.
20 | @JsonValue(2)
21 | advertiseTxPowerMedium,
22 |
23 | /// Advertise using high TX power level. This corresponds to largest visibility range of the
24 | /// advertising packet.
25 | @JsonValue(3)
26 | advertiseTxPowerHigh
27 | }
28 |
--------------------------------------------------------------------------------
/lib/src/models/enums/bluetooth_peripheral_state.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2023. Julian Steenbakker.
3 | * All rights reserved. Use of this source code is governed by a
4 | * BSD-style license that can be found in the LICENSE file.
5 | */
6 |
7 | import 'package:json_annotation/json_annotation.dart';
8 |
9 | enum BluetoothPeripheralState {
10 | /// The user granted access to the requested feature.
11 | @JsonValue(0)
12 | granted,
13 |
14 | /// The user denied access to the requested feature, permission needs to be asked first.
15 | @JsonValue(1)
16 | denied,
17 |
18 | /// Permission to the requested feature is permanently denied,
19 | /// the permission dialog will not be shown when requesting this permission.
20 | /// The user may still change the permission status in the settings.
21 | @JsonValue(2)
22 | permanentlyDenied,
23 |
24 | /// The OS denied access to the requested feature.
25 | /// The user cannot change this app's status, possibly due to active restrictions such as parental controls being in place.
26 | ///
27 | /// Only supported on iOS.
28 | ///
29 | @JsonValue(3)
30 | restricted,
31 |
32 | /// User has authorized this application for limited access.
33 | /// Only supported on iOS (iOS14+).
34 | @JsonValue(4)
35 | limited,
36 |
37 | /// Bluetooth is turned off
38 | @JsonValue(5)
39 | turnedOff,
40 | @JsonValue(6)
41 | unsupported,
42 |
43 | /// The status is unknown
44 | @JsonValue(7)
45 | unknown,
46 | @JsonValue(8)
47 | ready,
48 | }
49 |
--------------------------------------------------------------------------------
/lib/src/models/map_uint8list_converter.dart:
--------------------------------------------------------------------------------
1 | import 'dart:typed_data';
2 | import 'package:json_annotation/json_annotation.dart';
3 |
4 | class Uint8ListMapIntConverter
5 | implements JsonConverter