refactor: 重构项目 (#3)

This commit is contained in:
iAMD
2022-09-23 10:28:33 +08:00
committed by GitHub
parent 81a6f4528b
commit 9ec2449694
160 changed files with 16034 additions and 26183 deletions

1
example/.gitignore vendored
View File

@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml

View File

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
channel: stable
project_type: app

View File

@ -8,9 +8,9 @@ This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -9,3 +9,5 @@ GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -1,8 +1,7 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader)
}
}
@ -26,16 +25,29 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "dev.yanshouwang.bluetooth_low_energy_example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.yanshouwang.bluetooth_low_energy_example">
<!-- Flutter needs it to communicate with the running application
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -2,9 +2,11 @@
package="dev.yanshouwang.bluetooth_low_energy_example">
<application
android:label="bluetooth_low_energy_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
@ -18,15 +20,6 @@
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

View File

@ -3,14 +3,14 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>

View File

@ -3,14 +3,14 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.yanshouwang.bluetooth_low_energy_example">
<!-- Flutter needs it to communicate with the running application
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,13 +1,12 @@
buildscript {
ext.kotlin_version = '1.5.21'
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -16,13 +15,14 @@ allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}

View File

@ -3,5 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
#distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +1,4 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside

View File

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

View File

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

View File

@ -1,9 +1,9 @@
PODS:
- bluetooth_low_energy (0.0.1):
- Flutter
- SwiftProtobuf (~> 1.0)
- SwiftProtobuf (~> 1.20)
- Flutter (1.0.0)
- SwiftProtobuf (1.17.0)
- SwiftProtobuf (1.20.1)
DEPENDENCIES:
- bluetooth_low_energy (from `.symlinks/plugins/bluetooth_low_energy/ios`)
@ -20,10 +20,10 @@ EXTERNAL SOURCES:
:path: Flutter
SPEC CHECKSUMS:
bluetooth_low_energy: 9222b99f977897165e6ffaece386bd289ac8c1fb
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
SwiftProtobuf: 9c85136c6ba74b0a1b84279dbf0f6db8efb714e0
bluetooth_low_energy: af34d921ca3a9e085cf6364c500050965acf25f9
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
SwiftProtobuf: e40a7684079620e84ba522dbaeab0cddb0ec7ffd
PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.10.1
COCOAPODS: 1.11.3

View File

@ -13,7 +13,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
EA280E36114EEC52A66510AF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F6363DB0F6B8F760A0B70E6 /* Pods_Runner.framework */; };
BC25F992B6ECFCC18D953308 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3248507C6DD52333A770C1CB /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -30,11 +30,11 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1173070BE6CFE927D74C0736 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2F6363DB0F6B8F760A0B70E6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3248507C6DD52333A770C1CB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
5CB2CF9D872B54AC77EFD5AD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -45,8 +45,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9E8C4C982EC9200D28D9557C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
FA6DAFE6FDB9F8D1C4A8AEF3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
AEA309A3A59074B58910681F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
C3AC24B274EC26E645389946 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -54,13 +54,21 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
EA280E36114EEC52A66510AF /* Pods_Runner.framework in Frameworks */,
BC25F992B6ECFCC18D953308 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2B77126ACFA0B323BF67EC4C /* Frameworks */ = {
isa = PBXGroup;
children = (
3248507C6DD52333A770C1CB /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@ -78,8 +86,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
A19CF493038BEE74587FB0E8 /* Pods */,
990CDB7BA621E0B7AFA0C6BE /* Frameworks */,
F7511A57B6D81C59C9E0353E /* Pods */,
2B77126ACFA0B323BF67EC4C /* Frameworks */,
);
sourceTree = "<group>";
};
@ -106,20 +114,12 @@
path = Runner;
sourceTree = "<group>";
};
990CDB7BA621E0B7AFA0C6BE /* Frameworks */ = {
F7511A57B6D81C59C9E0353E /* Pods */ = {
isa = PBXGroup;
children = (
2F6363DB0F6B8F760A0B70E6 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
A19CF493038BEE74587FB0E8 /* Pods */ = {
isa = PBXGroup;
children = (
FA6DAFE6FDB9F8D1C4A8AEF3 /* Pods-Runner.debug.xcconfig */,
1173070BE6CFE927D74C0736 /* Pods-Runner.release.xcconfig */,
9E8C4C982EC9200D28D9557C /* Pods-Runner.profile.xcconfig */,
AEA309A3A59074B58910681F /* Pods-Runner.debug.xcconfig */,
C3AC24B274EC26E645389946 /* Pods-Runner.release.xcconfig */,
5CB2CF9D872B54AC77EFD5AD /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -131,14 +131,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
4DEF012D22BCBC3AF731DE9C /* [CP] Check Pods Manifest.lock */,
224727E4C00C24BC3CD4292A /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
EDB3731BEABF1E16CE6A52EB /* [CP] Embed Pods Frameworks */,
DCBB4C37F33FFC666159F299 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -155,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -197,21 +197,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
4DEF012D22BCBC3AF731DE9C /* [CP] Check Pods Manifest.lock */ = {
224727E4C00C24BC3CD4292A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -233,6 +219,20 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -247,7 +247,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
EDB3731BEABF1E16CE6A52EB /* [CP] Embed Pods Frameworks */ = {
DCBB4C37F33FFC666159F299 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -339,7 +339,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -417,7 +417,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -466,7 +466,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"

View File

@ -2,8 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Bluetooth Low Energy</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -23,7 +27,7 @@
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We need to use your bluetooth to communicate with other BLE devices.</string>
<string>Let's try to communicate use the bluetooth low energy!</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>

View File

@ -7,84 +7,91 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
final app = MaterialApp(
theme: ThemeData(
fontFamily: 'IBM Plex Mono',
),
home: HomeView(),
routes: {
'gatt': (context) => GattView(),
},
);
const app = MyApp();
runApp(app);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const HomeView(),
routes: {
'device': (context) {
final uuid = ModalRoute.of(context)?.settings.arguments as UUID;
return DeviceView(
uuid: uuid,
);
},
},
);
}
}
class HomeView extends StatefulWidget {
const HomeView({Key? key}) : super(key: key);
@override
_HomeViewState createState() => _HomeViewState();
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> with WidgetsBindingObserver {
final ValueNotifier<bool> state;
final ValueNotifier<bool> discovering;
final ValueNotifier<Map<UUID, Discovery>> discoveries;
late StreamSubscription<BluetoothState> stateSubscription;
late StreamSubscription<Discovery> discoverySubscription;
_HomeViewState()
: state = ValueNotifier(false),
discovering = ValueNotifier(false),
discoveries = ValueNotifier({});
class _HomeViewState extends State<HomeView> {
late ValueNotifier<bool> state;
late ValueNotifier<bool> discovering;
late ValueNotifier<List<Advertisement>> advertisements;
late Stream<Advertisement> advertisementStream;
late StreamSubscription<BluetoothState> stateStreamSubscription;
late StreamSubscription<Advertisement> advertisementStreamSubscription;
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
state = ValueNotifier(false);
discovering = ValueNotifier(false);
advertisements = ValueNotifier([]);
advertisementStream = CentralManager.instance.getAdvertisementStream();
state.addListener(onStateChanged);
stateStreamSubscription = CentralManager.instance.stateStream.listen(
(state) => this.state.value = state == BluetoothState.poweredOn,
);
setup();
}
void setup() async {
final state = await central.state;
final authorized = await CentralManager.instance.authorize();
if (!authorized) {
throw UnimplementedError();
}
final state = await CentralManager.instance.getState();
this.state.value = state == BluetoothState.poweredOn;
stateSubscription = central.stateChanged.listen(
(state) {
this.state.value = state == BluetoothState.poweredOn;
final invisible = !ModalRoute.of(context)!.isCurrent;
if (invisible) return;
if (this.state.value) {
startDiscovery();
} else {
discovering.value = false;
}
},
);
discoverySubscription = central.discovered.listen(
(discovery) {
discoveries.value[discovery.uuid] = discovery;
discoveries.value = {...discoveries.value};
},
);
if (this.state.value) {
startDiscovery();
}
void onStateChanged() {
final route = ModalRoute.of(context);
if (route == null || !route.isCurrent) {
return;
}
if (state.value) {
startScan();
} else {
discovering.value = false;
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
}
@override
void dispose() {
stopDiscovery();
stateSubscription.cancel();
discoverySubscription.cancel();
discoveries.dispose();
stopScan();
state.removeListener(onStateChanged);
stateStreamSubscription.cancel();
state.dispose();
discovering.dispose();
WidgetsBinding.instance!.removeObserver(this);
print('dispose');
advertisements.dispose();
super.dispose();
}
@ -92,89 +99,108 @@ class _HomeViewState extends State<HomeView> with WidgetsBindingObserver {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
title: const Text('Home'),
),
body: bodyView,
body: buildBody(context),
);
}
void startDiscovery() async {
if (discovering.value || !state.value) return;
await central.startDiscovery();
void startScan() async {
if (discovering.value || !state.value) {
return;
}
advertisementStreamSubscription = advertisementStream.listen(
(advertisement) {
final index = advertisements.value.indexWhere(
(element) => element.uuid == advertisement.uuid,
);
if (index >= 0) {
return;
}
advertisements.value = [...advertisements.value, advertisement];
},
);
discovering.value = true;
}
void stopDiscovery() async {
if (!discovering.value || !state.value) return;
await central.stopDiscovery();
discoveries.value = {};
void stopScan() async {
if (!discovering.value || !state.value) {
return;
}
await advertisementStreamSubscription.cancel();
advertisements.value = [];
discovering.value = false;
}
void showAdvertisements(Discovery discovery) {
void showAdvertisement(Advertisement advertisement) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
elevation: 0.0,
builder: (context) => buildAdvertisementsView(discovery),
builder: (context) => buildAdvertisementView(advertisement),
);
}
void showGattView(Discovery discovery) async {
stopDiscovery();
void showDeviceView(UUID uuid) async {
stopScan();
await Navigator.of(context).pushNamed(
'gatt',
arguments: discovery.uuid,
'device',
arguments: uuid,
);
startDiscovery();
startScan();
}
}
extension on _HomeViewState {
Widget get bodyView {
Widget buildBody(BuildContext context) {
return ValueListenableBuilder(
valueListenable: state,
builder: (context, bool state, child) =>
state ? discoveriesView : closedView,
builder: (context, bool state, child) {
return state
? buildAdvertisementsView(context)
: buildClosedView(context);
},
);
}
Widget get closedView {
return Center(
Widget buildClosedView(BuildContext context) {
return const Center(
child: Text('蓝牙未开启'),
);
}
Widget get discoveriesView {
Widget buildAdvertisementsView(BuildContext context) {
return RefreshIndicator(
onRefresh: () async => discoveries.value = {},
onRefresh: () async => advertisements.value = [],
child: ValueListenableBuilder(
valueListenable: discoveries,
builder: (context, Map<UUID, Discovery> discoveries, child) {
valueListenable: advertisements,
builder: (context, List<Advertisement> advertisements, child) {
return ListView.builder(
padding: EdgeInsets.all(6.0),
itemCount: discoveries.length,
padding: const EdgeInsets.all(6.0),
itemCount: advertisements.length,
itemBuilder: (context, i) {
final discovery = discoveries.values.elementAt(i);
final advertisement = advertisements.elementAt(i);
final connectable = advertisement.connectable ?? true;
return Card(
color: discovery.connectable ? Colors.amber : Colors.grey,
color: connectable ? Colors.amber : Colors.grey,
clipBehavior: Clip.antiAlias,
shape: BeveledRectangleBorder(
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(12.0),
bottomLeft: Radius.circular(12.0)),
topRight: Radius.circular(12.0),
bottomLeft: Radius.circular(12.0),
),
),
margin: EdgeInsets.all(6.0),
key: Key(discovery.uuid.name),
margin: const EdgeInsets.all(6.0),
key: Key('${advertisement.uuid}'),
child: InkWell(
splashColor: Colors.purple,
onTap: discovery.connectable
? () => showGattView(discovery)
onTap: connectable
? () => showDeviceView(advertisement.uuid)
: null,
onLongPress: () => showAdvertisements(discovery),
onLongPress: () => showAdvertisement(advertisement),
child: Container(
height: 100.0,
padding: EdgeInsets.all(12.0),
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
@ -183,9 +209,9 @@ extension on _HomeViewState {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(discovery.name ?? 'NaN'),
Text(advertisement.localName ?? 'UNKNOWN'),
Text(
discovery.uuid.name,
advertisement.uuid.toString(),
softWrap: true,
),
],
@ -194,7 +220,7 @@ extension on _HomeViewState {
Expanded(
flex: 1,
child: Text(
discovery.rssi.toString(),
advertisement.rssi.toString(),
textAlign: TextAlign.center,
),
),
@ -210,10 +236,10 @@ extension on _HomeViewState {
);
}
Widget buildAdvertisementsView(Discovery discovery) {
Widget buildAdvertisementView(Advertisement advertisement) {
final widgets = <Widget>[
Row(
children: [
children: const [
Text('Type'),
Expanded(
child: Center(
@ -222,34 +248,34 @@ extension on _HomeViewState {
),
],
),
Divider(),
const Divider(),
];
for (final entry in discovery.advertisements.entries) {
final key = entry.key.toRadixString(16).padLeft(2, '0');
final value = hex.encode(entry.value);
final widget = Row(
children: [
Text('0x$key'),
Container(width: 12.0),
Expanded(
child: Text(
'$value',
softWrap: true,
),
),
],
);
widgets.add(widget);
if (entry.key != discovery.advertisements.entries.last.key) {
final divider = Divider();
widgets.add(divider);
}
}
// for (final entry in advertisement.data.entries) {
// final type = '0x${entry.key.toRadixString(16).padLeft(2, '0')}';
// final value = hex.encode(entry.value);
// final widget = Row(
// children: [
// Text(type),
// Container(width: 12.0),
// Expanded(
// child: Text(
// value,
// softWrap: true,
// ),
// ),
// ],
// );
// widgets.add(widget);
// if (entry.key != advertisement.data.entries.last.key) {
// const divider = Divider();
// widgets.add(divider);
// }
// }
return Container(
margin: const EdgeInsets.all(12.0),
child: Material(
elevation: 1.0,
shape: BeveledRectangleBorder(
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12.0),
bottomRight: Radius.circular(12.0),
@ -268,49 +294,51 @@ extension on _HomeViewState {
}
}
class GattView extends StatefulWidget {
const GattView({Key? key}) : super(key: key);
class DeviceView extends StatefulWidget {
final UUID uuid;
const DeviceView({
Key? key,
required this.uuid,
}) : super(key: key);
@override
_GattViewState createState() => _GattViewState();
State<DeviceView> createState() => _DeviceViewState();
}
class _GattViewState extends State<GattView> {
final ValueNotifier<ConnectionState> state;
GATT? gatt;
StreamSubscription? connectionLostSubscription;
final ValueNotifier<GattService?> service;
final ValueNotifier<GattCharacteristic?> characteristic;
final TextEditingController writeController;
final ValueNotifier<Map<GattCharacteristic, StreamSubscription>> notifies;
final ValueNotifier<List<String>> logs;
late UUID uuid;
_GattViewState()
: state = ValueNotifier(ConnectionState.disconnected),
service = ValueNotifier(null),
characteristic = ValueNotifier(null),
writeController = TextEditingController(),
notifies = ValueNotifier({}),
logs = ValueNotifier([]);
class _DeviceViewState extends State<DeviceView> {
late Peripheral peripheral;
late Map<GattService, List<GattCharacteristic>> services;
late ValueNotifier<ConnectionState> state;
late ValueNotifier<GattService?> service;
late ValueNotifier<GattCharacteristic?> characteristic;
late TextEditingController writeController;
late ValueNotifier<Map<GattCharacteristic, StreamSubscription>> notifies;
late ValueNotifier<List<String>> logs;
@override
void initState() {
super.initState();
services = {};
state = ValueNotifier(ConnectionState.disconnected);
service = ValueNotifier(null);
characteristic = ValueNotifier(null);
writeController = TextEditingController();
notifies = ValueNotifier({});
logs = ValueNotifier([]);
}
@override
Widget build(BuildContext context) {
uuid = ModalRoute.of(context)!.settings.arguments as UUID;
return Scaffold(
appBar: AppBar(
title: Text(uuid.name),
title: Text('${widget.uuid}'),
actions: [
changeStateView,
buildConnectionState(context),
],
),
body: bodyView,
body: buildBody(context),
);
}
@ -356,16 +384,12 @@ class _GattViewState extends State<GattView> {
void connect() async {
try {
state.value = ConnectionState.connecting;
gatt = await central.connect(uuid);
state.value = ConnectionState.connected;
connectionLostSubscription = gatt!.connectionLost.listen(
(errorCode) async {
peripheral = await CentralManager.instance.connect(
widget.uuid,
onConnectionLost: (error) {
for (var subscription in notifies.value.values) {
await subscription.cancel();
subscription.cancel();
}
await connectionLostSubscription!.cancel();
gatt = null;
connectionLostSubscription = null;
service.value = null;
characteristic.value = null;
notifies.value.clear();
@ -373,7 +397,24 @@ class _GattViewState extends State<GattView> {
state.value = ConnectionState.disconnected;
},
);
} on PlatformException {
try {
final items0 = <GattService, List<GattCharacteristic>>{};
final services = await peripheral.discoverServices();
for (var service in services) {
final items1 = <GattCharacteristic>[];
final characteristics = await service.discoverCharacteristics();
for (var characteristic in characteristics) {
items1.add(characteristic);
}
items0[service] = items1;
}
this.services = items0;
} catch (e) {
peripheral.disconnect();
rethrow;
}
state.value = ConnectionState.connected;
} catch (error) {
state.value = ConnectionState.disconnected;
}
}
@ -381,32 +422,30 @@ class _GattViewState extends State<GattView> {
void disconnect() async {
try {
state.value = ConnectionState.disconnecting;
await gatt!.disconnect();
for (var subscription in notifies.value.values) {
await subscription.cancel();
subscription.cancel();
}
await connectionLostSubscription!.cancel();
gatt = null;
connectionLostSubscription = null;
await peripheral.disconnect();
services = {};
service.value = null;
characteristic.value = null;
notifies.value.clear();
logs.value.clear();
state.value = ConnectionState.disconnected;
} on PlatformException {
} catch (e) {
state.value = ConnectionState.connected;
}
}
}
extension on _GattViewState {
Widget get changeStateView {
extension on _DeviceViewState {
Widget buildConnectionState(BuildContext context) {
return ValueListenableBuilder(
valueListenable: state,
builder: (context, ConnectionState stateValue, child) {
builder: (context, ConnectionState state, child) {
void Function()? onPressed;
String data;
switch (stateValue) {
switch (state) {
case ConnectionState.disconnected:
onPressed = connect;
data = '连接';
@ -428,7 +467,7 @@ extension on _GattViewState {
return TextButton(
onPressed: onPressed,
style: TextButton.styleFrom(
primary: Colors.white,
foregroundColor: Colors.white,
),
child: Text(data),
);
@ -436,19 +475,19 @@ extension on _GattViewState {
);
}
Widget get bodyView {
Widget buildBody(BuildContext context) {
return ValueListenableBuilder(
valueListenable: state,
builder: (context, ConnectionState stateValue, child) {
switch (stateValue) {
case ConnectionState.disconnected:
return disconnectedView;
return buildDisconnectedView(context);
case ConnectionState.connecting:
return connectingView;
return buildConnectingView(context);
case ConnectionState.connected:
return connectedView;
return buildConnectedView(context);
case ConnectionState.disconnecting:
return disconnectingView;
return buildDisconnectingView(context);
default:
throw UnimplementedError();
}
@ -456,75 +495,74 @@ extension on _GattViewState {
);
}
Widget get disconnectedView {
return Center(
child: Text('未连接'),
Widget buildDisconnectedView(BuildContext context) {
return const Center(
child: Text('Disconnected'),
);
}
Widget get connectingView {
return Center(
child: Text('正在建立连接'),
Widget buildConnectingView(BuildContext context) {
return const Center(
child: Text('Connecting'),
);
}
Widget get connectedView {
Widget buildConnectedView(BuildContext context) {
return ValueListenableBuilder(
valueListenable: service,
builder: (context, GattService? serviceValue, child) {
final services = gatt!.services.values
.map((service) => DropdownMenuItem<GattService>(
value: service,
child: Text(
service.uuid.name,
softWrap: false,
),
))
.toList();
builder: (context, GattService? service, child) {
final services = this.services.keys.map((service) {
return DropdownMenuItem<GattService>(
value: service,
child: Text(
service.uuid.toString(),
softWrap: false,
),
);
}).toList();
final serviceView = DropdownButton<GattService>(
isExpanded: true,
hint: Text('选择服务'),
value: serviceValue,
hint: const Text('Choose a service'),
value: service,
items: services,
onChanged: (value) {
service.value = value;
onChanged: (service) {
this.service.value = service;
characteristic.value = null;
},
);
final views = <Widget>[serviceView];
if (serviceValue != null) {
final characteristics = serviceValue.characteristics.values
.map((characteristic) => DropdownMenuItem(
value: characteristic,
child: Text(
characteristic.uuid.name,
softWrap: false,
),
))
.toList();
if (service != null) {
final characteristics = this.services[service]?.map((characteristic) {
return DropdownMenuItem(
value: characteristic,
child: Text(
characteristic.uuid.toString(),
softWrap: false,
),
);
}).toList();
final characteristicView = ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, GattCharacteristic? characteristicValue, child) {
final canWrite = characteristicValue != null &&
(characteristicValue.canWrite ||
characteristicValue.canWriteWithoutResponse);
final canRead =
characteristicValue != null && characteristicValue.canRead;
builder: (context, GattCharacteristic? characteristic, child) {
final canWrite = characteristic != null &&
(characteristic.canWrite ||
characteristic.canWriteWithoutResponse);
final canRead = characteristic != null && characteristic.canRead;
final canNotify =
characteristicValue != null && characteristicValue.canNotify;
characteristic != null && characteristic.canNotify;
final readAndNotifyView = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: canRead
? () async {
final value = await characteristicValue!.read();
final value = await characteristic.read();
final time = DateTime.now().display;
final log = '[$time][READ] ${hex.encode(value)}';
logs.value = [...logs.value, log];
}
: null,
icon: Icon(Icons.archive),
icon: const Icon(Icons.archive),
),
ValueListenableBuilder(
valueListenable: notifies,
@ -533,19 +571,17 @@ extension on _GattViewState {
notifiesValue,
child) {
final notifying =
notifiesValue.containsKey(characteristicValue);
notifiesValue.containsKey(characteristic);
return IconButton(
onPressed: canNotify
? () async {
if (notifying) {
await characteristicValue!.notify(false);
await notifiesValue
.remove(characteristicValue)!
.remove(characteristic)!
.cancel();
} else {
await characteristicValue!.notify(true);
notifiesValue[characteristicValue] =
characteristicValue.valueChanged
notifiesValue[characteristic] =
characteristic.valueStream
.listen((value) {
final time = DateTime.now().display;
final log =
@ -567,18 +603,19 @@ extension on _GattViewState {
final controllerView = TextField(
controller: writeController,
decoration: InputDecoration(
hintText: 'MTU: ${gatt!.maximumWriteLength}',
hintText: 'MTU: ${peripheral.maximumWriteLength}',
suffixIcon: IconButton(
onPressed: canWrite
? () {
final value = utf8.encode(writeController.text);
final withoutResponse =
!characteristicValue!.canWrite;
characteristicValue.write(value,
withoutResponse: withoutResponse);
final elements = utf8.encode(writeController.text);
final value = Uint8List.fromList(elements);
characteristic.write(
value,
withoutResponse: !characteristic.canWrite,
);
}
: null,
icon: Icon(Icons.send),
icon: const Icon(Icons.send),
),
),
);
@ -586,10 +623,12 @@ extension on _GattViewState {
children: [
DropdownButton<GattCharacteristic>(
isExpanded: true,
hint: Text('选择特征值'),
value: characteristicValue,
hint: const Text('Choose a characteristic'),
value: characteristic,
items: characteristics,
onChanged: (value) => characteristic.value = value,
onChanged: (characteristic) {
this.characteristic.value = characteristic;
},
),
readAndNotifyView,
controllerView,
@ -617,7 +656,7 @@ extension on _GattViewState {
);
views.add(loggerView);
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.0),
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: views,
@ -627,9 +666,9 @@ extension on _GattViewState {
);
}
Widget get disconnectingView {
return Center(
child: Text('正在断开连接'),
Widget buildDisconnectingView(BuildContext context) {
return const Center(
child: Text('Disconnecting'),
);
}
}

View File

@ -1,20 +1,41 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "47.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "4.7.0"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
version: "2.9.0"
bluetooth_low_energy:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.1.0"
version: "1.0.0"
boolean_selector:
dependency: transitive
description:
@ -28,108 +49,164 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
convert:
dependency: "direct main"
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.0.2"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.5"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
version: "1.0.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_simple_treeview:
dependency: "direct main"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_simple_treeview
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0-nullsafety.1"
version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.8.0"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
pedantic:
dependency: "direct dev"
version: "1.8.2"
pigeon:
dependency: transitive
description:
name: pedantic
name: pigeon
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
version: "4.0.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sky_engine:
dependency: transitive
description: flutter
@ -141,7 +218,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.9.0"
stack_trace:
dependency: transitive
description:
@ -162,35 +239,56 @@ packages:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.12"
tuple:
dependency: transitive
description:
name: tuple
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.2"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.20.0"
dart: ">=2.17.6 <3.0.0"
flutter: ">=3.0.0"

View File

@ -2,15 +2,22 @@ name: bluetooth_low_energy_example
description: Demonstrates how to use the bluetooth_low_energy plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ">=2.12.0 <3.0.0"
sdk: ">=2.17.6 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
bluetooth_low_energy:
# When depending on this package from a real application you should use:
# bluetooth_low_energy: ^x.y.z
@ -18,21 +25,27 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
convert: ^3.0.0
flutter_simple_treeview: ^3.0.0-nullsafety.1
convert: ^3.0.2
dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.11.0
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
@ -46,7 +59,7 @@ flutter:
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
@ -70,12 +83,3 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
fonts:
- family: IBM Plex Mono
fonts:
- asset: fonts/IBMPlexMono-Regular.ttf
- asset: fonts/IBMPlexMono-Italic.ttf
style: italic
- asset: fonts/IBMPlexMono-Bold.ttf
weight: 700

View File

@ -1,8 +1,27 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
void main() {}
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bluetooth_low_energy_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}