├── .gitignore ├── .metadata ├── AUTHORS ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── net │ │ │ │ │ └── bemacized │ │ │ │ │ └── xiaomi_scale_example │ │ │ │ │ └── MainActivity.java │ │ │ └── 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 │ ├── settings.gradle │ └── settings_aar.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── .last_build_id │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── 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 │ ├── main.dart │ ├── measurement_pane.dart │ ├── raw_data_pane.dart │ ├── scanning_pane.dart │ └── util │ │ └── permissions.dart ├── pubspec.lock └── pubspec.yaml ├── lib ├── src │ ├── mi_scale.dart │ └── model │ │ ├── device │ │ ├── mi_scale_device.dart │ │ └── mi_scale_device_v2.dart │ │ ├── gender.dart │ │ ├── mi_scale_data.dart │ │ ├── mi_scale_extra_data.dart │ │ ├── mi_scale_measurement.dart │ │ └── mi_scale_unit.dart └── xiaomi_scale.dart ├── pubspec.lock ├── pubspec.yaml └── readme_res ├── scale_v2.jpg └── screenshots.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | doc/ 9 | .idea/ 10 | *.iml 11 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2294d75bfa8d067ba90230c0fc2268f3636d7584 8 | channel: beta 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Below is a list of people and organizations that have contributed 2 | # to the xiaomi_scale plugin. Names should be added to the list like so: 3 | # 4 | # Name/Organization 5 | 6 | Bodhi Mulders 7 | Koen Van Looveren 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.1 2 | Updated: 3 | - Pubspec homepage 4 | 5 | ## 2.2.0 6 | Added: 7 | - Support for extra body data, based on impedance. 8 | 9 | ## 2.1.0 10 | Updated: 11 | - Version bump the flutter_reactive_ble so we can use the correct gradle dependency 12 | Updated example: 13 | - Updated example to compile sdk 31 14 | - Disabled arm simulators for example 15 | 16 | ## 2.0.0 17 | Added: 18 | - Nullsafety 19 | Updated: 20 | - Version bump the flutter_reactive_ble so we can use the correct gradle dependency 21 | 22 | ## 1.0.2 23 | Fixed: 24 | - Issues fixed that prevented library from working on iOS 25 | - Various code improvements 26 | 27 | ## 1.0.1 28 | Fixed: 29 | - Dismiss repeat measurement events that are identical. 30 | 31 | ## 1.0.0 32 | 33 | First major release 34 | 35 | ## 0.0.3 36 | Updated: 37 | - Updated readme 38 | 39 | ## 0.0.2 40 | Fixed: 41 | - Formatting fixes 42 | 43 | ## 0.0.1 44 | 45 | Initial release of plugin 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Bodhi Mulders 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xiaomi_scale 2 | 3 | [![pub package](https://img.shields.io/pub/v/xiaomi_scale.svg)](https://pub.dartlang.org/packages/xiaomi_scale) 4 | 5 | A Flutter plugin to take measurements from Xiaomi weight scales. 6 | 7 | App Screenshots 8 | 9 | **What it does:** 10 | 11 | * Track measurements 12 | * Weight 13 | * Device weight unit (kg/lbs) 14 | * Impedance 15 | * Progress (e.g. Measuring -> Stabilized -> Measured) 16 | * Scan for nearby Xiaomi scales 17 | * Direct scale data 18 | * Weight 19 | * Device weight unit 20 | * Device timestamp 21 | * Impedance 22 | * Flags 23 | * Weight stabilized 24 | * Weight removed 25 | * Measurement completes 26 | * Calculate* body data from impedance: 27 | * Body fat 28 | * Visceral Fat 29 | * Bone Mass 30 | * Water 31 | * Muscle Mass 32 | * BMI 33 | * LBM Coefficient 34 | 35 | _* Note: These values are calculated through reverse engineered functions. While they should be similar to what the official app by Xiaomi reads, their accuracy is similarly not guaranteed and should be taken with a grain of salt._ 36 | 37 | **What it does NOT do:** 38 | 39 | * Sync historical data stored on device 40 | * Configure the device settings 41 | 42 | ## Supported devices 43 | 44 | | **Image** | **Name** | 45 | | ------------------------------------------------------------ | --------------------------- | 46 | | Mi Body Composition Scale 2 | Mi Body Composition Scale 2 | 47 | 48 | I am still looking to support the **Xiaomi Scale (v1)** as well (The one without the 4 electrodes on top). 49 | I only have access to the v2 model, and therefore am not able to test. In case you have access to one and are willing to help out, please get in contact! 50 | 51 | ## How to use 52 | 53 | First of all I can recommend to just take a look at the [example](https://github.com/BeMacized/xiaomi_scale/tree/master/example). 54 | 55 | ### Setup iOS 56 | 57 | Min iOS Development Target => 11 58 | This is because of flutter_reactive_ble 59 | 60 | Add a description why you want to use the bluetooth peripherals in the info.plist 61 | iOS13 and higher 62 | ``` 63 | NSBluetoothAlwaysUsageDescription 64 | Connect to xiaomi scale to get weight 65 | ``` 66 | 67 | iOS12 and lower 68 | ``` 69 | NSBluetoothPeripheralUsageDescription 70 | Connect to xiaomi scale to get weight 71 | ``` 72 | 73 | No need to ask for runtime permission. This is already handled by flutter_reactive_ble 74 | Best practice: 75 | You should check if the permission is already given. if not show a message to the user. You can only request the permission once. 76 | After that you should check the status of the permission yourself. (this is not handled by this package or flutter_reactive_ble) 77 | 78 | ### Setup Android 79 | 80 | Min sdk Development Target => 24 81 | This is because of flutter_reactive_ble 82 | 83 | flutter_reactive_ble adds these permissions automaticly 84 | ``` 85 | 86 | 87 | 88 | ``` 89 | 90 | At runtime you should still request the location permissions yourself. Otherwise the app won't work. 91 | 92 | ### Setup Dart 93 | 94 | 95 | For the examples below, grab an instance of `MiScale` first. 96 | 97 | ```typescript 98 | MiScale _mi = MiScale.instance; 99 | ``` 100 | 101 | ### Tracking measurements 102 | 103 | You can keep track of measurements as follows: 104 | 105 | ```typescript 106 | StreamSubscription subscription = 107 | _mi.takeMeasurements().listen((MiScaleMeasurement measurement) { 108 | // Code for handing measurement 109 | }); 110 | 111 | // Stop taking measurements 112 | subscription.cancel(); 113 | ``` 114 | 115 | #### Cancelling measurements 116 | 117 | Measurements must be cancelled before a new measurement can be started for the same device. Measurements are automatically cancelled when they reached the final `MEASURED` stage. 118 | 119 | In case you would like to cancel a measurement before the `MEASURED` stage is reached, it is up to you to cancel the measurement manually. 120 | 121 | ```typescript 122 | _mi.cancelMeasurement(deviceId) 123 | ``` 124 | 125 | You can obtain the `deviceId` either from a `MiScaleMeasurement` or `MiScaleDevice` instance. 126 | 127 | **Note:** If a user steps off the scale before the `STABILIZED` stage is reached, the measurement will remain incomplete. Hence, if you want to take a new measurement, you must also cancel the incomplete measurement first 128 | 129 | ### Scanning for devices 130 | 131 | The `discoverDevices` stream will only output compatible devices that it finds. 132 | 133 | ```typescript 134 | StreamSubscription subscription = _mi.discoverDevices( 135 | duration: Duration(seconds: 10), // Optional, default is 5 seconds 136 | ).listen( 137 | (MiScaleDevice device) { 138 | // Code for handling found device 139 | }, 140 | ); 141 | 142 | // Stop discovering before given duration has expired 143 | subscription?.cancel(); 144 | ``` 145 | 146 | ### Getting all scale data 147 | 148 | If you want to get the scale data directly without tracking measurements, you can do so as follows: 149 | 150 | ```typescript 151 | StreamSubscription subscription = _mi.readScaleData().listen( 152 | (data) { 153 | // Code to handle the scale data 154 | }, 155 | ); 156 | 157 | // Stop reading data 158 | subscription.cancel(); 159 | ``` 160 | 161 | ## How to adjust ProGuard (Android) 162 | In case you are using ProGuard add the following snippet to your proguard-rules.pro file: 163 | 164 | ``` 165 | -keep class com.signify.hue.** { *; } 166 | ``` 167 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | errors: 3 | missing_required_param: error 4 | missing_return: error 5 | todo: ignore 6 | sdk_version_async_exported_from_core: ignore 7 | 8 | linter: 9 | rules: 10 | - avoid_print 11 | - always_put_required_named_parameters_first 12 | - always_require_non_null_named_parameters 13 | - annotate_overrides 14 | - avoid_annotating_with_dynamic 15 | - avoid_bool_literals_in_conditional_expressions 16 | - avoid_catching_errors 17 | - avoid_double_and_int_checks 18 | - avoid_empty_else 19 | - avoid_field_initializers_in_const_classes 20 | - avoid_implementing_value_types 21 | - avoid_init_to_null 22 | - avoid_js_rounded_ints 23 | - avoid_null_checks_in_equality_operators 24 | - avoid_positional_boolean_parameters 25 | - avoid_private_typedef_functions 26 | - avoid_relative_lib_imports 27 | - avoid_renaming_method_parameters 28 | - avoid_return_types_on_setters 29 | - avoid_returning_null 30 | - avoid_returning_null_for_future 31 | - avoid_returning_null_for_void 32 | - avoid_returning_this 33 | - avoid_setters_without_getters 34 | - avoid_shadowing_type_parameters 35 | - avoid_single_cascade_in_expression_statements 36 | - avoid_slow_async_io 37 | - avoid_types_as_parameter_names 38 | - avoid_types_on_closure_parameters 39 | - avoid_unused_constructor_parameters 40 | - avoid_void_async 41 | - await_only_futures 42 | - camel_case_types 43 | - cancel_subscriptions 44 | - cascade_invocations 45 | - close_sinks 46 | - comment_references 47 | - control_flow_in_finally 48 | - curly_braces_in_flow_control_structures 49 | - directives_ordering 50 | - empty_catches 51 | - empty_constructor_bodies 52 | - empty_statements 53 | - file_names 54 | - hash_and_equals 55 | - implementation_imports 56 | - invariant_booleans 57 | - iterable_contains_unrelated_type 58 | - join_return_with_assignment 59 | - library_names 60 | - library_prefixes 61 | - list_remove_unrelated_type 62 | - literal_only_boolean_expressions 63 | - no_adjacent_strings_in_list 64 | - no_duplicate_case_values 65 | - non_constant_identifier_names 66 | - null_closures 67 | - omit_local_variable_types 68 | - one_member_abstracts 69 | - only_throw_errors 70 | - overridden_fields 71 | - package_api_docs 72 | - package_names 73 | - package_prefixed_library_names 74 | - parameter_assignments 75 | - prefer_adjacent_string_concatenation 76 | - prefer_asserts_in_initializer_lists 77 | - prefer_conditional_assignment 78 | - prefer_const_constructors 79 | - prefer_const_constructors_in_immutables 80 | - prefer_const_declarations 81 | - prefer_const_literals_to_create_immutables 82 | - prefer_contains 83 | - prefer_equal_for_default_values 84 | - prefer_final_fields 85 | - prefer_final_in_for_each 86 | - prefer_final_locals 87 | - prefer_foreach 88 | - prefer_function_declarations_over_variables 89 | - prefer_generic_function_type_aliases 90 | - prefer_initializing_formals 91 | - prefer_int_literals 92 | - prefer_interpolation_to_compose_strings 93 | - prefer_is_empty 94 | - prefer_is_not_empty 95 | - prefer_iterable_whereType 96 | - prefer_null_aware_operators 97 | - prefer_single_quotes 98 | - prefer_typing_uninitialized_variables 99 | - prefer_void_to_null 100 | - recursive_getters 101 | - sort_pub_dependencies 102 | - test_types_in_equals 103 | - throw_in_finally 104 | - type_init_formals 105 | - unawaited_futures 106 | - unnecessary_await_in_return 107 | - unnecessary_brace_in_string_interps 108 | - unnecessary_const 109 | - unnecessary_getters_setters 110 | - unnecessary_lambdas 111 | - unnecessary_new 112 | - unnecessary_null_aware_assignments 113 | - unnecessary_null_in_if_null_operators 114 | - unnecessary_overrides 115 | - unnecessary_parenthesis 116 | - unnecessary_statements 117 | - unnecessary_this 118 | - unrelated_type_equality_checks 119 | - use_full_hex_values_for_flutter_colors 120 | - use_rethrow_when_possible 121 | - use_setters_to_change_properties 122 | - use_string_buffers 123 | - use_to_and_as_if_applicable 124 | - valid_regexps 125 | - void_checks -------------------------------------------------------------------------------- /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 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | -------------------------------------------------------------------------------- /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: 1ad9baa8b99a2897c20f9e6e54d3b9b359ade314 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # xiaomi_scale_example 2 | 3 | Demonstrates how to use the xiaomi_scale plugin. 4 | 5 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | errors: 3 | missing_required_param: error 4 | missing_return: error 5 | todo: ignore 6 | sdk_version_async_exported_from_core: ignore 7 | 8 | linter: 9 | rules: 10 | - always_put_required_named_parameters_first 11 | - always_require_non_null_named_parameters 12 | - annotate_overrides 13 | - avoid_annotating_with_dynamic 14 | - avoid_bool_literals_in_conditional_expressions 15 | - avoid_catching_errors 16 | - avoid_double_and_int_checks 17 | - avoid_empty_else 18 | - avoid_field_initializers_in_const_classes 19 | - avoid_implementing_value_types 20 | - avoid_init_to_null 21 | - avoid_js_rounded_ints 22 | - avoid_null_checks_in_equality_operators 23 | - avoid_positional_boolean_parameters 24 | - avoid_private_typedef_functions 25 | - avoid_relative_lib_imports 26 | - avoid_renaming_method_parameters 27 | - avoid_return_types_on_setters 28 | - avoid_returning_null 29 | - avoid_returning_null_for_future 30 | - avoid_returning_null_for_void 31 | - avoid_returning_this 32 | - avoid_setters_without_getters 33 | - avoid_shadowing_type_parameters 34 | - avoid_single_cascade_in_expression_statements 35 | - avoid_slow_async_io 36 | - avoid_types_as_parameter_names 37 | - avoid_types_on_closure_parameters 38 | - avoid_unused_constructor_parameters 39 | - avoid_void_async 40 | - await_only_futures 41 | - camel_case_types 42 | - cancel_subscriptions 43 | - cascade_invocations 44 | - close_sinks 45 | - comment_references 46 | - control_flow_in_finally 47 | - curly_braces_in_flow_control_structures 48 | - directives_ordering 49 | - empty_catches 50 | - empty_constructor_bodies 51 | - empty_statements 52 | - file_names 53 | - hash_and_equals 54 | - implementation_imports 55 | - invariant_booleans 56 | - iterable_contains_unrelated_type 57 | - join_return_with_assignment 58 | - library_names 59 | - library_prefixes 60 | - list_remove_unrelated_type 61 | - literal_only_boolean_expressions 62 | - no_adjacent_strings_in_list 63 | - no_duplicate_case_values 64 | - non_constant_identifier_names 65 | - null_closures 66 | - omit_local_variable_types 67 | - one_member_abstracts 68 | - only_throw_errors 69 | - overridden_fields 70 | - package_api_docs 71 | - package_names 72 | - package_prefixed_library_names 73 | - parameter_assignments 74 | - prefer_adjacent_string_concatenation 75 | - prefer_asserts_in_initializer_lists 76 | - prefer_conditional_assignment 77 | - prefer_const_constructors 78 | - prefer_const_constructors_in_immutables 79 | - prefer_const_declarations 80 | - prefer_const_literals_to_create_immutables 81 | - prefer_contains 82 | - prefer_equal_for_default_values 83 | - prefer_final_fields 84 | - prefer_final_in_for_each 85 | - prefer_final_locals 86 | - prefer_foreach 87 | - prefer_function_declarations_over_variables 88 | - prefer_generic_function_type_aliases 89 | - prefer_initializing_formals 90 | - prefer_int_literals 91 | - prefer_interpolation_to_compose_strings 92 | - prefer_is_empty 93 | - prefer_is_not_empty 94 | - prefer_iterable_whereType 95 | - prefer_null_aware_operators 96 | - prefer_single_quotes 97 | - prefer_typing_uninitialized_variables 98 | - prefer_void_to_null 99 | - recursive_getters 100 | - sort_pub_dependencies 101 | - test_types_in_equals 102 | - throw_in_finally 103 | - type_init_formals 104 | - unawaited_futures 105 | - unnecessary_await_in_return 106 | - unnecessary_brace_in_string_interps 107 | - unnecessary_const 108 | - unnecessary_getters_setters 109 | - unnecessary_lambdas 110 | - unnecessary_new 111 | - unnecessary_null_aware_assignments 112 | - unnecessary_null_in_if_null_operators 113 | - unnecessary_overrides 114 | - unnecessary_parenthesis 115 | - unnecessary_statements 116 | - unnecessary_this 117 | - unrelated_type_equality_checks 118 | - use_full_hex_values_for_flutter_colors 119 | - use_rethrow_when_possible 120 | - use_setters_to_change_properties 121 | - use_string_buffers 122 | - use_to_and_as_if_applicable 123 | - valid_regexps 124 | - void_checks -------------------------------------------------------------------------------- /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 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 31 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "net.bemacized.xiaomi_scale_example" 37 | minSdkVersion 24 38 | targetSdkVersion 31 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | } 42 | 43 | buildTypes { 44 | release { 45 | // TODO: Add your own signing config for the release build. 46 | // Signing with the debug keys for now, so `flutter run --release` works. 47 | signingConfig signingConfigs.debug 48 | } 49 | } 50 | } 51 | 52 | flutter { 53 | source '../..' 54 | } 55 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 20 | 24 | 27 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/net/bemacized/xiaomi_scale_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package net.bemacized.xiaomi_scale_example; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | -------------------------------------------------------------------------------- /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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:4.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 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.7.1-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | -------------------------------------------------------------------------------- /example/android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /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/.last_build_id: -------------------------------------------------------------------------------- 1 | 3f2adc18ed814f540662c30bba208808 -------------------------------------------------------------------------------- /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 | 9.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, '9.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.build_configurations.each do |config| 39 | config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" 40 | end 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - "permission_handler (5.1.0+2)": 4 | - Flutter 5 | - Protobuf (3.19.1) 6 | - reactive_ble_mobile (0.0.1): 7 | - Flutter 8 | - Protobuf (~> 3.5) 9 | - SwiftProtobuf (~> 1.0) 10 | - SwiftProtobuf (1.18.0) 11 | 12 | DEPENDENCIES: 13 | - Flutter (from `Flutter`) 14 | - permission_handler (from `.symlinks/plugins/permission_handler/ios`) 15 | - reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`) 16 | 17 | SPEC REPOS: 18 | trunk: 19 | - Protobuf 20 | - SwiftProtobuf 21 | 22 | EXTERNAL SOURCES: 23 | Flutter: 24 | :path: Flutter 25 | permission_handler: 26 | :path: ".symlinks/plugins/permission_handler/ios" 27 | reactive_ble_mobile: 28 | :path: ".symlinks/plugins/reactive_ble_mobile/ios" 29 | 30 | SPEC CHECKSUMS: 31 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 32 | permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 33 | Protobuf: 3724efa50cb2846d7ccebc8691c574e85fd74471 34 | reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c 35 | SwiftProtobuf: c3c12645230d9b09c72267e0de89468c5543bd86 36 | 37 | PODFILE CHECKSUM: 794c88b7d405598319947bbbef908d8388ad4297 38 | 39 | COCOAPODS: 1.10.0 40 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 26137D5747FA5B75CE0D280E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D518828FE7FAE935847A17B7 /* Pods_Runner.framework */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 19717937511563B29D6A09E1 /* 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 = ""; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 79600FD085C415C98E953FD5 /* 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 = ""; }; 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 41 | 7EE5C63501DF333030531599 /* 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 = ""; }; 42 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 43 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 44 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | D518828FE7FAE935847A17B7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 26137D5747FA5B75CE0D280E /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 78AF30E401129428778FAEBB /* Pods */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 19717937511563B29D6A09E1 /* Pods-Runner.debug.xcconfig */, 68 | 7EE5C63501DF333030531599 /* Pods-Runner.release.xcconfig */, 69 | 79600FD085C415C98E953FD5 /* Pods-Runner.profile.xcconfig */, 70 | ); 71 | path = Pods; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | 78AF30E401129428778FAEBB /* Pods */, 92 | C3AF3C16A329001BE3225DA3 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 97C146F11CF9000F007C117D /* Supporting Files */, 112 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 113 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 114 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 115 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 116 | ); 117 | path = Runner; 118 | sourceTree = ""; 119 | }; 120 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | ); 124 | name = "Supporting Files"; 125 | sourceTree = ""; 126 | }; 127 | C3AF3C16A329001BE3225DA3 /* Frameworks */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | D518828FE7FAE935847A17B7 /* Pods_Runner.framework */, 131 | ); 132 | name = Frameworks; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 97C146ED1CF9000F007C117D /* Runner */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 141 | buildPhases = ( 142 | 6A7D25971F1B9EE0C1E5C76D /* [CP] Check Pods Manifest.lock */, 143 | 9740EEB61CF901F6004384FC /* Run Script */, 144 | 97C146EA1CF9000F007C117D /* Sources */, 145 | 97C146EB1CF9000F007C117D /* Frameworks */, 146 | 97C146EC1CF9000F007C117D /* Resources */, 147 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 148 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 149 | EC3B0829EF0F9CFB8932F40E /* [CP] Embed Pods Frameworks */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = Runner; 156 | productName = Runner; 157 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 97C146E61CF9000F007C117D /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastUpgradeCheck = 1020; 167 | ORGANIZATIONNAME = ""; 168 | TargetAttributes = { 169 | 97C146ED1CF9000F007C117D = { 170 | CreatedOnToolsVersion = 7.3.1; 171 | LastSwiftMigration = 1100; 172 | }; 173 | }; 174 | }; 175 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 176 | compatibilityVersion = "Xcode 9.3"; 177 | developmentRegion = en; 178 | hasScannedForEncodings = 0; 179 | knownRegions = ( 180 | en, 181 | Base, 182 | ); 183 | mainGroup = 97C146E51CF9000F007C117D; 184 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 97C146ED1CF9000F007C117D /* Runner */, 189 | ); 190 | }; 191 | /* End PBXProject section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 97C146EC1CF9000F007C117D /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 199 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 200 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 201 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXResourcesBuildPhase section */ 206 | 207 | /* Begin PBXShellScriptBuildPhase section */ 208 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "Thin Binary"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 221 | }; 222 | 6A7D25971F1B9EE0C1E5C76D /* [CP] Check Pods Manifest.lock */ = { 223 | isa = PBXShellScriptBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputFileListPaths = ( 228 | ); 229 | inputPaths = ( 230 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 231 | "${PODS_ROOT}/Manifest.lock", 232 | ); 233 | name = "[CP] Check Pods Manifest.lock"; 234 | outputFileListPaths = ( 235 | ); 236 | outputPaths = ( 237 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | shellPath = /bin/sh; 241 | 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"; 242 | showEnvVarsInLog = 0; 243 | }; 244 | 9740EEB61CF901F6004384FC /* Run Script */ = { 245 | isa = PBXShellScriptBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | inputPaths = ( 250 | ); 251 | name = "Run Script"; 252 | outputPaths = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | shellPath = /bin/sh; 256 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 257 | }; 258 | EC3B0829EF0F9CFB8932F40E /* [CP] Embed Pods Frameworks */ = { 259 | isa = PBXShellScriptBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | inputFileListPaths = ( 264 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 265 | ); 266 | name = "[CP] Embed Pods Frameworks"; 267 | outputFileListPaths = ( 268 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 273 | showEnvVarsInLog = 0; 274 | }; 275 | /* End PBXShellScriptBuildPhase section */ 276 | 277 | /* Begin PBXSourcesBuildPhase section */ 278 | 97C146EA1CF9000F007C117D /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 283 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 97C146FB1CF9000F007C117D /* Base */, 294 | ); 295 | name = Main.storyboard; 296 | sourceTree = ""; 297 | }; 298 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 97C147001CF9000F007C117D /* Base */, 302 | ); 303 | name = LaunchScreen.storyboard; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 319 | CLANG_WARN_BOOL_CONVERSION = YES; 320 | CLANG_WARN_COMMA = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | SUPPORTED_PLATFORMS = iphoneos; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Profile; 358 | }; 359 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 360 | isa = XCBuildConfiguration; 361 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | CLANG_ENABLE_MODULES = YES; 365 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 366 | DEVELOPMENT_TEAM = ""; 367 | ENABLE_BITCODE = NO; 368 | FRAMEWORK_SEARCH_PATHS = ( 369 | "$(inherited)", 370 | "$(PROJECT_DIR)/Flutter", 371 | ); 372 | INFOPLIST_FILE = Runner/Info.plist; 373 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 374 | LD_RUNPATH_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "@executable_path/Frameworks", 377 | ); 378 | LIBRARY_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "$(PROJECT_DIR)/Flutter", 381 | ); 382 | PRODUCT_BUNDLE_IDENTIFIER = "net.bemacized.xiaomi-scale-example"; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 385 | SWIFT_VERSION = 5.0; 386 | VERSIONING_SYSTEM = "apple-generic"; 387 | }; 388 | name = Profile; 389 | }; 390 | 97C147031CF9000F007C117D /* Debug */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 396 | CLANG_CXX_LIBRARY = "libc++"; 397 | CLANG_ENABLE_MODULES = YES; 398 | CLANG_ENABLE_OBJC_ARC = YES; 399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_COMMA = YES; 402 | CLANG_WARN_CONSTANT_CONVERSION = YES; 403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INFINITE_RECURSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 414 | CLANG_WARN_STRICT_PROTOTYPES = YES; 415 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 416 | CLANG_WARN_UNREACHABLE_CODE = YES; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 419 | COPY_PHASE_STRIP = NO; 420 | DEBUG_INFORMATION_FORMAT = dwarf; 421 | ENABLE_STRICT_OBJC_MSGSEND = YES; 422 | ENABLE_TESTABILITY = YES; 423 | GCC_C_LANGUAGE_STANDARD = gnu99; 424 | GCC_DYNAMIC_NO_PIC = NO; 425 | GCC_NO_COMMON_BLOCKS = YES; 426 | GCC_OPTIMIZATION_LEVEL = 0; 427 | GCC_PREPROCESSOR_DEFINITIONS = ( 428 | "DEBUG=1", 429 | "$(inherited)", 430 | ); 431 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 432 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 433 | GCC_WARN_UNDECLARED_SELECTOR = YES; 434 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 435 | GCC_WARN_UNUSED_FUNCTION = YES; 436 | GCC_WARN_UNUSED_VARIABLE = YES; 437 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 438 | MTL_ENABLE_DEBUG_INFO = YES; 439 | ONLY_ACTIVE_ARCH = YES; 440 | SDKROOT = iphoneos; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | }; 443 | name = Debug; 444 | }; 445 | 97C147041CF9000F007C117D /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ALWAYS_SEARCH_USER_PATHS = NO; 449 | CLANG_ANALYZER_NONNULL = YES; 450 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 451 | CLANG_CXX_LIBRARY = "libc++"; 452 | CLANG_ENABLE_MODULES = YES; 453 | CLANG_ENABLE_OBJC_ARC = YES; 454 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 455 | CLANG_WARN_BOOL_CONVERSION = YES; 456 | CLANG_WARN_COMMA = YES; 457 | CLANG_WARN_CONSTANT_CONVERSION = YES; 458 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 459 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 460 | CLANG_WARN_EMPTY_BODY = YES; 461 | CLANG_WARN_ENUM_CONVERSION = YES; 462 | CLANG_WARN_INFINITE_RECURSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 465 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 466 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 467 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 468 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 469 | CLANG_WARN_STRICT_PROTOTYPES = YES; 470 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 471 | CLANG_WARN_UNREACHABLE_CODE = YES; 472 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 473 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 474 | COPY_PHASE_STRIP = NO; 475 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 476 | ENABLE_NS_ASSERTIONS = NO; 477 | ENABLE_STRICT_OBJC_MSGSEND = YES; 478 | GCC_C_LANGUAGE_STANDARD = gnu99; 479 | GCC_NO_COMMON_BLOCKS = YES; 480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 482 | GCC_WARN_UNDECLARED_SELECTOR = YES; 483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 484 | GCC_WARN_UNUSED_FUNCTION = YES; 485 | GCC_WARN_UNUSED_VARIABLE = YES; 486 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 487 | MTL_ENABLE_DEBUG_INFO = NO; 488 | SDKROOT = iphoneos; 489 | SUPPORTED_PLATFORMS = iphoneos; 490 | SWIFT_COMPILATION_MODE = wholemodule; 491 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | VALIDATE_PRODUCT = YES; 494 | }; 495 | name = Release; 496 | }; 497 | 97C147061CF9000F007C117D /* Debug */ = { 498 | isa = XCBuildConfiguration; 499 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 500 | buildSettings = { 501 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 502 | CLANG_ENABLE_MODULES = YES; 503 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 504 | DEVELOPMENT_TEAM = ""; 505 | ENABLE_BITCODE = NO; 506 | FRAMEWORK_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "$(PROJECT_DIR)/Flutter", 509 | ); 510 | INFOPLIST_FILE = Runner/Info.plist; 511 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 512 | LD_RUNPATH_SEARCH_PATHS = ( 513 | "$(inherited)", 514 | "@executable_path/Frameworks", 515 | ); 516 | LIBRARY_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(PROJECT_DIR)/Flutter", 519 | ); 520 | PRODUCT_BUNDLE_IDENTIFIER = "net.bemacized.xiaomi-scale-example"; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 523 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 524 | SWIFT_VERSION = 5.0; 525 | VERSIONING_SYSTEM = "apple-generic"; 526 | }; 527 | name = Debug; 528 | }; 529 | 97C147071CF9000F007C117D /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 532 | buildSettings = { 533 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 534 | CLANG_ENABLE_MODULES = YES; 535 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 536 | DEVELOPMENT_TEAM = ""; 537 | ENABLE_BITCODE = NO; 538 | FRAMEWORK_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "$(PROJECT_DIR)/Flutter", 541 | ); 542 | INFOPLIST_FILE = Runner/Info.plist; 543 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/Frameworks", 547 | ); 548 | LIBRARY_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)/Flutter", 551 | ); 552 | PRODUCT_BUNDLE_IDENTIFIER = "net.bemacized.xiaomi-scale-example"; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 555 | SWIFT_VERSION = 5.0; 556 | VERSIONING_SYSTEM = "apple-generic"; 557 | }; 558 | name = Release; 559 | }; 560 | /* End XCBuildConfiguration section */ 561 | 562 | /* Begin XCConfigurationList section */ 563 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 97C147031CF9000F007C117D /* Debug */, 567 | 97C147041CF9000F007C117D /* Release */, 568 | 249021D3217E4FDB00AE95B9 /* Profile */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | defaultConfigurationName = Release; 572 | }; 573 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | 97C147061CF9000F007C117D /* Debug */, 577 | 97C147071CF9000F007C117D /* Release */, 578 | 249021D4217E4FDB00AE95B9 /* Profile */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | /* End XCConfigurationList section */ 584 | }; 585 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 586 | } 587 | -------------------------------------------------------------------------------- /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 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /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 | @UIApplicationMain 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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/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 | xiaomi_scale_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSBluetoothPeripheralUsageDescription 26 | Connect to xiaomi scale to get weight 27 | NSBluetoothAlwaysUsageDescription 28 | Connect to xiaomi scale to get weight 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiaomi_scale_example/measurement_pane.dart'; 3 | import 'package:xiaomi_scale_example/raw_data_pane.dart'; 4 | import 'package:xiaomi_scale_example/scanning_pane.dart'; 5 | 6 | void main() { 7 | runApp(ScaleApp()); 8 | } 9 | 10 | class ScaleApp extends StatefulWidget { 11 | @override 12 | _ScaleAppState createState() => _ScaleAppState(); 13 | } 14 | 15 | class _ScaleAppState extends State { 16 | var _currentIndex = 0; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return MaterialApp( 21 | home: Scaffold( 22 | appBar: AppBar( 23 | title: const Text('Scale Example App'), 24 | ), 25 | body: IndexedStack( 26 | index: _currentIndex, 27 | children: [ 28 | MeasurementPane(), 29 | ScanningPane(), 30 | RawDataPane(), 31 | ], 32 | ), 33 | bottomNavigationBar: BottomNavigationBar( 34 | currentIndex: _currentIndex, 35 | onTap: _bottomTapped, 36 | selectedItemColor: Colors.black, 37 | unselectedItemColor: Colors.black26, 38 | items: const [ 39 | BottomNavigationBarItem( 40 | icon: Icon(Icons.timeline), 41 | label: 'Measurements', 42 | ), 43 | BottomNavigationBarItem( 44 | icon: Icon(Icons.search), 45 | label: 'Scanning', 46 | ), 47 | BottomNavigationBarItem( 48 | icon: Icon(Icons.description), 49 | label: 'Raw Data', 50 | ), 51 | ], 52 | ), 53 | ), 54 | ); 55 | } 56 | 57 | void _bottomTapped(int index) => setState(() => _currentIndex = index); 58 | } 59 | -------------------------------------------------------------------------------- /example/lib/measurement_pane.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:xiaomi_scale/xiaomi_scale.dart'; 5 | 6 | import 'util/permissions.dart'; 7 | 8 | class MeasurementPane extends StatefulWidget { 9 | @override 10 | _MeasurementPaneState createState() => _MeasurementPaneState(); 11 | } 12 | 13 | class _MeasurementPaneState extends State { 14 | StreamSubscription? _measurementSubscription; 15 | Map measurements = {}; // 16 | final _scale = MiScale.instance; 17 | 18 | @override 19 | void dispose() { 20 | super.dispose(); 21 | stopTakingMeasurements(dispose: true); 22 | } 23 | 24 | Future startTakingMeasurements() async { 25 | // Make sure we have location permission required for BLE scanning 26 | if (!await checkPermission()) return; 27 | // Start taking measurements 28 | setState(() { 29 | _measurementSubscription = _scale.takeMeasurements().listen( 30 | (measurement) { 31 | setState(() { 32 | measurements[measurement.id] = measurement; 33 | }); 34 | }, 35 | onError: (e) { 36 | print(e); 37 | stopTakingMeasurements(); 38 | }, 39 | onDone: stopTakingMeasurements, 40 | ); 41 | }); 42 | } 43 | 44 | void stopTakingMeasurements({dispose = false}) { 45 | _measurementSubscription?.cancel(); 46 | _measurementSubscription = null; 47 | if (!dispose) setState(() {}); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return Column( 53 | children: [ 54 | Row( 55 | mainAxisAlignment: MainAxisAlignment.spaceAround, 56 | crossAxisAlignment: CrossAxisAlignment.center, 57 | children: [ 58 | Expanded( 59 | child: ElevatedButton( 60 | child: const Text( 61 | 'Start Taking Measurements', 62 | textAlign: TextAlign.center, 63 | ), 64 | onPressed: _measurementSubscription == null 65 | ? startTakingMeasurements 66 | : null, 67 | ), 68 | ), 69 | Expanded( 70 | child: ElevatedButton( 71 | child: const Text( 72 | 'Stop Taking Measurements', 73 | textAlign: TextAlign.center, 74 | ), 75 | onPressed: _measurementSubscription != null 76 | ? stopTakingMeasurements 77 | : null, 78 | ), 79 | ), 80 | ], 81 | ), 82 | Opacity( 83 | opacity: _measurementSubscription != null ? 1 : 0, 84 | child: const Center(child: CircularProgressIndicator()), 85 | ), 86 | Expanded( 87 | child: SingleChildScrollView( 88 | child: Column( 89 | children: 90 | measurements.values.map(_buildMeasurementWidget).toList(), 91 | ), 92 | ), 93 | ) 94 | ], 95 | ); 96 | } 97 | 98 | Widget _buildMeasurementWidget(MiScaleMeasurement measurement) { 99 | final extraData = measurement.getBodyData(MiScaleGender.MALE, 25, 188); 100 | return Container( 101 | child: Row( 102 | children: [ 103 | Expanded( 104 | child: Padding( 105 | padding: const EdgeInsets.all(8), 106 | child: Column( 107 | crossAxisAlignment: CrossAxisAlignment.start, 108 | mainAxisSize: MainAxisSize.min, 109 | children: [ 110 | Text( 111 | measurement.weight.toStringAsFixed(2) + 112 | measurement.unit.toString().split('.')[1], 113 | ), 114 | Text( 115 | measurement.stage.toString().split('.')[1], 116 | ), 117 | Text( 118 | measurement.dateTime.toIso8601String(), 119 | ), 120 | if (extraData != null) ...[ 121 | Container( 122 | height: 2, 123 | color: Colors.grey, 124 | ), 125 | Text( 126 | 'bodyFat: ${extraData.bodyFat}', 127 | ), 128 | Text( 129 | 'boneMass: ${extraData.boneMass}', 130 | ), 131 | Text( 132 | 'lbmCoefficient: ${extraData.lbmCoefficient}', 133 | ), 134 | Text( 135 | 'muscleMass: ${extraData.muscleMass}', 136 | ), 137 | Text( 138 | 'BMI: ${extraData.bmi}', 139 | ), 140 | Text( 141 | 'water: ${extraData.water}', 142 | ), 143 | Text( 144 | 'visceralFat: ${extraData.visceralFat}', 145 | ), 146 | ], 147 | ], 148 | ), 149 | ), 150 | ), 151 | Padding( 152 | padding: const EdgeInsets.all(8), 153 | child: IconButton( 154 | icon: const Icon(Icons.delete), 155 | onPressed: () { 156 | final deviceId = measurement.deviceId; 157 | // Cancel the measurement if it is still active 158 | if (measurement.isActive && deviceId != null) { 159 | _scale.cancelMeasurement(deviceId); 160 | } 161 | // Remove the measurement from the list 162 | setState(() { 163 | measurements.remove(measurement.id); 164 | }); 165 | }, 166 | ), 167 | ), 168 | ], 169 | ), 170 | ); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /example/lib/raw_data_pane.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:xiaomi_scale/xiaomi_scale.dart'; 6 | 7 | import 'util/permissions.dart'; 8 | 9 | class RawDataPane extends StatefulWidget { 10 | @override 11 | _RawDataPaneState createState() => _RawDataPaneState(); 12 | } 13 | 14 | class _RawDataPaneState extends State { 15 | StreamSubscription? _dataSubscription; 16 | List scaleData = []; 17 | final _scale = MiScale.instance; 18 | 19 | @override 20 | void dispose() { 21 | super.dispose(); 22 | stopTakingData(dispose: true); 23 | } 24 | 25 | Future startTakingData() async { 26 | // Make sure we have location permission required for BLE scanning 27 | if (!await checkPermission()) return; 28 | // Start taking measurements 29 | setState(() { 30 | _dataSubscription = _scale.readScaleData().listen( 31 | (data) { 32 | setState(() { 33 | scaleData.insert(0, data); 34 | if (scaleData.length > 10) scaleData.removeLast(); 35 | }); 36 | }, 37 | onError: (e) { 38 | print(e); 39 | stopTakingData(); 40 | }, 41 | onDone: stopTakingData, 42 | ); 43 | }); 44 | } 45 | 46 | void stopTakingData({dispose = false}) { 47 | _dataSubscription?.cancel(); 48 | _dataSubscription = null; 49 | if (!dispose) setState(() {}); 50 | } 51 | 52 | @override 53 | Widget build(BuildContext context) { 54 | return Column( 55 | children: [ 56 | Row( 57 | mainAxisAlignment: MainAxisAlignment.spaceAround, 58 | crossAxisAlignment: CrossAxisAlignment.center, 59 | children: [ 60 | ElevatedButton( 61 | child: const Text('Start Reading'), 62 | onPressed: _dataSubscription == null ? startTakingData : null, 63 | ), 64 | ElevatedButton( 65 | child: const Text('Stop Reading'), 66 | onPressed: _dataSubscription != null ? stopTakingData : null, 67 | ), 68 | ], 69 | ), 70 | Opacity( 71 | opacity: _dataSubscription != null ? 1 : 0, 72 | child: const Center(child: CircularProgressIndicator()), 73 | ), 74 | Expanded( 75 | child: SingleChildScrollView( 76 | child: Column( 77 | children: [ 78 | const Text('Last 10 readings:'), 79 | ...scaleData.map(_buildScaleDataWidget), 80 | ], 81 | ), 82 | ), 83 | ) 84 | ], 85 | ); 86 | } 87 | 88 | Widget _buildScaleDataWidget(MiScaleData data) { 89 | return Container( 90 | child: Padding( 91 | padding: const EdgeInsets.all(8), 92 | child: Text(data.toString()), 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /example/lib/scanning_pane.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:xiaomi_scale/xiaomi_scale.dart'; 5 | 6 | import 'util/permissions.dart'; 7 | 8 | class ScanningPane extends StatefulWidget { 9 | @override 10 | _ScanningPaneState createState() => _ScanningPaneState(); 11 | } 12 | 13 | class _ScanningPaneState extends State { 14 | StreamSubscription? _scanSubscription; 15 | Map devices = {}; // 16 | final _scale = MiScale.instance; 17 | 18 | @override 19 | void dispose() { 20 | stopDiscovery(dispose: true); 21 | super.dispose(); 22 | } 23 | 24 | Future startDiscovery() async { 25 | // Make sure we have location permission required for BLE scanning 26 | if (!await checkPermission()) return; 27 | // Clear device list 28 | devices = {}; 29 | // Start scanning 30 | setState(() { 31 | _scanSubscription = _scale.discoverDevices().listen( 32 | (device) { 33 | print(device); 34 | setState(() { 35 | devices[device.id] = device; 36 | }); 37 | }, 38 | onError: (e) { 39 | print(e); 40 | stopDiscovery(); 41 | }, 42 | onDone: stopDiscovery, 43 | ); 44 | }); 45 | } 46 | 47 | void stopDiscovery({dispose = false}) { 48 | _scanSubscription?.cancel(); 49 | _scanSubscription = null; 50 | if (!dispose) setState(() {}); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Column( 56 | children: [ 57 | Row( 58 | mainAxisAlignment: MainAxisAlignment.spaceAround, 59 | crossAxisAlignment: CrossAxisAlignment.center, 60 | children: [ 61 | ElevatedButton( 62 | child: const Text('Start Scanning'), 63 | onPressed: _scanSubscription == null ? startDiscovery : null, 64 | ), 65 | ElevatedButton( 66 | child: const Text('Stop Scanning'), 67 | onPressed: _scanSubscription != null ? stopDiscovery : null, 68 | ), 69 | ], 70 | ), 71 | Opacity( 72 | opacity: _scanSubscription != null ? 1 : 0, 73 | child: const Center(child: CircularProgressIndicator()), 74 | ), 75 | Expanded( 76 | child: SingleChildScrollView( 77 | child: Column( 78 | children: devices.values.map(_buildDeviceWidget).toList(), 79 | ), 80 | ), 81 | ) 82 | ], 83 | ); 84 | } 85 | 86 | Widget _buildDeviceWidget(MiScaleDevice device) { 87 | return Container( 88 | child: Row( 89 | children: [ 90 | Expanded( 91 | child: Padding( 92 | padding: const EdgeInsets.all(8), 93 | child: Column( 94 | crossAxisAlignment: CrossAxisAlignment.start, 95 | mainAxisSize: MainAxisSize.min, 96 | children: [ 97 | Text('Name: ${device.name}'), 98 | Text('Device ID: ${device.id}'), 99 | ], 100 | ), 101 | ), 102 | ), 103 | Padding( 104 | padding: const EdgeInsets.all(8), 105 | child: Column( 106 | mainAxisAlignment: MainAxisAlignment.start, 107 | mainAxisSize: MainAxisSize.min, 108 | children: [ 109 | Text('RSSI: ${device.rssi}dBm'), 110 | ], 111 | ), 112 | ), 113 | ], 114 | ), 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /example/lib/util/permissions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:device_info_plus/device_info_plus.dart'; 4 | import 'package:permission_handler/permission_handler.dart'; 5 | 6 | Future checkPermission() async { 7 | if (Platform.isIOS) return true; 8 | final androidInfo = await DeviceInfoPlugin().androidInfo; 9 | if ((androidInfo.version.sdkInt ?? 0) >= 31) { 10 | var status = await Permission.bluetoothScan.status; 11 | if (status.isDenied) { 12 | status = await Permission.bluetoothScan.request(); 13 | } 14 | return status.isGranted; 15 | } 16 | var status = await Permission.location.status; 17 | if (status.isDenied) { 18 | status = await Permission.location.request(); 19 | } 20 | return status.isGranted; 21 | } 22 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | crypto: 47 | dependency: transitive 48 | description: 49 | name: crypto 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "3.0.0" 53 | cupertino_icons: 54 | dependency: "direct main" 55 | description: 56 | name: cupertino_icons 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.0.3" 60 | device_info_plus: 61 | dependency: "direct main" 62 | description: 63 | name: device_info_plus 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "3.1.0" 67 | device_info_plus_linux: 68 | dependency: transitive 69 | description: 70 | name: device_info_plus_linux 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.1.0" 74 | device_info_plus_macos: 75 | dependency: transitive 76 | description: 77 | name: device_info_plus_macos 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.2.0" 81 | device_info_plus_platform_interface: 82 | dependency: transitive 83 | description: 84 | name: device_info_plus_platform_interface 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "2.2.0" 88 | device_info_plus_web: 89 | dependency: transitive 90 | description: 91 | name: device_info_plus_web 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "2.1.0" 95 | device_info_plus_windows: 96 | dependency: transitive 97 | description: 98 | name: device_info_plus_windows 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "2.1.0" 102 | fake_async: 103 | dependency: transitive 104 | description: 105 | name: fake_async 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.2.0" 109 | ffi: 110 | dependency: transitive 111 | description: 112 | name: ffi 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.1.2" 116 | file: 117 | dependency: transitive 118 | description: 119 | name: file 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "6.1.2" 123 | fixnum: 124 | dependency: transitive 125 | description: 126 | name: fixnum 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.0.0" 130 | flutter: 131 | dependency: "direct main" 132 | description: flutter 133 | source: sdk 134 | version: "0.0.0" 135 | flutter_reactive_ble: 136 | dependency: transitive 137 | description: 138 | name: flutter_reactive_ble 139 | url: "https://pub.dartlang.org" 140 | source: hosted 141 | version: "5.0.1" 142 | flutter_test: 143 | dependency: "direct dev" 144 | description: flutter 145 | source: sdk 146 | version: "0.0.0" 147 | flutter_web_plugins: 148 | dependency: transitive 149 | description: flutter 150 | source: sdk 151 | version: "0.0.0" 152 | functional_data: 153 | dependency: transitive 154 | description: 155 | name: functional_data 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.0.0" 159 | js: 160 | dependency: transitive 161 | description: 162 | name: js 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.6.3" 166 | matcher: 167 | dependency: transitive 168 | description: 169 | name: matcher 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "0.12.10" 173 | meta: 174 | dependency: transitive 175 | description: 176 | name: meta 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.7.0" 180 | path: 181 | dependency: transitive 182 | description: 183 | name: path 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "1.8.0" 187 | permission_handler: 188 | dependency: "direct main" 189 | description: 190 | name: permission_handler 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "8.2.5" 194 | permission_handler_platform_interface: 195 | dependency: transitive 196 | description: 197 | name: permission_handler_platform_interface 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "3.7.0" 201 | plugin_platform_interface: 202 | dependency: transitive 203 | description: 204 | name: plugin_platform_interface 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.0.2" 208 | protobuf: 209 | dependency: transitive 210 | description: 211 | name: protobuf 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "2.0.0" 215 | reactive_ble_mobile: 216 | dependency: transitive 217 | description: 218 | name: reactive_ble_mobile 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "5.0.1" 222 | reactive_ble_platform_interface: 223 | dependency: transitive 224 | description: 225 | name: reactive_ble_platform_interface 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "5.0.1" 229 | rxdart: 230 | dependency: transitive 231 | description: 232 | name: rxdart 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "0.27.2" 236 | sky_engine: 237 | dependency: transitive 238 | description: flutter 239 | source: sdk 240 | version: "0.0.99" 241 | source_span: 242 | dependency: transitive 243 | description: 244 | name: source_span 245 | url: "https://pub.dartlang.org" 246 | source: hosted 247 | version: "1.8.1" 248 | stack_trace: 249 | dependency: transitive 250 | description: 251 | name: stack_trace 252 | url: "https://pub.dartlang.org" 253 | source: hosted 254 | version: "1.10.0" 255 | stream_channel: 256 | dependency: transitive 257 | description: 258 | name: stream_channel 259 | url: "https://pub.dartlang.org" 260 | source: hosted 261 | version: "2.1.0" 262 | string_scanner: 263 | dependency: transitive 264 | description: 265 | name: string_scanner 266 | url: "https://pub.dartlang.org" 267 | source: hosted 268 | version: "1.1.0" 269 | term_glyph: 270 | dependency: transitive 271 | description: 272 | name: term_glyph 273 | url: "https://pub.dartlang.org" 274 | source: hosted 275 | version: "1.2.0" 276 | test_api: 277 | dependency: transitive 278 | description: 279 | name: test_api 280 | url: "https://pub.dartlang.org" 281 | source: hosted 282 | version: "0.4.2" 283 | typed_data: 284 | dependency: transitive 285 | description: 286 | name: typed_data 287 | url: "https://pub.dartlang.org" 288 | source: hosted 289 | version: "1.3.0" 290 | uuid: 291 | dependency: transitive 292 | description: 293 | name: uuid 294 | url: "https://pub.dartlang.org" 295 | source: hosted 296 | version: "3.0.5" 297 | vector_math: 298 | dependency: transitive 299 | description: 300 | name: vector_math 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "2.1.0" 304 | win32: 305 | dependency: transitive 306 | description: 307 | name: win32 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "2.2.10" 311 | xiaomi_scale: 312 | dependency: "direct main" 313 | description: 314 | path: ".." 315 | relative: true 316 | source: path 317 | version: "2.1.0" 318 | sdks: 319 | dart: ">=2.14.0 <3.0.0" 320 | flutter: ">=2.5.0" 321 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: xiaomi_scale_example 2 | description: Demonstrates how to use the xiaomi_scale plugin. 3 | version: 1.0.0 4 | publish_to: 'none' 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | cupertino_icons: ^1.0.3 11 | device_info_plus: ^3.1.0 12 | flutter: 13 | sdk: flutter 14 | permission_handler: ^8.2.5 15 | xiaomi_scale: 16 | path: ../ 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | flutter: 23 | uses-material-design: true 24 | -------------------------------------------------------------------------------- /lib/src/mi_scale.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; 4 | 5 | import 'model/device/mi_scale_device.dart'; 6 | import 'model/mi_scale_data.dart'; 7 | import 'model/mi_scale_measurement.dart'; 8 | 9 | final Uuid bodyCompositionService = Uuid([0x18, 0x1B]); 10 | 11 | class MiScale { 12 | //Internal singleton instance 13 | static MiScale? _instance; 14 | 15 | /// Obtain the singleton [MiScale] instance 16 | static MiScale get instance => _instance ??= MiScale._internal(); 17 | 18 | final _ble = FlutterReactiveBle(); 19 | final _activeMeasurements = {}; 20 | 21 | MiScale._internal(); 22 | 23 | /// Cancel any active measurement for the given device 24 | /// 25 | /// Read 'active measurement' as a measurement not on the [MiScaleMeasurementStage.MEASURED] stage. 26 | /// NOTE: If a user steps off the scale before the [MiScaleMeasurementStage.STABILIZED] stage is reached, the measurement will remain active. 27 | /// In this case, the measurement will have to be canceled before a new measurement is to be started. 28 | void cancelMeasurement(String deviceId) { 29 | _activeMeasurements.remove(deviceId); 30 | } 31 | 32 | /// Listens for weight measurements 33 | /// 34 | /// Provides a stream of [MiScaleMeasurement] instances. 35 | /// Multiple instances are emitted for the same measurement throughout the progress of the measurement to denote changes. 36 | /// The measurements continue to be taken until the returned stream is cancelled. 37 | Stream takeMeasurements() { 38 | StreamSubscription? dataSubscription; 39 | StreamSubscription? cleanUpSubscription; 40 | late StreamController controller; 41 | controller = StreamController.broadcast( 42 | onListen: () { 43 | // Process scale data into measurements 44 | dataSubscription = readScaleData().listen((scaleData) { 45 | final measurement = MiScaleMeasurement.processData( 46 | _activeMeasurements[scaleData.deviceId], 47 | scaleData, 48 | ); 49 | if (measurement != null && 50 | measurement.stage != MiScaleMeasurementStage.MEASURED) { 51 | _activeMeasurements[scaleData.deviceId] = measurement; 52 | } else { 53 | _activeMeasurements.remove(scaleData.deviceId); 54 | } 55 | if (measurement != null) controller.add(measurement); 56 | }); 57 | }, 58 | onCancel: () { 59 | dataSubscription?.cancel(); 60 | cleanUpSubscription?.cancel(); 61 | _activeMeasurements.clear(); 62 | controller.close(); 63 | }, 64 | ); 65 | return controller.stream.distinct(); 66 | } 67 | 68 | /// Starts a scan for compatible devices 69 | /// 70 | /// Found devices are returned as a [MiScaleDevice] instance. 71 | /// The scan will automatically stop after the set [duration]. 72 | /// To stop the scan prematurely, cancel the returned stream. 73 | Stream discoverDevices( 74 | {Duration duration = const Duration(seconds: 5)}) { 75 | StreamSubscription? scanSubscription; 76 | late StreamController controller; 77 | final foundDeviceIds = []; 78 | controller = StreamController.broadcast( 79 | onListen: () async { 80 | scanSubscription = scanForDevices().listen((device) { 81 | // Determine the device type 82 | final scaleDevice = MiScaleDevice.from(device); 83 | // If no device type found, stop 84 | if (scaleDevice == null) return; 85 | // If we already found it, stop 86 | if (foundDeviceIds.contains(scaleDevice.id)) return; 87 | // Add it to the list of found devices 88 | foundDeviceIds.add(scaleDevice.id); 89 | // Emit data 90 | controller.add(scaleDevice); 91 | }); 92 | await Future.delayed(duration); 93 | await scanSubscription?.cancel(); 94 | if (!controller.isClosed) await controller.close(); 95 | }, 96 | onCancel: () { 97 | scanSubscription?.cancel(); 98 | controller.close(); 99 | foundDeviceIds.clear(); 100 | }, 101 | ); 102 | return controller.stream; 103 | } 104 | 105 | /// Listens for any incoming scale data 106 | /// 107 | /// The returned stream emits a [MiScaleData] for each received advertisement packet. 108 | /// Unless you need access to the parsed advertisement data directly, It is preferable to use [takeMeasurements] instead. 109 | Stream readScaleData() { 110 | StreamSubscription? scanSubscription; 111 | late StreamController controller; 112 | controller = StreamController.broadcast( 113 | onListen: () { 114 | scanSubscription = scanForDevices().listen((device) { 115 | final scaleDevice = MiScaleDevice.from(device); 116 | // Stop if it's not a known scale device 117 | if (scaleDevice == null) return; 118 | // Parse scale data 119 | final data = 120 | scaleDevice.parseScaleData(device.serviceData.values.first); 121 | if (data == null) return; 122 | // Emit data 123 | controller.add(data); 124 | }); 125 | }, 126 | onCancel: () { 127 | scanSubscription?.cancel(); 128 | controller.close(); 129 | }, 130 | ); 131 | return controller.stream; 132 | } 133 | 134 | Stream scanForDevices() { 135 | return _ble.scanForDevices( 136 | withServices: [bodyCompositionService], 137 | scanMode: ScanMode.lowLatency, 138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/src/model/device/mi_scale_device.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; 4 | 5 | import '../mi_scale_data.dart'; 6 | import 'mi_scale_device_v2.dart'; 7 | 8 | abstract class MiScaleDevice { 9 | final DiscoveredDevice _device; 10 | 11 | /// The id of the discovered device 12 | String get id => _device.id; 13 | 14 | /// The name of the discovered device 15 | String get name => _device.name; 16 | 17 | /// The signal strength of the device when it was first discovered 18 | int get rssi => _device.rssi; 19 | 20 | MiScaleDevice(this._device); 21 | 22 | /// Parse the raw advertisement data to obtain a [MiScaleData] instance 23 | MiScaleData? parseScaleData(Uint8List data); 24 | 25 | /// Constructs an instance of an extending [MiScaleDevice] class. 26 | /// 27 | /// Returns `null` if [device] has no matching class for its device type. 28 | static MiScaleDevice? from(DiscoveredDevice device) { 29 | if (MiScaleDeviceV2.matchesDeviceType(device)) { 30 | return MiScaleDeviceV2(device); 31 | } 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/model/device/mi_scale_device_v2.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; 4 | 5 | import '../mi_scale_data.dart'; 6 | import '../mi_scale_unit.dart'; 7 | import 'mi_scale_device.dart'; 8 | 9 | class MiScaleDeviceV2 extends MiScaleDevice { 10 | MiScaleDeviceV2(DiscoveredDevice device) 11 | : assert(matchesDeviceType(device)), 12 | super(device); 13 | 14 | @override 15 | MiScaleData? parseScaleData(Uint8List data) { 16 | return MiScaleDeviceV2._parseScaleData(id, data); 17 | } 18 | 19 | /// Determine whether this class matches the device type of the given device 20 | static bool matchesDeviceType(DiscoveredDevice device) { 21 | return device.name == 'MIBFS' && 22 | device.serviceData.length == 1 && 23 | device.serviceData.values.first.length == 13; 24 | } 25 | 26 | static MiScaleData? _parseScaleData(String deviceId, Uint8List data) { 27 | if (data.length != 13) return null; 28 | // Prepare data 29 | final byteData = data.buffer.asByteData(); 30 | // Parse flags 31 | final measurementComplete = data[1] & (0x01 << 1) != 0; 32 | final weightStabilized = data[1] & (0x01 << 5) != 0; 33 | final weightRemoved = data[1] & (0x01 << 7) != 0; 34 | 35 | int? impedance; 36 | if (measurementComplete) { 37 | impedance = ((data[10] & 0xFF) << 8) | (data[9] & 0xFF); 38 | } 39 | 40 | final unit = (data[0] & 0x01 != 0) ? MiScaleUnit.LBS : MiScaleUnit.KG; 41 | // Parse date 42 | final year = byteData.getUint16(2, Endian.little); 43 | final month = byteData.getUint8(4); 44 | final day = byteData.getUint8(5); 45 | final hour = byteData.getUint8(6); 46 | final minute = byteData.getUint8(7); 47 | final seconds = byteData.getUint8(8); 48 | final measurementTime = 49 | DateTime.utc(year, month, day, hour, minute, seconds); 50 | // Parse weight 51 | var weight = byteData.getUint16(11, Endian.little).toDouble(); 52 | if (unit == MiScaleUnit.LBS) { 53 | weight /= 100; 54 | } else if (unit == MiScaleUnit.KG) { 55 | weight /= 200; 56 | } 57 | // Return new scale data 58 | return MiScaleData( 59 | deviceId: deviceId, 60 | measurementComplete: measurementComplete, 61 | weightStabilized: weightStabilized, 62 | weightRemoved: weightRemoved, 63 | unit: unit, 64 | dateTime: measurementTime, 65 | weight: weight, 66 | impedance: impedance, 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/model/gender.dart: -------------------------------------------------------------------------------- 1 | enum MiScaleGender { 2 | MALE, 3 | FEMALE, 4 | } 5 | -------------------------------------------------------------------------------- /lib/src/model/mi_scale_data.dart: -------------------------------------------------------------------------------- 1 | import 'gender.dart'; 2 | import 'mi_scale_extra_data.dart'; 3 | import 'mi_scale_unit.dart'; 4 | 5 | class MiScaleData { 6 | /// ID of the device this data was parsed from. 7 | final String deviceId; 8 | final double weight; 9 | 10 | /// Value is `true` if the weight has stabilized. 11 | final bool weightStabilized; 12 | 13 | /// Value is `true` if the device is done measuring. 14 | /// This value is usually given after other measurements (such as body fat) have been completed as well. 15 | final bool measurementComplete; 16 | 17 | /// Value is `true` if there is no weight detected. 18 | final bool weightRemoved; 19 | 20 | /// The currently configured weight unit on the device. 21 | final MiScaleUnit unit; 22 | 23 | /// The timestamp given by the device. 24 | /// 25 | /// Note that this value must only be considered valid if [weightRemoved] is `false` and [weightStabilized] is `true`. 26 | /// This can also be checked by calling [dateTimeValid] 27 | final DateTime dateTime; 28 | 29 | final int? impedance; 30 | 31 | MiScaleData({ 32 | required this.deviceId, 33 | required this.weight, 34 | required this.weightStabilized, 35 | required this.measurementComplete, 36 | required this.weightRemoved, 37 | required this.unit, 38 | required this.dateTime, 39 | required this.impedance, 40 | }); 41 | 42 | /// The extra data that will be used to measure bmi, bone mass, fat percentage,... based on 43 | MiScaleBodyData? getBodyData(MiScaleGender gender, int age, double height) { 44 | final impedance = this.impedance; 45 | if (impedance == null) return null; 46 | return MiScaleBodyData( 47 | gender: gender, 48 | age: age, 49 | height: height, 50 | weight: weight, 51 | impedance: impedance, 52 | ); 53 | } 54 | 55 | bool get dateTimeValid => weightStabilized && !weightRemoved; 56 | 57 | @override 58 | bool operator ==(Object other) => 59 | identical(this, other) || 60 | other is MiScaleData && 61 | runtimeType == other.runtimeType && 62 | weight == other.weight && 63 | weightStabilized == other.weightStabilized && 64 | measurementComplete == other.measurementComplete && 65 | weightRemoved == other.weightRemoved && 66 | unit == other.unit && 67 | dateTime == other.dateTime; 68 | 69 | @override 70 | int get hashCode => 71 | weight.hashCode ^ 72 | weightStabilized.hashCode ^ 73 | measurementComplete.hashCode ^ 74 | weightRemoved.hashCode ^ 75 | unit.hashCode ^ 76 | dateTime.hashCode; 77 | 78 | @override 79 | String toString() { 80 | return 'MiScaleData{deviceId: $deviceId, weight: $weight, weightStabilized: $weightStabilized, measurementComplete: $measurementComplete, weightRemoved: $weightRemoved, unit: $unit, dateTime: $dateTime, dateTimeValid: $dateTimeValid}'; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/model/mi_scale_extra_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:xiaomi_scale/src/model/gender.dart'; 2 | 3 | class MiScaleBodyData { 4 | final MiScaleGender gender; 5 | final int age; 6 | final double height; 7 | final double weight; 8 | final int impedance; 9 | 10 | const MiScaleBodyData({ 11 | required this.gender, 12 | required this.age, 13 | required this.height, 14 | required this.weight, 15 | required this.impedance, 16 | }); 17 | 18 | double get lbmCoefficient { 19 | var lbm = (height * 9.058 / 100.0) * (height / 100.0); 20 | lbm += weight * 0.32 + 12.226; 21 | lbm -= impedance * 0.0068; 22 | lbm -= age * 0.0542; 23 | 24 | return lbm; 25 | } 26 | 27 | double get bmi { 28 | return weight / (((height * height) / 100.0) / 100.0); 29 | } 30 | 31 | double get muscleMass { 32 | var muscleMass = weight - ((bodyFat * 0.01) * weight) - boneMass; 33 | 34 | if (gender == MiScaleGender.FEMALE && muscleMass >= 84.0) { 35 | muscleMass = 120.0; 36 | } else if (gender == MiScaleGender.MALE && muscleMass >= 93.5) { 37 | muscleMass = 120.0; 38 | } 39 | 40 | return muscleMass; 41 | } 42 | 43 | double get water { 44 | double coeff; 45 | final water = (100.0 - bodyFat) * 0.7; 46 | 47 | if (water < 50) { 48 | coeff = 1.02; 49 | } else { 50 | coeff = 0.98; 51 | } 52 | 53 | return coeff * water; 54 | } 55 | 56 | double get boneMass { 57 | double boneMass; 58 | double base; 59 | 60 | if (gender == MiScaleGender.FEMALE) { 61 | base = 0.245691014; 62 | } else { 63 | base = 0.18016894; 64 | } 65 | 66 | boneMass = (base - (lbmCoefficient * 0.05158)) * -1.0; 67 | 68 | if (boneMass > 2.2) { 69 | boneMass += 0.1; 70 | } else { 71 | boneMass -= 0.1; 72 | } 73 | 74 | if (gender == MiScaleGender.FEMALE && boneMass > 5.1) { 75 | boneMass = 8.0; 76 | } else if (gender == MiScaleGender.MALE && boneMass > 5.2) { 77 | boneMass = 8.0; 78 | } 79 | 80 | return boneMass; 81 | } 82 | 83 | double get visceralFat { 84 | var visceralFat = 0.0; 85 | if (gender == MiScaleGender.FEMALE) { 86 | if (weight > (13.0 - (height * 0.5)) * -1.0) { 87 | final subsubcalc = 88 | ((height * 1.45) + (height * 0.1158) * height) - 120.0; 89 | final subcalc = weight * 500.0 / subsubcalc; 90 | visceralFat = (subcalc - 6.0) + (age * 0.07); 91 | } else { 92 | final subcalc = 0.691 + (height * -0.0024) + (height * -0.0024); 93 | visceralFat = (((height * 0.027) - (subcalc * weight)) * -1.0) + 94 | (age * 0.07) - 95 | age; 96 | } 97 | } else if (gender == MiScaleGender.MALE) { 98 | if (height < weight * 1.6) { 99 | final subcalc = ((height * 0.4) - (height * (height * 0.0826))) * -1.0; 100 | visceralFat = 101 | ((weight * 305.0) / (subcalc + 48.0)) - 2.9 + (age * 0.15); 102 | } else { 103 | final subcalc = 0.765 + height * -0.0015; 104 | visceralFat = (((height * 0.143) - (weight * subcalc)) * -1.0) + 105 | (age * 0.15) - 106 | 5.0; 107 | } 108 | } 109 | 110 | return visceralFat; 111 | } 112 | 113 | double get bodyFat { 114 | var bodyFat = 0.0; 115 | var lbmSub = 0.8; 116 | 117 | if (gender == MiScaleGender.FEMALE && age <= 49) { 118 | lbmSub = 9.25; 119 | } else if (gender == MiScaleGender.MALE && age > 49) { 120 | lbmSub = 7.25; 121 | } 122 | 123 | final lbmCoeff = lbmCoefficient; 124 | var coeff = 1.0; 125 | 126 | if (gender == MiScaleGender.MALE && weight < 61.0) { 127 | coeff = 0.98; 128 | } else if (gender == MiScaleGender.FEMALE && weight > 60.0) { 129 | coeff = 0.96; 130 | 131 | if (height > 160.0) { 132 | coeff *= 1.03; 133 | } 134 | } else if (gender == MiScaleGender.FEMALE && weight < 50.0) { 135 | coeff = 1.02; 136 | 137 | if (height > 160.0) { 138 | coeff *= 1.03; 139 | } 140 | } 141 | 142 | bodyFat = (1.0 - (((lbmCoeff - lbmSub) * coeff) / weight)) * 100.0; 143 | 144 | if (bodyFat > 63.0) { 145 | bodyFat = 75.0; 146 | } 147 | 148 | return bodyFat; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/src/model/mi_scale_measurement.dart: -------------------------------------------------------------------------------- 1 | import 'package:uuid/uuid.dart'; 2 | import 'package:xiaomi_scale/src/model/gender.dart'; 3 | import 'package:xiaomi_scale/src/model/mi_scale_extra_data.dart'; 4 | 5 | import 'mi_scale_data.dart'; 6 | import 'mi_scale_unit.dart'; 7 | 8 | const Uuid _uuid = Uuid(); 9 | 10 | enum MiScaleMeasurementStage { 11 | /// Person has stepped off the scale before the scale has stabilized 12 | WEIGHT_REMOVED, 13 | 14 | /// Person is on the scale, but the measurement is not stable yet 15 | MEASURING, 16 | 17 | /// Person is on the scale and the scale has stabilized. Scale is still be taking other measurements (Body fat, etc) 18 | STABILIZED, 19 | 20 | /// Measurement has fully completed. 21 | MEASURED, 22 | } 23 | 24 | class MiScaleMeasurement { 25 | /// The unique id of the measurement in uuid v4 format 26 | final String id; 27 | 28 | /// The id given to the device used for this measurement 29 | final String? deviceId; 30 | 31 | /// The weight associated with this measurement 32 | /// 33 | /// The weight does not change anymore after [stage] has turned to [MiScaleMeasurementStage.STABILIZED] 34 | final double weight; 35 | 36 | /// The impedance associated with this measurement 37 | /// 38 | /// The impedance does not change anymore after [stage] has turned to [MiScaleMeasurementStage.MEASURED] 39 | final int? impedance; 40 | 41 | /// The current stage this measurement is at. 42 | /// 43 | /// Starts out on [MiScaleMeasurementStage.MEASURING] 44 | /// When a person steps off the scale before reaching [MiScaleMeasurementStage.STABILIZED], it will move on to [MiScaleMeasurementStage.WEIGHT_REMOVED] instead. 45 | /// When a person steps off the scale after reaching [MiScaleMeasurementStage.STABILIZED], but before reaching [MiScaleMeasurementStage.MEASURED], the measurement will automatically move on to [MiScaleMeasurementStage.MEASURED]. 46 | final MiScaleMeasurementStage stage; 47 | 48 | /// The weight unit for the current measurement, based on the device configuration 49 | final MiScaleUnit unit; 50 | 51 | /// The timestamp associated with this measurement. 52 | /// 53 | /// By default, it is based on the current host time, not the current device (scale) time. 54 | final DateTime dateTime; 55 | 56 | MiScaleMeasurement({ 57 | required this.weight, 58 | required this.stage, 59 | required this.unit, 60 | this.deviceId, 61 | this.impedance, 62 | String? id, 63 | DateTime? dateTime, 64 | }) : dateTime = dateTime ?? DateTime.now(), 65 | id = id ?? _uuid.v4(); 66 | 67 | bool get isActive => stage != MiScaleMeasurementStage.MEASURED; 68 | 69 | static MiScaleMeasurement? processData( 70 | MiScaleMeasurement? previousMeasurement, MiScaleData scaleData) { 71 | // Start new measurement if new weight is detected 72 | if (previousMeasurement == null && 73 | !scaleData.weightRemoved && 74 | !scaleData.measurementComplete) { 75 | return MiScaleMeasurement( 76 | weight: scaleData.weight, 77 | stage: MiScaleMeasurementStage.MEASURING, 78 | unit: scaleData.unit, 79 | ); 80 | } 81 | 82 | // From this point we assume a measurement is already taking place. 83 | if (previousMeasurement == null) return null; 84 | 85 | // Update measurement if we're still measuring 86 | if (previousMeasurement.stage == MiScaleMeasurementStage.MEASURING && 87 | !scaleData.weightStabilized && 88 | !scaleData.measurementComplete && 89 | !scaleData.weightRemoved) { 90 | return MiScaleMeasurement( 91 | id: previousMeasurement.id, 92 | weight: scaleData.weight, 93 | stage: MiScaleMeasurementStage.MEASURING, 94 | unit: scaleData.unit, 95 | ); 96 | } 97 | 98 | // Handle person stepping off mid measurement 99 | if (previousMeasurement.stage == MiScaleMeasurementStage.MEASURING && 100 | scaleData.weightRemoved && 101 | !scaleData.measurementComplete) { 102 | return MiScaleMeasurement( 103 | id: previousMeasurement.id, 104 | weight: 0, 105 | stage: MiScaleMeasurementStage.WEIGHT_REMOVED, 106 | unit: scaleData.unit, 107 | ); 108 | } 109 | 110 | // Handle person stepping back on mid measurement 111 | if (previousMeasurement.stage == MiScaleMeasurementStage.WEIGHT_REMOVED && 112 | !scaleData.weightRemoved && 113 | !scaleData.measurementComplete) { 114 | return MiScaleMeasurement( 115 | id: previousMeasurement.id, 116 | weight: scaleData.weight, 117 | stage: scaleData.weightStabilized 118 | ? MiScaleMeasurementStage.STABILIZED 119 | : MiScaleMeasurementStage.MEASURING, 120 | unit: scaleData.unit, 121 | ); 122 | } 123 | 124 | // Lock measurement if we've just stabilized 125 | if (previousMeasurement.stage == MiScaleMeasurementStage.MEASURING && 126 | !scaleData.weightRemoved && 127 | scaleData.weightStabilized) { 128 | return MiScaleMeasurement( 129 | id: previousMeasurement.id, 130 | weight: scaleData.weight, 131 | stage: MiScaleMeasurementStage.STABILIZED, 132 | unit: scaleData.unit, 133 | impedance: null, 134 | ); 135 | } 136 | 137 | // Handle person stepping off after stabilizing, before done measuring 138 | if (previousMeasurement.stage == MiScaleMeasurementStage.STABILIZED && 139 | !scaleData.measurementComplete && 140 | scaleData.weightRemoved) { 141 | return MiScaleMeasurement( 142 | id: previousMeasurement.id, 143 | weight: previousMeasurement.weight, 144 | stage: MiScaleMeasurementStage.MEASURED, 145 | unit: scaleData.unit, 146 | impedance: null, 147 | ); 148 | } 149 | 150 | // Finalize measurement if we are done measuring 151 | if (previousMeasurement.stage == MiScaleMeasurementStage.STABILIZED && 152 | scaleData.measurementComplete) { 153 | final impedance = scaleData.impedance; 154 | return MiScaleMeasurement( 155 | id: previousMeasurement.id, 156 | weight: previousMeasurement.weight, 157 | stage: MiScaleMeasurementStage.MEASURED, 158 | unit: scaleData.unit, 159 | impedance: impedance, 160 | ); 161 | } 162 | 163 | // Otherwise just return the previous measurement 164 | return previousMeasurement; 165 | } 166 | 167 | /// The extra data that will be used to measure bmi, bone mass, fat percentage,... based on 168 | MiScaleBodyData? getBodyData(MiScaleGender gender, int age, double height) { 169 | final impedance = this.impedance; 170 | if (impedance == null) return null; 171 | return MiScaleBodyData( 172 | gender: gender, 173 | age: age, 174 | height: height, 175 | weight: weight, 176 | impedance: impedance, 177 | ); 178 | } 179 | 180 | @override 181 | bool operator ==(Object other) => 182 | identical(this, other) || 183 | other is MiScaleMeasurement && 184 | runtimeType == other.runtimeType && 185 | id == other.id && 186 | deviceId == other.deviceId && 187 | weight == other.weight && 188 | stage == other.stage && 189 | unit == other.unit && 190 | dateTime == other.dateTime; 191 | 192 | @override 193 | int get hashCode => 194 | id.hashCode ^ 195 | deviceId.hashCode ^ 196 | weight.hashCode ^ 197 | stage.hashCode ^ 198 | unit.hashCode ^ 199 | dateTime.hashCode; 200 | } 201 | -------------------------------------------------------------------------------- /lib/src/model/mi_scale_unit.dart: -------------------------------------------------------------------------------- 1 | enum MiScaleUnit { 2 | KG, 3 | LBS, 4 | } 5 | -------------------------------------------------------------------------------- /lib/xiaomi_scale.dart: -------------------------------------------------------------------------------- 1 | library xiaomi_scale; 2 | 3 | export 'src/mi_scale.dart'; 4 | export 'src/model/device/mi_scale_device.dart'; 5 | export 'src/model/device/mi_scale_device_v2.dart'; 6 | export 'src/model/gender.dart'; 7 | export 'src/model/mi_scale_data.dart'; 8 | export 'src/model/mi_scale_extra_data.dart'; 9 | export 'src/model/mi_scale_measurement.dart'; 10 | export 'src/model/mi_scale_unit.dart'; 11 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | crypto: 47 | dependency: transitive 48 | description: 49 | name: crypto 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "3.0.0" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | fixnum: 61 | dependency: transitive 62 | description: 63 | name: fixnum 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.0.0" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_reactive_ble: 73 | dependency: "direct main" 74 | description: 75 | name: flutter_reactive_ble 76 | url: "https://pub.dartlang.org" 77 | source: hosted 78 | version: "5.0.1" 79 | flutter_test: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | functional_data: 85 | dependency: transitive 86 | description: 87 | name: functional_data 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.0.0" 91 | matcher: 92 | dependency: transitive 93 | description: 94 | name: matcher 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.12.10" 98 | meta: 99 | dependency: transitive 100 | description: 101 | name: meta 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "1.7.0" 105 | path: 106 | dependency: transitive 107 | description: 108 | name: path 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.8.0" 112 | plugin_platform_interface: 113 | dependency: transitive 114 | description: 115 | name: plugin_platform_interface 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "2.0.2" 119 | protobuf: 120 | dependency: transitive 121 | description: 122 | name: protobuf 123 | url: "https://pub.dartlang.org" 124 | source: hosted 125 | version: "2.0.0" 126 | reactive_ble_mobile: 127 | dependency: transitive 128 | description: 129 | name: reactive_ble_mobile 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "5.0.1" 133 | reactive_ble_platform_interface: 134 | dependency: transitive 135 | description: 136 | name: reactive_ble_platform_interface 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "5.0.1" 140 | rxdart: 141 | dependency: "direct main" 142 | description: 143 | name: rxdart 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.27.2" 147 | sky_engine: 148 | dependency: transitive 149 | description: flutter 150 | source: sdk 151 | version: "0.0.99" 152 | source_span: 153 | dependency: transitive 154 | description: 155 | name: source_span 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.8.1" 159 | stack_trace: 160 | dependency: transitive 161 | description: 162 | name: stack_trace 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "1.10.0" 166 | stream_channel: 167 | dependency: transitive 168 | description: 169 | name: stream_channel 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.1.0" 173 | string_scanner: 174 | dependency: transitive 175 | description: 176 | name: string_scanner 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.1.0" 180 | term_glyph: 181 | dependency: transitive 182 | description: 183 | name: term_glyph 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "1.2.0" 187 | test_api: 188 | dependency: transitive 189 | description: 190 | name: test_api 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "0.4.2" 194 | typed_data: 195 | dependency: transitive 196 | description: 197 | name: typed_data 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.3.0" 201 | uuid: 202 | dependency: "direct main" 203 | description: 204 | name: uuid 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "3.0.5" 208 | vector_math: 209 | dependency: transitive 210 | description: 211 | name: vector_math 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "2.1.0" 215 | sdks: 216 | dart: ">=2.12.0 <3.0.0" 217 | flutter: ">=2.0.0" 218 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: xiaomi_scale 2 | description: A Flutter plugin for taking measurements from Bluetooth Xiaomi weight scales 3 | version: 2.2.1 4 | repository: https://github.com/BeMacized/xiaomi_scale 5 | issue_tracker: https://github.com/BeMacized/xiaomi_scale/issues 6 | homepage: https://bemacized.net/ 7 | 8 | environment: 9 | sdk: ">=2.12.0 <3.0.0" 10 | flutter: ">=2.0.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | flutter_reactive_ble: ^5.0.1 16 | rxdart: ^0.27.2 17 | uuid: ^3.0.5 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | dartdoc: 24 | include: ['xiaomi_scale'] 25 | 26 | # For information on the generic Dart part of this file, see the 27 | # following page: https://dart.dev/tools/pub/pubspec 28 | 29 | # The following section is specific to Flutter. 30 | flutter: 31 | -------------------------------------------------------------------------------- /readme_res/scale_v2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/readme_res/scale_v2.jpg -------------------------------------------------------------------------------- /readme_res/screenshots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeMacized/xiaomi_scale/8cccefdcbb5592d978260eccab8ac0a3d6c7daa9/readme_res/screenshots.png --------------------------------------------------------------------------------