├── .gitignore ├── .gradle ├── 5.2.1 │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ └── fileHashes.lock │ └── gc.properties ├── buildOutputCleanup │ ├── buildOutputCleanup.lock │ └── cache.properties └── vcs-1 │ └── gc.properties ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── com │ └── pravera │ └── geofence_service │ └── GeofenceServicePlugin.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── pravera │ │ │ │ │ └── geofence_service_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── 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 ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── geofence_service.iml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── GeofenceServicePlugin.h │ ├── GeofenceServicePlugin.m │ └── SwiftGeofenceServicePlugin.swift └── geofence_service.podspec ├── lib ├── errors │ └── error_codes.dart ├── geofence_service.dart └── models │ ├── geofence.dart │ ├── geofence_radius.dart │ ├── geofence_radius_sort_type.dart │ ├── geofence_service_options.dart │ └── geofence_status.dart ├── pubspec.yaml └── test └── geofence_service_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .idea/ 4 | 5 | .packages 6 | .pub/ 7 | 8 | build/ 9 | pubspec.lock 10 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/.gradle/5.2.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/5.2.1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/.gradle/5.2.1/gc.properties -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Feb 01 14:17:08 KST 2021 2 | gradle.version=5.2.1 3 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /.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: 8874f21e79d7ec66d0457c7ab338348e31b17f1d 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 6.0.0+1 2 | 3 | * [**DOCS**] Deprecated 4 | 5 | ## 6.0.0 6 | 7 | * [**FEAT**] Support AGP 8 8 | * [**REMOVE**] Remove `flutter_foreground_task` dependency. If you want to start a service in the background, use the [flutter_foreground_task](https://pub.dev/packages/flutter_foreground_task) plugin. 9 | 10 | ## 5.0.0 11 | 12 | * [**CHORE**] Update dependency constraints to `sdk: '>=2.18.0 <4.0.0'` `flutter: '>=3.3.0'` 13 | * [**CHORE**] Update dependency to the latest version 14 | * [**CHORE**] Bump Android compileSdkVersion to 33 15 | 16 | ## 4.0.0 17 | 18 | * [**CHORE**] Upgrade minimum Flutter version to 3.0.0 19 | * [**CHORE**] Upgrade dependencies 20 | 21 | ## 3.5.0 22 | 23 | * Upgrade dependencies. 24 | * [[#4](https://github.com/Dev-hwang/flutter_location/issues/4)] Fix onRequestPermissionsResult implement issue. 25 | 26 | ## 3.4.8 27 | 28 | * Upgrade dependencies. 29 | * Downgrade Android minSdkVersion to 21. 30 | 31 | ## 3.4.7 32 | 33 | * Upgrade dependencies. 34 | * [[#42](https://github.com/Dev-hwang/flutter_foreground_task/issues/42)] Only minimize app on pop when there is no route to pop. 35 | 36 | ## 3.4.6 37 | 38 | * [**iOS**] Fixed an issue where notifications not related to the service were removed. 39 | * [**iOS**] Improved compatibility with other plugins that use notifications. 40 | - Additional settings are required, so please check the Readme-Getting started. 41 | 42 | ## 3.4.5 43 | 44 | * [[#16](https://github.com/Dev-hwang/geofence_service/issues/16)] Add process exit code to prevent memory leak. 45 | 46 | ## 3.4.4 47 | 48 | * Upgrade dependencies. 49 | * [**Bug**] Fixed an issue where lockMode(wakeLock, wifiLock) was not properly released when the service was forcibly shutdown. 50 | * [**Bug**] Fixed an issue where foreground service notification UX was delayed on Android version 12. 51 | 52 | ## 3.4.3 53 | 54 | * Upgrade dependencies. 55 | * Bump Android minSdkVersion to 23. 56 | * Bump Android compileSdkVersion to 31. 57 | 58 | ## 3.4.2 59 | 60 | * Upgrade flutter_foreground_task: ^3.2.2 61 | * [**Bug**] Fixed an issue where RemoteServiceException occurred intermittently. 62 | 63 | ## 3.4.1 64 | 65 | * Upgrade flutter_foreground_task: ^3.2.0 66 | 67 | ## 3.4.0 68 | 69 | * Upgrade flutter_foreground_task: ^3.0.0 70 | * Changed parameter name of `WillStartForegroundTask` widget. 71 | 72 | ## 3.3.2 73 | 74 | * Fix errorCodes parsing function not working properly. 75 | 76 | ## 3.3.1 77 | 78 | * Upgrade fl_location: ^1.0.1 79 | 80 | ## 3.3.0 81 | 82 | * Upgrade flutter_foreground_task: ^2.1.0 83 | * [**BREAKING**] Replace plugin from `location` to `fl_location`. 84 | * [**BREAKING**] Replace data model from `LocationData` to `Location`. 85 | * Rename the listener function. 86 | ```text 87 | addLocationDataChangeListener -> addLocationChangeListener 88 | addLocationServiceStatusChangeListener -> addLocationServicesStatusChangeListener 89 | removeLocationDataChangeListener -> removeLocationChangeListener 90 | removeLocationServiceStatusChangeListener -> removeLocationServicesStatusChangeListener 91 | ``` 92 | * Rename the error code. 93 | ```text 94 | LOCATION_SERVICE_DISABLED -> LOCATION_SERVICES_DISABLED 95 | ``` 96 | * Add `clearAllListeners` function. 97 | * Add `foregroundServiceType` to android service tag. 98 | * Fixed DWELL status change being delayed due to statusChangeDelayMs. 99 | 100 | ## 3.2.1 101 | 102 | * Upgrade flutter_foreground_task: ^2.0.4 103 | 104 | ## 3.2.0 105 | 106 | * [**BREAKING**] Replace plugin from `geolocator` to `location`. 107 | * [**BREAKING**] Replace data model from `Position` to `LocationData`. 108 | * Rename the listener function. 109 | ```text 110 | addPositionChangeListener -> addLocationDataChangeListener 111 | removePositionChangeListener -> removeLocationDataChangeListener 112 | ``` 113 | * Fix location permission request not working properly. 114 | * Fix an issue that the location stream is not closed even when the service is stopped. 115 | 116 | ## 3.1.4 117 | 118 | * Move component declaration inside the plugin. Check the readme for more details. 119 | * Upgrade flutter_foreground_task: ^2.0.3 120 | * Upgrade flutter_activity_recognition: ^1.1.2 121 | ```markdown 122 | * Upgrade to `Activity Recognition Transition API`. 123 | * Remove `ON_FOOT` activity type. 124 | * Remove `TILTING` activity type. 125 | * Fix `requestPermission` not working properly. 126 | ``` 127 | 128 | ## 3.1.3 129 | 130 | * Upgrade flutter_foreground_task: ^2.0.1 131 | 132 | ## 3.1.2 133 | 134 | * Upgrade geolocator: ^7.2.0+1 135 | 136 | ## 3.1.1 137 | 138 | * Upgrade geolocator: ^7.1.1 139 | * Upgrade flutter_foreground_task: ^2.0.0 140 | 141 | ## 3.1.0 142 | 143 | * Upgrade geolocator: ^7.1.0 144 | * Upgrade flutter_activity_recognition: ^1.0.2 145 | * Add `addPositionChangeListener` function. 146 | * Add `removePositionChangeListener` function. 147 | * Add `addLocationServiceStatusChangeListener` function. 148 | * Add `removeLocationServiceStatusChangeListener` function. 149 | * Add `printDevLog` option. 150 | * Rename the listener function. 151 | ```text 152 | addGeofenceStatusChangedListener -> addGeofenceStatusChangeListener 153 | addActivityChangedListener -> addActivityChangeListener 154 | removeGeofenceStatusChangedListener -> removeGeofenceStatusChangeListener 155 | removeActivityChangedListener -> removeActivityChangeListener 156 | ``` 157 | * Change the model's `toMap` function name to `toJson`. 158 | * Update example 159 | * Update README.md 160 | 161 | ## 3.0.4 162 | 163 | * Upgrade flutter_foreground_task: ^1.0.8 164 | 165 | ## 3.0.3 166 | 167 | * Add `GeofenceStatus.DWELL` that occurs when loitering within a geofence radius. 168 | * Add `loiteringDelayMs` options. 169 | * Add `statusChangeDelayMs` options. 170 | 171 | ## 3.0.0 172 | 173 | * [**BREAKING**] Remove the activity_recognition package inside the plugin, and add the `flutter_activity_recognition: ^1.0.0` plugin. 174 | * [**BREAKING**] Remove the foreground_service package inside the plugin, and add the `flutter_foreground_task: ^1.0.7` plugin. 175 | * Updates Comment and Documentation. 176 | * Android SDK target upgrade. 177 | 178 | ## 2.1.4 179 | 180 | * Upgrade geolocator: ^7.0.3 181 | 182 | ## 2.1.3 183 | 184 | * Upgrade geolocator: ^7.0.2 185 | 186 | ## 2.1.2 187 | 188 | * Add future-async to `GeofenceStatusChangedCallback`. 189 | * Add `geofenceRadiusSortType` options. 190 | 191 | ## 2.1.0 192 | 193 | * Apply singleton pattern. Now access the `GeofenceService` through the `instance` field. Use the `setup` function to set options. 194 | * Remove `setOnGeofenceStatusChanged` function. 195 | * Remove `setOnActivityChanged` function. 196 | * Remove `setOnStreamError` function. 197 | * Add `addGeofenceStatusChangedListener` function. 198 | * Add `addActivityChangedListener` function. 199 | * Add `addStreamErrorListener` function. 200 | * Add `removeGeofenceStatusChangedListener` function. 201 | * Add `removeActivityChangedListener` function. 202 | * Add `removeStreamErrorListener` function. 203 | * Example updates. 204 | * README updates. 205 | 206 | ## 2.0.5 207 | 208 | * Prevent RemoteServiceException. 209 | 210 | ## 2.0.4 211 | 212 | * Fix foreground service duplicate call issues. 213 | * Fix foreground service start and stop timing issues. 214 | * Change the `serviceId` value of the foreground service. [1 >> 1000] 215 | 216 | ## 2.0.1 217 | 218 | * Add `useActivityRecognition` option to selectively use the activity recognition API. 219 | * Example updates. 220 | * README updates. 221 | 222 | ## 2.0.0 223 | 224 | * Migrate null safety. 225 | 226 | ## 1.0.4 227 | 228 | * Upgrade geolocator: ^7.0.1 229 | 230 | ## 1.0.3 231 | 232 | * Modify package name. 233 | * Add method to get activity with unknown type. 234 | * Add null check for geofenceList of start function. 235 | 236 | ## 1.0.1 237 | 238 | * README updates. 239 | * Add data field for inserting custom data. 240 | 241 | ## 1.0.0 242 | 243 | * Initial release. 244 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dev-hwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This plugin has been deprecated. You can use [geofencing_api](https://pub.dev/packages/geofencing_api) instead. 2 | 3 | This plugin is a geofence service with activity recognition API. It does not use the Geofence API implemented on the platform. Therefore, battery efficiency cannot be guaranteed. Instead, this plugin can provide more accurate and realtime geo-fencing by navigating your location while your app is alive. 4 | 5 | [![pub package](https://img.shields.io/pub/v/geofence_service.svg)](https://pub.dev/packages/geofence_service) 6 | 7 | ## Features 8 | 9 | * `Geofence` can have multiple radius. 10 | * `Geofence` can see what activity took place when the device entered the radius. 11 | * `GeofenceService` can perform geo-fencing in real time and catch errors during operation. 12 | 13 | **WAIT**: This plugin performs geo-fencing based on a circular geofence. If you want to create a polygon geofence, this [plugin](https://pub.dev/packages/poly_geofence_service) is recommended. 14 | 15 | ## Getting started 16 | 17 | To use this plugin, add `geofence_service` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). For example: 18 | 19 | ```yaml 20 | dependencies: 21 | geofence_service: ^6.0.0 22 | ``` 23 | 24 | After adding the `geofence_service` plugin to the flutter project, we need to specify the platform-specific permissions and services to use for this plugin to work properly. 25 | 26 | ### :baby_chick: Android 27 | 28 | Since geo-fencing operates based on location, we need to add the following permission to the `AndroidManifest.xml` file. Open the `AndroidManifest.xml` file and specify it between the `` and `` tags. 29 | 30 | ``` 31 | 32 | 33 | ``` 34 | 35 | The biggest feature of this plugin is that it can know user activity while geo-fencing. Please specify the permission usage in `` tag. 36 | 37 | ``` 38 | 39 | 40 | 41 | ``` 42 | 43 | ### :baby_chick: iOS 44 | 45 | Like Android platform, geo-fencing is based on location, we need to add the following description. Open the `ios/Runner/Info.plist` file and specify it inside the `` tag. 46 | 47 | ``` 48 | NSLocationWhenInUseUsageDescription 49 | Used to provide geofence service. 50 | ``` 51 | 52 | To detect changes in user activity, add the following description. 53 | 54 | ``` 55 | NSMotionUsageDescription 56 | Used to recognize user activity information. 57 | ``` 58 | 59 | ## How to use 60 | 61 | 1. Create a `GeofenceService` instance and set options. `GeofenceService.instance.setup()` provides the following options: 62 | * `interval`: The time interval in milliseconds to check the geofence status. The default is `5000`. 63 | * `accuracy`: Geo-fencing error range in meters. The default is `100`. 64 | * `loiteringDelayMs`: Sets the delay between `GeofenceStatus.ENTER` and `GeofenceStatus.DWELL` in milliseconds. The default is `300000`. 65 | * `statusChangeDelayMs`: Sets the status change delay in milliseconds. `GeofenceStatus.ENTER` and `GeofenceStatus.EXIT` events may be called frequently when the location is near the boundary of the geofence. Use this option to minimize event calls at this time. If the option value is too large, realtime geo-fencing is not possible, so use it carefully. The default is `10000`. 66 | * `useActivityRecognition`: Whether to use the activity recognition API. The default is `true`. 67 | * `allowMockLocations`: Whether to allow mock locations. The default is `false`. 68 | * `printDevLog`: Whether to show the developer log. If this value is set to true, logs for geofence service activities (start, stop, etc.) can be viewed. It does not work in release mode. The default is `false`. 69 | * `geofenceRadiusSortType`: Sets the sort type of the geofence radius. The default is `GeofenceRadiusSortType.DESC`. 70 | 71 | ```dart 72 | // Create a [GeofenceService] instance and set options. 73 | final _geofenceService = GeofenceService.instance.setup( 74 | interval: 5000, 75 | accuracy: 100, 76 | loiteringDelayMs: 60000, 77 | statusChangeDelayMs: 10000, 78 | useActivityRecognition: true, 79 | allowMockLocations: false, 80 | printDevLog: false, 81 | geofenceRadiusSortType: GeofenceRadiusSortType.DESC); 82 | ``` 83 | 84 | 2. Create a `Geofence` and `GeofenceRadius` list. `Geofence` and `GeofenceRadius` provides the following parameters: 85 | * `id`: Identifier for `Geofence` and `GeofenceRadius`. 86 | * `data`: Custom data for `Geofence` and `GeofenceRadius`. 87 | * `latitude`: The latitude of geofence center. 88 | * `longitude`: The longitude of geofence center. 89 | * `radius`: The radius of `Geofence`. 90 | * `length`: The length of the radius in meters. The best result should be set between 100 and 150 meters in radius. If Wi-FI is available, it can be set up to 20~40m. 91 | 92 | ```dart 93 | // Create a [Geofence] list. 94 | final _geofenceList = [ 95 | Geofence( 96 | id: 'place_1', 97 | latitude: 35.103422, 98 | longitude: 129.036023, 99 | radius: [ 100 | GeofenceRadius(id: 'radius_100m', length: 100), 101 | GeofenceRadius(id: 'radius_25m', length: 25), 102 | GeofenceRadius(id: 'radius_250m', length: 250), 103 | GeofenceRadius(id: 'radius_200m', length: 200), 104 | ], 105 | ), 106 | Geofence( 107 | id: 'place_2', 108 | latitude: 35.104971, 109 | longitude: 129.034851, 110 | radius: [ 111 | GeofenceRadius(id: 'radius_25m', length: 25), 112 | GeofenceRadius(id: 'radius_100m', length: 100), 113 | GeofenceRadius(id: 'radius_200m', length: 200), 114 | ], 115 | ), 116 | ]; 117 | ``` 118 | 119 | 3. Register the listener and call `GeofenceService.instance.start()`. 120 | 121 | ```dart 122 | // This function is to be called when the geofence status is changed. 123 | Future _onGeofenceStatusChanged( 124 | Geofence geofence, 125 | GeofenceRadius geofenceRadius, 126 | GeofenceStatus geofenceStatus, 127 | Location location) async { 128 | print('geofence: ${geofence.toJson()}'); 129 | print('geofenceRadius: ${geofenceRadius.toJson()}'); 130 | print('geofenceStatus: ${geofenceStatus.toString()}'); 131 | _geofenceStreamController.sink.add(geofence); 132 | } 133 | 134 | // This function is to be called when the activity has changed. 135 | void _onActivityChanged(Activity prevActivity, Activity currActivity) { 136 | print('prevActivity: ${prevActivity.toJson()}'); 137 | print('currActivity: ${currActivity.toJson()}'); 138 | _activityStreamController.sink.add(currActivity); 139 | } 140 | 141 | // This function is to be called when the location has changed. 142 | void _onLocationChanged(Location location) { 143 | print('location: ${location.toJson()}'); 144 | } 145 | 146 | // This function is to be called when a location services status change occurs 147 | // since the service was started. 148 | void _onLocationServicesStatusChanged(bool status) { 149 | print('isLocationServicesEnabled: $status'); 150 | } 151 | 152 | // This function is used to handle errors that occur in the service. 153 | void _onError(error) { 154 | final errorCode = getErrorCodesFromError(error); 155 | if (errorCode == null) { 156 | print('Undefined error: $error'); 157 | return; 158 | } 159 | 160 | print('ErrorCode: $errorCode'); 161 | } 162 | 163 | @override 164 | void initState() { 165 | super.initState(); 166 | WidgetsBinding.instance.addPostFrameCallback((_) { 167 | _geofenceService.addGeofenceStatusChangeListener(_onGeofenceStatusChanged); 168 | _geofenceService.addLocationChangeListener(_onLocationChanged); 169 | _geofenceService.addLocationServicesStatusChangeListener(_onLocationServicesStatusChanged); 170 | _geofenceService.addActivityChangeListener(_onActivityChanged); 171 | _geofenceService.addStreamErrorListener(_onError); 172 | _geofenceService.start(_geofenceList).catchError(_onError); 173 | }); 174 | } 175 | ``` 176 | 177 | 4. To add or remove `Geofence` while the service is running, use the following function: 178 | 179 | ```text 180 | _geofenceService.addGeofence(Object); 181 | _geofenceService.addGeofenceList(List); 182 | _geofenceService.removeGeofence(Object); 183 | _geofenceService.removeGeofenceList(List); 184 | _geofenceService.removeGeofenceById(String); 185 | _geofenceService.clearGeofenceList(); 186 | ``` 187 | 188 | 5. If you want to pause or resume the service, use the function below. 189 | 190 | ```text 191 | _geofenceService.pause(); 192 | _geofenceService.resume(); 193 | ``` 194 | 195 | 6. When you are finished using the service, unregister the listener and call `GeofenceService.instance.stop()`. 196 | 197 | ```text 198 | _geofenceService.removeGeofenceStatusChangeListener(_onGeofenceStatusChanged); 199 | _geofenceService.removeLocationChangeListener(_onLocationChanged); 200 | _geofenceService.removeLocationServicesStatusChangeListener(_onLocationServicesStatusChanged); 201 | _geofenceService.removeActivityChangeListener(_onActivityChanged); 202 | _geofenceService.removeStreamErrorListener(_onError); 203 | _geofenceService.clearAllListeners(); 204 | _geofenceService.stop(); 205 | ``` 206 | 207 | **Note**: When calling the stop function, the listener is not removed, but the added geofence is cleared. 208 | 209 | ## Models 210 | 211 | ### :chicken: Geofence 212 | 213 | A model representing a geofence. 214 | 215 | | Property | Description | 216 | |---------------------|--------------------------------------------| 217 | | `id` | Identifier for `Geofence`. | 218 | | `data` | Custom data for `Geofence`. | 219 | | `latitude` | The latitude of geofence center. | 220 | | `longitude` | The longitude of geofence center. | 221 | | `radius` | The radius of `Geofence`. | 222 | | `status` | The status of `Geofence`. | 223 | | `timestamp` | The timestamp of `Geofence`. | 224 | | `remainingDistance` | The remaining distance to the destination. | 225 | 226 | ### :chicken: GeofenceRadius 227 | 228 | A model representing the radius of `Geofence`. 229 | 230 | | Property | Description | 231 | |---------------------|-------------------------------------------------| 232 | | `id` | Identifier for `GeofenceRadius`. | 233 | | `data` | Custom data for `GeofenceRadius`. | 234 | | `length` | The length of the radius in meters. | 235 | | `status` | The status of `GeofenceRadius`. | 236 | | `activity` | The user activity when geofence status changes. | 237 | | `speed` | The passing speed when geofence status changes. | 238 | | `timestamp` | The timestamp when geofence status changes. | 239 | | `remainingDistance` | The remaining distance to the radius. | 240 | 241 | ### :chicken: GeofenceStatus 242 | 243 | Defines the type of the geofence status. 244 | 245 | | Value | Description | 246 | |---------|---------------------------------------------------------------------------| 247 | | `ENTER` | Occurs when entering the geofence radius. | 248 | | `EXIT` | Occurs when exiting the geofence radius. | 249 | | `DWELL` | Occurs when the loitering delay elapses after entering the geofence area. | 250 | 251 | ### :chicken: Activity 252 | 253 | A model representing the user's activity. 254 | 255 | | Property | Description | 256 | |--------------|----------------------------------------| 257 | | `type` | The type of activity recognized. | 258 | | `confidence` | The confidence of activity recognized. | 259 | 260 | ### :chicken: ActivityType 261 | 262 | Defines the type of activity. 263 | 264 | | Value | Description | 265 | |--------------|----------------------------------------------------------------------------| 266 | | `IN_VEHICLE` | The device is in a vehicle, such as a car. | 267 | | `ON_BICYCLE` | The device is on a bicycle. | 268 | | `RUNNING` | The device is on a user who is running. This is a sub-activity of ON_FOOT. | 269 | | `STILL` | The device is still (not moving). | 270 | | `WALKING` | The device is on a user who is walking. This is a sub-activity of ON_FOOT. | 271 | | `UNKNOWN` | Unable to detect the current activity. | 272 | 273 | ### :chicken: ActivityConfidence 274 | 275 | Defines the confidence of activity. 276 | 277 | | Value | Description | 278 | |----------|------------------------| 279 | | `HIGH` | High accuracy: 75~100 | 280 | | `MEDIUM` | Medium accuracy: 50~75 | 281 | | `LOW` | Low accuracy: 0~50 | 282 | 283 | ### :chicken: GeofenceRadiusSortType 284 | 285 | Defines the sort type of the geofence radius. If you have set multiple radius for one geofence, multiple radius can come in at the same time. At this time, you can control the order in which the radius comes in by referring to the radius meters. 286 | 287 | | Value | Description | 288 | |--------|--------------------------------------| 289 | | `ASC` | Sort the meters in ascending order. | 290 | | `DESC` | Sort the meters in descending order. | 291 | 292 | ### :chicken: ErrorCodes 293 | 294 | Error codes that may occur in the service. 295 | 296 | | Value | Description | 297 | |------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| 298 | | `ALREADY_STARTED` | Occurs when the service has already been started but the start function is called. | 299 | | `LOCATION_SERVICES_DISABLED` | Occurs when location services are disabled. When this error occurs, you should notify the user and request activation. | 300 | | `LOCATION_PERMISSION_DENIED` | Occurs when location permission is denied. | 301 | | `LOCATION_PERMISSION_PERMANENTLY_DENIED` | Occurs when location permission is permanently denied. In this case, the user must manually allow the permission. | 302 | | `ACTIVITY_RECOGNITION_PERMISSION_DENIED` | Occurs when activity recognition permission is denied. | 303 | | `ACTIVITY_RECOGNITION_PERMISSION_PERMANENTLY_DENIED` | Occurs when activity recognition permission is permanently denied. In this case, the user must manually allow the permission. | 304 | 305 | ## Support 306 | 307 | If you find any bugs or issues while using the plugin, please register an issues on [GitHub](https://github.com/Dev-hwang/geofence_service/issues). You can also contact us at . 308 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: false 6 | avoid_function_literals_in_foreach_calls: false 7 | constant_identifier_names: false 8 | prefer_void_to_null: false 9 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.pravera.geofence_service' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.7.10' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:7.4.2' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | // Conditional for compatibility with AGP <4.2. 29 | if (project.android.hasProperty("namespace")) { 30 | namespace 'com.pravera.geofence_service' 31 | } 32 | 33 | compileSdk 34 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '1.8' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | test.java.srcDirs += 'src/test/kotlin' 47 | } 48 | 49 | defaultConfig { 50 | minSdkVersion 21 51 | } 52 | 53 | dependencies { 54 | implementation 'com.google.android.gms:play-services-location:21.0.1' 55 | 56 | testImplementation 'org.jetbrains.kotlin:kotlin-test' 57 | testImplementation 'org.mockito:mockito-core:5.0.0' 58 | } 59 | 60 | testOptions { 61 | unitTests.all { 62 | useJUnitPlatform() 63 | 64 | testLogging { 65 | events "passed", "skipped", "failed", "standardOut", "standardError" 66 | outputs.upToDateWhen { false } 67 | showStandardStreams = true 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'geofence_service' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/pravera/geofence_service/GeofenceServicePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.pravera.geofence_service 2 | 3 | import io.flutter.embedding.engine.plugins.FlutterPlugin 4 | import io.flutter.embedding.engine.plugins.activity.ActivityAware 5 | import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding 6 | 7 | /** GeofenceServicePlugin */ 8 | class GeofenceServicePlugin: FlutterPlugin, ActivityAware { 9 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { 10 | // onAttachedToEngine 11 | } 12 | 13 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 14 | // onDetachedFromEngine 15 | } 16 | 17 | override fun onAttachedToActivity(binding: ActivityPluginBinding) { 18 | // onAttachedToActivity 19 | } 20 | 21 | override fun onDetachedFromActivityForConfigChanges() { 22 | onDetachedFromActivity() 23 | } 24 | 25 | override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { 26 | onAttachedToActivity(binding) 27 | } 28 | 29 | override fun onDetachedFromActivity() { 30 | // onDetachedFromActivity 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /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 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /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: 8874f21e79d7ec66d0457c7ab338348e31b17f1d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # geofence_service_example 2 | 3 | Demonstrates how to use the geofence_service plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: false 6 | avoid_function_literals_in_foreach_calls: false 7 | constant_identifier_names: false 8 | prefer_void_to_null: false 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.pravera.geofence_service_example" 27 | 28 | compileSdk 34 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | applicationId "com.pravera.geofence_service_example" 45 | minSdkVersion 21 46 | targetSdkVersion 34 47 | versionCode flutterVersionCode.toInteger() 48 | versionName flutterVersionName 49 | } 50 | 51 | buildTypes { 52 | release { 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies {} 64 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 20 | 28 | 32 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/pravera/geofence_service_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.pravera.geofence_service_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: 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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=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-7.6.3-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | } 19 | 20 | plugins { 21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 22 | id "com.android.application" version "7.3.0" apply false 23 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false 24 | } 25 | 26 | include ":app" 27 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 665AE3C4DB6B841F71C0B115 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3118BCCECF2B5602D44E3DE /* Pods_Runner.framework */; }; 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 | 2E70460F176D27229683DAD0 /* 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 = ""; }; 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 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 40 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 41 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 42 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 44 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | C96A51944CC3C043403653AE /* 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 = ""; }; 48 | E3118BCCECF2B5602D44E3DE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | E7E9B3BAB974DEE0217C3ADF /* 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 = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 665AE3C4DB6B841F71C0B115 /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 820202C079AD31FC239A5140 /* Frameworks */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | E3118BCCECF2B5602D44E3DE /* Pods_Runner.framework */, 68 | ); 69 | name = Frameworks; 70 | sourceTree = ""; 71 | }; 72 | 9740EEB11CF90186004384FC /* Flutter */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 76 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 77 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 78 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 79 | ); 80 | name = Flutter; 81 | sourceTree = ""; 82 | }; 83 | 97C146E51CF9000F007C117D = { 84 | isa = PBXGroup; 85 | children = ( 86 | 9740EEB11CF90186004384FC /* Flutter */, 87 | 97C146F01CF9000F007C117D /* Runner */, 88 | 97C146EF1CF9000F007C117D /* Products */, 89 | E0D893E1EFEDB746B85E30CD /* Pods */, 90 | 820202C079AD31FC239A5140 /* Frameworks */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 97C146EF1CF9000F007C117D /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146EE1CF9000F007C117D /* Runner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 97C146F01CF9000F007C117D /* Runner */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 106 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 107 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 108 | 97C147021CF9000F007C117D /* Info.plist */, 109 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 110 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 111 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 112 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 113 | ); 114 | path = Runner; 115 | sourceTree = ""; 116 | }; 117 | E0D893E1EFEDB746B85E30CD /* Pods */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | E7E9B3BAB974DEE0217C3ADF /* Pods-Runner.debug.xcconfig */, 121 | 2E70460F176D27229683DAD0 /* Pods-Runner.release.xcconfig */, 122 | C96A51944CC3C043403653AE /* Pods-Runner.profile.xcconfig */, 123 | ); 124 | name = Pods; 125 | path = Pods; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 97C146ED1CF9000F007C117D /* Runner */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 134 | buildPhases = ( 135 | EE8F1BC97B50A8063D22B87D /* [CP] Check Pods Manifest.lock */, 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | F89CB9A7B6F986A98FF717DD /* [CP] Embed Pods Frameworks */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = Runner; 149 | productName = Runner; 150 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 151 | productType = "com.apple.product-type.application"; 152 | }; 153 | /* End PBXNativeTarget section */ 154 | 155 | /* Begin PBXProject section */ 156 | 97C146E61CF9000F007C117D /* Project object */ = { 157 | isa = PBXProject; 158 | attributes = { 159 | LastUpgradeCheck = 1510; 160 | ORGANIZATIONNAME = ""; 161 | TargetAttributes = { 162 | 97C146ED1CF9000F007C117D = { 163 | CreatedOnToolsVersion = 7.3.1; 164 | LastSwiftMigration = 1100; 165 | }; 166 | }; 167 | }; 168 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 169 | compatibilityVersion = "Xcode 9.3"; 170 | developmentRegion = en; 171 | hasScannedForEncodings = 0; 172 | knownRegions = ( 173 | en, 174 | Base, 175 | ); 176 | mainGroup = 97C146E51CF9000F007C117D; 177 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 97C146ED1CF9000F007C117D /* Runner */, 182 | ); 183 | }; 184 | /* End PBXProject section */ 185 | 186 | /* Begin PBXResourcesBuildPhase section */ 187 | 97C146EC1CF9000F007C117D /* Resources */ = { 188 | isa = PBXResourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 192 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 193 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | alwaysOutOfDate = 1; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | inputPaths = ( 208 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 209 | ); 210 | name = "Thin Binary"; 211 | outputPaths = ( 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 216 | }; 217 | 9740EEB61CF901F6004384FC /* Run Script */ = { 218 | isa = PBXShellScriptBuildPhase; 219 | alwaysOutOfDate = 1; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | inputPaths = ( 224 | ); 225 | name = "Run Script"; 226 | outputPaths = ( 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | shellPath = /bin/sh; 230 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 231 | }; 232 | EE8F1BC97B50A8063D22B87D /* [CP] Check Pods Manifest.lock */ = { 233 | isa = PBXShellScriptBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | ); 237 | inputFileListPaths = ( 238 | ); 239 | inputPaths = ( 240 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 241 | "${PODS_ROOT}/Manifest.lock", 242 | ); 243 | name = "[CP] Check Pods Manifest.lock"; 244 | outputFileListPaths = ( 245 | ); 246 | outputPaths = ( 247 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | 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"; 252 | showEnvVarsInLog = 0; 253 | }; 254 | F89CB9A7B6F986A98FF717DD /* [CP] Embed Pods Frameworks */ = { 255 | isa = PBXShellScriptBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | inputFileListPaths = ( 260 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 261 | ); 262 | name = "[CP] Embed Pods Frameworks"; 263 | outputFileListPaths = ( 264 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | shellPath = /bin/sh; 268 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 269 | showEnvVarsInLog = 0; 270 | }; 271 | /* End PBXShellScriptBuildPhase section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 97C146EA1CF9000F007C117D /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 279 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | /* End PBXSourcesBuildPhase section */ 284 | 285 | /* Begin PBXVariantGroup section */ 286 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | 97C146FB1CF9000F007C117D /* Base */, 290 | ); 291 | name = Main.storyboard; 292 | sourceTree = ""; 293 | }; 294 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 295 | isa = PBXVariantGroup; 296 | children = ( 297 | 97C147001CF9000F007C117D /* Base */, 298 | ); 299 | name = LaunchScreen.storyboard; 300 | sourceTree = ""; 301 | }; 302 | /* End PBXVariantGroup section */ 303 | 304 | /* Begin XCBuildConfiguration section */ 305 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 347 | MTL_ENABLE_DEBUG_INFO = NO; 348 | SDKROOT = iphoneos; 349 | SUPPORTED_PLATFORMS = iphoneos; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | VALIDATE_PRODUCT = YES; 352 | }; 353 | name = Profile; 354 | }; 355 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 356 | isa = XCBuildConfiguration; 357 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CLANG_ENABLE_MODULES = YES; 361 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 362 | ENABLE_BITCODE = NO; 363 | FRAMEWORK_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "$(PROJECT_DIR)/Flutter", 366 | ); 367 | INFOPLIST_FILE = Runner/Info.plist; 368 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 369 | LIBRARY_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | PRODUCT_BUNDLE_IDENTIFIER = com.pravera.geofenceServiceExample; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 376 | SWIFT_VERSION = 5.0; 377 | VERSIONING_SYSTEM = "apple-generic"; 378 | }; 379 | name = Profile; 380 | }; 381 | 97C147031CF9000F007C117D /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ALWAYS_SEARCH_USER_PATHS = NO; 385 | CLANG_ANALYZER_NONNULL = YES; 386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 387 | CLANG_CXX_LIBRARY = "libc++"; 388 | CLANG_ENABLE_MODULES = YES; 389 | CLANG_ENABLE_OBJC_ARC = YES; 390 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_COMMA = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 396 | CLANG_WARN_EMPTY_BODY = YES; 397 | CLANG_WARN_ENUM_CONVERSION = YES; 398 | CLANG_WARN_INFINITE_RECURSION = YES; 399 | CLANG_WARN_INT_CONVERSION = YES; 400 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 402 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 403 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 404 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 405 | CLANG_WARN_STRICT_PROTOTYPES = YES; 406 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 407 | CLANG_WARN_UNREACHABLE_CODE = YES; 408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 409 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 410 | COPY_PHASE_STRIP = NO; 411 | DEBUG_INFORMATION_FORMAT = dwarf; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | ENABLE_TESTABILITY = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu99; 415 | GCC_DYNAMIC_NO_PIC = NO; 416 | GCC_NO_COMMON_BLOCKS = YES; 417 | GCC_OPTIMIZATION_LEVEL = 0; 418 | GCC_PREPROCESSOR_DEFINITIONS = ( 419 | "DEBUG=1", 420 | "$(inherited)", 421 | ); 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 429 | MTL_ENABLE_DEBUG_INFO = YES; 430 | ONLY_ACTIVE_ARCH = YES; 431 | SDKROOT = iphoneos; 432 | TARGETED_DEVICE_FAMILY = "1,2"; 433 | }; 434 | name = Debug; 435 | }; 436 | 97C147041CF9000F007C117D /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | ALWAYS_SEARCH_USER_PATHS = NO; 440 | CLANG_ANALYZER_NONNULL = YES; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 442 | CLANG_CXX_LIBRARY = "libc++"; 443 | CLANG_ENABLE_MODULES = YES; 444 | CLANG_ENABLE_OBJC_ARC = YES; 445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 446 | CLANG_WARN_BOOL_CONVERSION = YES; 447 | CLANG_WARN_COMMA = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INFINITE_RECURSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 456 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 457 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 459 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 460 | CLANG_WARN_STRICT_PROTOTYPES = YES; 461 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 467 | ENABLE_NS_ASSERTIONS = NO; 468 | ENABLE_STRICT_OBJC_MSGSEND = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu99; 470 | GCC_NO_COMMON_BLOCKS = YES; 471 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 472 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 473 | GCC_WARN_UNDECLARED_SELECTOR = YES; 474 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 475 | GCC_WARN_UNUSED_FUNCTION = YES; 476 | GCC_WARN_UNUSED_VARIABLE = YES; 477 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 478 | MTL_ENABLE_DEBUG_INFO = NO; 479 | SDKROOT = iphoneos; 480 | SUPPORTED_PLATFORMS = iphoneos; 481 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 482 | TARGETED_DEVICE_FAMILY = "1,2"; 483 | VALIDATE_PRODUCT = YES; 484 | }; 485 | name = Release; 486 | }; 487 | 97C147061CF9000F007C117D /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | CLANG_ENABLE_MODULES = YES; 493 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 494 | ENABLE_BITCODE = NO; 495 | FRAMEWORK_SEARCH_PATHS = ( 496 | "$(inherited)", 497 | "$(PROJECT_DIR)/Flutter", 498 | ); 499 | INFOPLIST_FILE = Runner/Info.plist; 500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 501 | LIBRARY_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | PRODUCT_BUNDLE_IDENTIFIER = com.pravera.geofenceServiceExample; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 508 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 509 | SWIFT_VERSION = 5.0; 510 | VERSIONING_SYSTEM = "apple-generic"; 511 | }; 512 | name = Debug; 513 | }; 514 | 97C147071CF9000F007C117D /* Release */ = { 515 | isa = XCBuildConfiguration; 516 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 517 | buildSettings = { 518 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 519 | CLANG_ENABLE_MODULES = YES; 520 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 521 | ENABLE_BITCODE = NO; 522 | FRAMEWORK_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "$(PROJECT_DIR)/Flutter", 525 | ); 526 | INFOPLIST_FILE = Runner/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 528 | LIBRARY_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | PRODUCT_BUNDLE_IDENTIFIER = com.pravera.geofenceServiceExample; 533 | PRODUCT_NAME = "$(TARGET_NAME)"; 534 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 535 | SWIFT_VERSION = 5.0; 536 | VERSIONING_SYSTEM = "apple-generic"; 537 | }; 538 | name = Release; 539 | }; 540 | /* End XCBuildConfiguration section */ 541 | 542 | /* Begin XCConfigurationList section */ 543 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 544 | isa = XCConfigurationList; 545 | buildConfigurations = ( 546 | 97C147031CF9000F007C117D /* Debug */, 547 | 97C147041CF9000F007C117D /* Release */, 548 | 249021D3217E4FDB00AE95B9 /* Profile */, 549 | ); 550 | defaultConfigurationIsVisible = 0; 551 | defaultConfigurationName = Release; 552 | }; 553 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | 97C147061CF9000F007C117D /* Debug */, 557 | 97C147071CF9000F007C117D /* Release */, 558 | 249021D4217E4FDB00AE95B9 /* Profile */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | /* End XCConfigurationList section */ 564 | }; 565 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 566 | } 567 | -------------------------------------------------------------------------------- /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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/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 | geofence_service_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | NSLocationWhenInUseUsageDescription 45 | Used to provide geofence service. 46 | NSMotionUsageDescription 47 | Used to recognize user activity information. 48 | CADisableMinimumFrameDurationOnPhone 49 | 50 | UIApplicationSupportsIndirectInputEvents 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:geofence_service/geofence_service.dart'; 5 | 6 | void main() => runApp(const ExampleApp()); 7 | 8 | class ExampleApp extends StatefulWidget { 9 | const ExampleApp({Key? key}) : super(key: key); 10 | 11 | @override 12 | State createState() => _ExampleAppState(); 13 | } 14 | 15 | class _ExampleAppState extends State { 16 | final _activityStreamController = StreamController(); 17 | final _geofenceStreamController = StreamController(); 18 | 19 | // Create a [GeofenceService] instance and set options. 20 | final _geofenceService = GeofenceService.instance.setup( 21 | interval: 5000, 22 | accuracy: 100, 23 | loiteringDelayMs: 60000, 24 | statusChangeDelayMs: 10000, 25 | useActivityRecognition: true, 26 | allowMockLocations: false, 27 | printDevLog: false, 28 | geofenceRadiusSortType: GeofenceRadiusSortType.DESC); 29 | 30 | // Create a [Geofence] list. 31 | final _geofenceList = [ 32 | Geofence( 33 | id: 'place_1', 34 | latitude: 35.103422, 35 | longitude: 129.036023, 36 | radius: [ 37 | GeofenceRadius(id: 'radius_100m', length: 100), 38 | GeofenceRadius(id: 'radius_25m', length: 25), 39 | GeofenceRadius(id: 'radius_250m', length: 250), 40 | GeofenceRadius(id: 'radius_200m', length: 200), 41 | ], 42 | ), 43 | Geofence( 44 | id: 'place_2', 45 | latitude: 35.104971, 46 | longitude: 129.034851, 47 | radius: [ 48 | GeofenceRadius(id: 'radius_25m', length: 25), 49 | GeofenceRadius(id: 'radius_100m', length: 100), 50 | GeofenceRadius(id: 'radius_200m', length: 200), 51 | ], 52 | ), 53 | ]; 54 | 55 | // This function is to be called when the geofence status is changed. 56 | Future _onGeofenceStatusChanged( 57 | Geofence geofence, 58 | GeofenceRadius geofenceRadius, 59 | GeofenceStatus geofenceStatus, 60 | Location location) async { 61 | print('geofence: ${geofence.toJson()}'); 62 | print('geofenceRadius: ${geofenceRadius.toJson()}'); 63 | print('geofenceStatus: ${geofenceStatus.toString()}'); 64 | _geofenceStreamController.sink.add(geofence); 65 | } 66 | 67 | // This function is to be called when the activity has changed. 68 | void _onActivityChanged(Activity prevActivity, Activity currActivity) { 69 | print('prevActivity: ${prevActivity.toJson()}'); 70 | print('currActivity: ${currActivity.toJson()}'); 71 | _activityStreamController.sink.add(currActivity); 72 | } 73 | 74 | // This function is to be called when the location has changed. 75 | void _onLocationChanged(Location location) { 76 | print('location: ${location.toJson()}'); 77 | } 78 | 79 | // This function is to be called when a location services status change occurs 80 | // since the service was started. 81 | void _onLocationServicesStatusChanged(bool status) { 82 | print('isLocationServicesEnabled: $status'); 83 | } 84 | 85 | // This function is used to handle errors that occur in the service. 86 | void _onError(error) { 87 | final errorCode = getErrorCodesFromError(error); 88 | if (errorCode == null) { 89 | print('Undefined error: $error'); 90 | return; 91 | } 92 | 93 | print('ErrorCode: $errorCode'); 94 | } 95 | 96 | @override 97 | void initState() { 98 | super.initState(); 99 | WidgetsBinding.instance.addPostFrameCallback((_) { 100 | _geofenceService.addGeofenceStatusChangeListener(_onGeofenceStatusChanged); 101 | _geofenceService.addLocationChangeListener(_onLocationChanged); 102 | _geofenceService.addLocationServicesStatusChangeListener(_onLocationServicesStatusChanged); 103 | _geofenceService.addActivityChangeListener(_onActivityChanged); 104 | _geofenceService.addStreamErrorListener(_onError); 105 | _geofenceService.start(_geofenceList).catchError(_onError); 106 | }); 107 | } 108 | 109 | @override 110 | Widget build(BuildContext context) { 111 | return MaterialApp( 112 | home: Scaffold( 113 | appBar: AppBar( 114 | title: const Text('Geofence Service'), 115 | centerTitle: true, 116 | ), 117 | body: _buildContentView(), 118 | ), 119 | ); 120 | } 121 | 122 | @override 123 | void dispose() { 124 | _activityStreamController.close(); 125 | _geofenceStreamController.close(); 126 | super.dispose(); 127 | } 128 | 129 | Widget _buildContentView() { 130 | return ListView( 131 | physics: const BouncingScrollPhysics(), 132 | padding: const EdgeInsets.all(8.0), 133 | children: [ 134 | _buildActivityMonitor(), 135 | const SizedBox(height: 20.0), 136 | _buildGeofenceMonitor(), 137 | ], 138 | ); 139 | } 140 | 141 | Widget _buildActivityMonitor() { 142 | return StreamBuilder( 143 | stream: _activityStreamController.stream, 144 | builder: (context, snapshot) { 145 | final updatedDateTime = DateTime.now(); 146 | final content = snapshot.data?.toJson().toString() ?? ''; 147 | 148 | return Column( 149 | crossAxisAlignment: CrossAxisAlignment.start, 150 | children: [ 151 | Text('•\t\tActivity (updated: $updatedDateTime)'), 152 | const SizedBox(height: 10.0), 153 | Text(content), 154 | ], 155 | ); 156 | }, 157 | ); 158 | } 159 | 160 | Widget _buildGeofenceMonitor() { 161 | return StreamBuilder( 162 | stream: _geofenceStreamController.stream, 163 | builder: (context, snapshot) { 164 | final updatedDateTime = DateTime.now(); 165 | final content = snapshot.data?.toJson().toString() ?? ''; 166 | 167 | return Column( 168 | crossAxisAlignment: CrossAxisAlignment.start, 169 | children: [ 170 | Text('•\t\tGeofence (updated: $updatedDateTime)'), 171 | const SizedBox(height: 10.0), 172 | Text(content), 173 | ], 174 | ); 175 | }, 176 | ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: geofence_service_example 2 | description: Demonstrates how to use the geofence_service plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: ">=2.18.0 <4.0.0" 10 | flutter: ">=3.3.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | 16 | geofence_service: 17 | # When depending on this package from a real application you should use: 18 | # geofence_service: ^x.y.z 19 | # See https://dart.dev/tools/pub/dependencies#version-constraints 20 | # The example app is bundled with the plugin so we use a path dependency on 21 | # the parent directory to use the current plugin's version. 22 | path: ../ 23 | 24 | dev_dependencies: 25 | flutter_test: 26 | sdk: flutter 27 | 28 | flutter_lints: ^2.0.1 29 | 30 | # The following section is specific to Flutter. 31 | flutter: 32 | 33 | # The following line ensures that the Material Icons font is 34 | # included with your application, so that you can use the icons in 35 | # the material Icons class. 36 | uses-material-design: true 37 | 38 | # To add assets to your application, add an assets section, like this: 39 | # assets: 40 | # - images/a_dot_burr.jpeg 41 | # - images/a_dot_ham.jpeg 42 | 43 | # An image asset can refer to one or more resolution-specific "variants", see 44 | # https://flutter.dev/assets-and-images/#resolution-aware. 45 | 46 | # For details regarding adding assets from package dependencies, see 47 | # https://flutter.dev/assets-and-images/#from-packages 48 | 49 | # To add custom fonts to your application, add a fonts section here, 50 | # in this "flutter" section. Each entry in this list should have a 51 | # "family" key with the font family name, and a "fonts" key with a 52 | # list giving the asset and other descriptors for the font. For 53 | # example: 54 | # fonts: 55 | # - family: Schyler 56 | # fonts: 57 | # - asset: fonts/Schyler-Regular.ttf 58 | # - asset: fonts/Schyler-Italic.ttf 59 | # style: italic 60 | # - family: Trajan Pro 61 | # fonts: 62 | # - asset: fonts/TrajanPro.ttf 63 | # - asset: fonts/TrajanPro_Bold.ttf 64 | # weight: 700 65 | # 66 | # For details regarding fonts from package dependencies, 67 | # see https://flutter.dev/custom-fonts/#from-packages 68 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /geofence_service.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dev-hwang/geofence_service/3f89f7094326f573b8286c094a5f37c2c187fd0c/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/GeofenceServicePlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface GeofenceServicePlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/GeofenceServicePlugin.m: -------------------------------------------------------------------------------- 1 | #import "GeofenceServicePlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "geofence_service-Swift.h" 9 | #endif 10 | 11 | @implementation GeofenceServicePlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftGeofenceServicePlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/SwiftGeofenceServicePlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftGeofenceServicePlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let instance = SwiftGeofenceServicePlugin() 7 | instance.initServices() 8 | instance.initChannels(registrar.messenger()) 9 | } 10 | 11 | private func initServices() { 12 | // initServices 13 | } 14 | 15 | private func initChannels(_ messenger: FlutterBinaryMessenger) { 16 | // initChannels 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/geofence_service.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint geofence_service.podspec' to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'geofence_service' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '8.0' 19 | 20 | # Flutter.framework does not contain a i386 slice. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 22 | s.swift_version = '5.0' 23 | end 24 | -------------------------------------------------------------------------------- /lib/errors/error_codes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | /// Error codes that may occur in the service. 4 | enum ErrorCodes { 5 | /// Occurs when the service has already been started but the start function is called. 6 | ALREADY_STARTED, 7 | 8 | /// Occurs when location services are disabled. 9 | /// When this error occurs, you should notify the user and request activation. 10 | LOCATION_SERVICES_DISABLED, 11 | 12 | /// Occurs when location permission is denied. 13 | LOCATION_PERMISSION_DENIED, 14 | 15 | /// Occurs when location permission is permanently denied. 16 | /// In this case, the user must manually allow the permission. 17 | LOCATION_PERMISSION_PERMANENTLY_DENIED, 18 | 19 | /// Occurs when activity recognition permission is denied. 20 | ACTIVITY_RECOGNITION_PERMISSION_DENIED, 21 | 22 | /// Occurs when activity recognition permission is permanently denied. 23 | /// In this case, the user must manually allow the permission. 24 | ACTIVITY_RECOGNITION_PERMISSION_PERMANENTLY_DENIED 25 | } 26 | 27 | /// Returns the error codes from [value]. 28 | ErrorCodes? getErrorCodesFromString(String value) { 29 | final errorCodes = ErrorCodes.values.where((e) => 30 | e.toString() == value || 31 | e.toString() == 'ErrorCodes.${value.toUpperCase()}'); 32 | 33 | if (errorCodes.isNotEmpty) return errorCodes.first; 34 | return null; 35 | } 36 | 37 | /// Returns the error codes from [error]. 38 | ErrorCodes? getErrorCodesFromError(dynamic error) { 39 | if (error is ErrorCodes) { 40 | return error; 41 | } else if (error is PlatformException) { 42 | PlatformException pe = error; 43 | return getErrorCodesFromString(pe.code); 44 | } 45 | 46 | return getErrorCodesFromString(error.toString()); 47 | } 48 | -------------------------------------------------------------------------------- /lib/geofence_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer' as dev; 3 | 4 | import 'package:fl_location/fl_location.dart'; 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter_activity_recognition/flutter_activity_recognition.dart'; 7 | import 'package:geofence_service/errors/error_codes.dart'; 8 | import 'package:geofence_service/models/geofence.dart'; 9 | import 'package:geofence_service/models/geofence_radius.dart'; 10 | import 'package:geofence_service/models/geofence_radius_sort_type.dart'; 11 | import 'package:geofence_service/models/geofence_service_options.dart'; 12 | import 'package:geofence_service/models/geofence_status.dart'; 13 | 14 | export 'package:fl_location/fl_location.dart'; 15 | export 'package:flutter_activity_recognition/flutter_activity_recognition.dart'; 16 | export 'package:geofence_service/errors/error_codes.dart'; 17 | export 'package:geofence_service/models/geofence.dart'; 18 | export 'package:geofence_service/models/geofence_radius.dart'; 19 | export 'package:geofence_service/models/geofence_radius_sort_type.dart'; 20 | export 'package:geofence_service/models/geofence_service_options.dart'; 21 | export 'package:geofence_service/models/geofence_status.dart'; 22 | 23 | /// Callback function to handle geofence status changes. 24 | typedef GeofenceStatusChanged = Future Function( 25 | Geofence geofence, 26 | GeofenceRadius geofenceRadius, 27 | GeofenceStatus geofenceStatus, 28 | Location location); 29 | 30 | /// Callback function to handle location changes. 31 | typedef LocationChanged = void Function(Location location); 32 | 33 | /// Callback function to handle activity changes. 34 | typedef ActivityChanged = void Function( 35 | Activity prevActivity, Activity currActivity); 36 | 37 | /// A class provides geofence management and geo-fencing. 38 | class GeofenceService { 39 | GeofenceService._internal(); 40 | 41 | /// Instance of [GeofenceService]. 42 | static final instance = GeofenceService._internal(); 43 | 44 | /// Whether the service is running. 45 | bool _isRunningService = false; 46 | 47 | /// Returns whether the service is running. 48 | bool get isRunningService => _isRunningService; 49 | 50 | final GeofenceServiceOptions _options = GeofenceServiceOptions(); 51 | 52 | StreamSubscription? _locationSubscription; 53 | StreamSubscription? _locationServicesStatusSubscription; 54 | StreamSubscription? _activitySubscription; 55 | Activity _activity = Activity.unknown; 56 | 57 | final _geofenceList = []; 58 | final _geofenceStatusChangeListeners = []; 59 | final _locationChangeListeners = []; 60 | final _locationServicesStatusChangeListeners = >[]; 61 | final _activityChangeListeners = []; 62 | final _streamErrorListeners = []; 63 | 64 | /// Setup [GeofenceService]. 65 | /// Some options do not change while the service is running. 66 | GeofenceService setup({ 67 | int? interval, 68 | int? accuracy, 69 | int? loiteringDelayMs, 70 | int? statusChangeDelayMs, 71 | bool? useActivityRecognition, 72 | bool? allowMockLocations, 73 | bool? printDevLog, 74 | GeofenceRadiusSortType? geofenceRadiusSortType, 75 | }) { 76 | _options.interval = interval; 77 | _options.accuracy = accuracy; 78 | _options.loiteringDelayMs = loiteringDelayMs; 79 | _options.statusChangeDelayMs = statusChangeDelayMs; 80 | _options.useActivityRecognition = useActivityRecognition; 81 | _options.allowMockLocations = allowMockLocations; 82 | _options.printDevLog = printDevLog; 83 | _options.geofenceRadiusSortType = geofenceRadiusSortType; 84 | 85 | return this; 86 | } 87 | 88 | /// Start [GeofenceService]. 89 | /// It can be initialized with [geofenceList]. 90 | Future start([List? geofenceList]) async { 91 | if (_isRunningService) return Future.error(ErrorCodes.ALREADY_STARTED); 92 | 93 | await _checkPermissions(); 94 | await _listenStream(); 95 | 96 | _activity = Activity.unknown; 97 | if (geofenceList != null) _geofenceList.addAll(geofenceList); 98 | 99 | _isRunningService = true; 100 | _printDevLog('GeofenceService started.'); 101 | } 102 | 103 | /// Stop [GeofenceService]. 104 | /// Note that the registered geofence list is cleared when this function is called. 105 | Future stop() async { 106 | await _cancelStream(); 107 | 108 | _activity = Activity.unknown; 109 | _geofenceList.clear(); 110 | 111 | _isRunningService = false; 112 | _printDevLog('GeofenceService stopped.'); 113 | } 114 | 115 | /// Pause [GeofenceService]. 116 | void pause() { 117 | _locationSubscription?.pause(); 118 | _activitySubscription?.pause(); 119 | _printDevLog('GeofenceService paused.'); 120 | } 121 | 122 | /// Resume [GeofenceService]. 123 | void resume() { 124 | _locationSubscription?.resume(); 125 | _activitySubscription?.resume(); 126 | _printDevLog('GeofenceService resumed.'); 127 | } 128 | 129 | /// Register a closure to be called when the [GeofenceStatus] changes. 130 | void addGeofenceStatusChangeListener(GeofenceStatusChanged listener) { 131 | _geofenceStatusChangeListeners.add(listener); 132 | _printDevLog( 133 | 'Added GeofenceStatusChange listener. (size: ${_geofenceStatusChangeListeners.length})'); 134 | } 135 | 136 | /// Remove a previously registered closure from the list of closures that 137 | /// are notified when the [GeofenceStatus] changes. 138 | void removeGeofenceStatusChangeListener(GeofenceStatusChanged listener) { 139 | _geofenceStatusChangeListeners.remove(listener); 140 | _printDevLog( 141 | 'The GeofenceStatusChange listener has been removed. (size: ${_geofenceStatusChangeListeners.length})'); 142 | } 143 | 144 | /// Register a closure to be called when the [Activity] changes. 145 | void addActivityChangeListener(ActivityChanged listener) { 146 | _activityChangeListeners.add(listener); 147 | _printDevLog( 148 | 'Added ActivityChange listener. (size: ${_activityChangeListeners.length})'); 149 | } 150 | 151 | /// Remove a previously registered closure from the list of closures that 152 | /// are notified when the [Activity] changes. 153 | void removeActivityChangeListener(ActivityChanged listener) { 154 | _activityChangeListeners.remove(listener); 155 | _printDevLog( 156 | 'The ActivityChange listener has been removed. (size: ${_activityChangeListeners.length})'); 157 | } 158 | 159 | /// Register a closure to be called when the [Location] changes. 160 | void addLocationChangeListener(LocationChanged listener) { 161 | _locationChangeListeners.add(listener); 162 | _printDevLog( 163 | 'Added LocationChange listener. (size: ${_locationChangeListeners.length})'); 164 | } 165 | 166 | /// Remove a previously registered closure from the list of closures that 167 | /// are notified when the [Location] changes. 168 | void removeLocationChangeListener(LocationChanged listener) { 169 | _locationChangeListeners.remove(listener); 170 | _printDevLog( 171 | 'The LocationChange listener has been removed. (size: ${_locationChangeListeners.length})'); 172 | } 173 | 174 | /// Register a closure to be called when the location services status changes. 175 | void addLocationServicesStatusChangeListener(ValueChanged listener) { 176 | _locationServicesStatusChangeListeners.add(listener); 177 | _printDevLog( 178 | 'Added LocationServicesStatusChange listener. (size: ${_locationServicesStatusChangeListeners.length})'); 179 | } 180 | 181 | /// Remove a previously registered closure from the list of closures that 182 | /// are notified when the location services status changes. 183 | void removeLocationServicesStatusChangeListener(ValueChanged listener) { 184 | _locationServicesStatusChangeListeners.remove(listener); 185 | _printDevLog( 186 | 'The LocationServicesStatusChange listener has been removed. (size: ${_locationServicesStatusChangeListeners.length})'); 187 | } 188 | 189 | /// Register a closure to be called when a stream error occurs. 190 | void addStreamErrorListener(ValueChanged listener) { 191 | _streamErrorListeners.add(listener); 192 | _printDevLog( 193 | 'Added StreamError listener. (size: ${_streamErrorListeners.length})'); 194 | } 195 | 196 | /// Remove a previously registered closure from the list of closures that 197 | /// are notified when a stream error occurs. 198 | void removeStreamErrorListener(ValueChanged listener) { 199 | _streamErrorListeners.remove(listener); 200 | _printDevLog( 201 | 'The StreamError listener has been removed. (size: ${_streamErrorListeners.length})'); 202 | } 203 | 204 | /// Clears all listeners registered with the service. 205 | void clearAllListeners() { 206 | _geofenceStatusChangeListeners.clear(); 207 | _locationChangeListeners.clear(); 208 | _locationServicesStatusChangeListeners.clear(); 209 | _activityChangeListeners.clear(); 210 | _streamErrorListeners.clear(); 211 | } 212 | 213 | /// Add geofence. 214 | void addGeofence(Geofence geofence) { 215 | _geofenceList.add(geofence); 216 | _printDevLog( 217 | 'Added Geofence(${geofence.id}) (size: ${_geofenceList.length})'); 218 | } 219 | 220 | /// Add geofence list. 221 | void addGeofenceList(List geofenceList) { 222 | for (var i = 0; i < geofenceList.length; i++) { 223 | addGeofence(geofenceList[i]); 224 | } 225 | } 226 | 227 | /// Remove geofence. 228 | void removeGeofence(Geofence geofence) { 229 | _geofenceList.remove(geofence); 230 | _printDevLog( 231 | 'The Geofence(${geofence.id}) has been removed. (size: ${_geofenceList.length})'); 232 | } 233 | 234 | /// Remove geofence list. 235 | void removeGeofenceList(List geofenceList) { 236 | for (var i = 0; i < geofenceList.length; i++) { 237 | removeGeofence(geofenceList[i]); 238 | } 239 | } 240 | 241 | /// Remove geofence by [id]. 242 | void removeGeofenceById(String id) { 243 | _geofenceList.removeWhere((geofence) => geofence.id == id); 244 | _printDevLog( 245 | 'The Geofence($id) has been removed. (size: ${_geofenceList.length})'); 246 | } 247 | 248 | /// Clear geofence list. 249 | void clearGeofenceList() { 250 | _geofenceList.clear(); 251 | _printDevLog('The GeofenceList has been cleared.'); 252 | } 253 | 254 | Future _checkPermissions() async { 255 | // Check whether location services are enabled. 256 | if (!await FlLocation.isLocationServicesEnabled) { 257 | return Future.error(ErrorCodes.LOCATION_SERVICES_DISABLED); 258 | } 259 | 260 | // Check whether to allow location permission. 261 | var locationPermission = await FlLocation.checkLocationPermission(); 262 | if (locationPermission == LocationPermission.deniedForever) { 263 | return Future.error(ErrorCodes.LOCATION_PERMISSION_PERMANENTLY_DENIED); 264 | } else if (locationPermission == LocationPermission.denied) { 265 | locationPermission = await FlLocation.requestLocationPermission(); 266 | if (locationPermission == LocationPermission.denied || 267 | locationPermission == LocationPermission.deniedForever) { 268 | return Future.error(ErrorCodes.LOCATION_PERMISSION_DENIED); 269 | } 270 | } 271 | 272 | // Activity Recognition API 사용 안함 273 | if (_options.useActivityRecognition == false) return; 274 | 275 | // Check whether to allow activity recognition permission. 276 | var activityPermission = 277 | await FlutterActivityRecognition.instance.checkPermission(); 278 | if (activityPermission == PermissionRequestResult.PERMANENTLY_DENIED) { 279 | return Future.error( 280 | ErrorCodes.ACTIVITY_RECOGNITION_PERMISSION_PERMANENTLY_DENIED); 281 | } else if (activityPermission == PermissionRequestResult.DENIED) { 282 | activityPermission = 283 | await FlutterActivityRecognition.instance.requestPermission(); 284 | if (activityPermission != PermissionRequestResult.GRANTED) { 285 | return Future.error(ErrorCodes.ACTIVITY_RECOGNITION_PERMISSION_DENIED); 286 | } 287 | } 288 | } 289 | 290 | Future _listenStream() async { 291 | _locationSubscription = FlLocation.getLocationStream( 292 | accuracy: LocationAccuracy.navigation, 293 | interval: _options.interval, 294 | ).handleError(_handleStreamError).listen(_onLocationReceive); 295 | 296 | _locationServicesStatusSubscription = 297 | FlLocation.getLocationServicesStatusStream() 298 | .map((event) => event == LocationServicesStatus.enabled) 299 | .listen(_onLocationServicesStatusChange); 300 | 301 | // Activity Recognition API 사용 안함 302 | if (_options.useActivityRecognition == false) return; 303 | 304 | _activitySubscription = FlutterActivityRecognition.instance.activityStream 305 | .handleError(_handleStreamError) 306 | .listen(_onActivityReceive); 307 | } 308 | 309 | Future _cancelStream() async { 310 | await _locationSubscription?.cancel(); 311 | _locationSubscription = null; 312 | 313 | await _locationServicesStatusSubscription?.cancel(); 314 | _locationServicesStatusSubscription = null; 315 | 316 | await _activitySubscription?.cancel(); 317 | _activitySubscription = null; 318 | } 319 | 320 | void _onLocationReceive(Location location) async { 321 | if (location.isMock && !_options.allowMockLocations) return; 322 | if (location.accuracy > _options.accuracy) return; 323 | 324 | for (final listener in _locationChangeListeners) { 325 | listener(location); 326 | } 327 | 328 | // Pause the service and process the location. 329 | _locationSubscription?.pause(); 330 | 331 | double geoRemainingDistance; 332 | double radRemainingDistance; 333 | Geofence geofence; 334 | GeofenceRadius geofenceRadius; 335 | GeofenceStatus geofenceStatus; 336 | List geofenceRadiusList; 337 | 338 | final currTimestamp = location.timestamp; 339 | DateTime? radTimestamp; 340 | Duration diffTimestamp; 341 | 342 | for (var i = 0; i < _geofenceList.length; i++) { 343 | geofence = _geofenceList[i]; 344 | 345 | // 지오펜스 남은 거리 계산 및 업데이트 346 | geoRemainingDistance = LocationUtils.distanceBetween(location.latitude, 347 | location.longitude, geofence.latitude, geofence.longitude); 348 | geofence.updateRemainingDistance(geoRemainingDistance); 349 | 350 | // 지오펜스 반경 미터 단위 정렬 351 | geofenceRadiusList = geofence.radius.toList(); 352 | if (_options.geofenceRadiusSortType == GeofenceRadiusSortType.ASC) { 353 | geofenceRadiusList.sort((a, b) => a.length.compareTo(b.length)); 354 | } else { 355 | geofenceRadiusList.sort((a, b) => b.length.compareTo(a.length)); 356 | } 357 | 358 | // 지오펜스 반경 처리 시작 359 | for (var j = 0; j < geofenceRadiusList.length; j++) { 360 | geofenceRadius = geofenceRadiusList[j]; 361 | 362 | // 지오펜스 반경 상태 업데이트 시간차 계산 363 | radTimestamp = geofenceRadius.timestamp; 364 | diffTimestamp = currTimestamp.difference(radTimestamp ?? currTimestamp); 365 | 366 | // 지오펜스 반경 상태 결정 367 | if (geoRemainingDistance <= geofenceRadius.length) { 368 | geofenceStatus = GeofenceStatus.ENTER; 369 | 370 | if ((diffTimestamp.inMilliseconds > _options.loiteringDelayMs && 371 | geofenceRadius.status == GeofenceStatus.ENTER) || 372 | geofenceRadius.status == GeofenceStatus.DWELL) { 373 | geofenceStatus = GeofenceStatus.DWELL; 374 | } 375 | } else { 376 | geofenceStatus = GeofenceStatus.EXIT; 377 | } 378 | 379 | // 지오펜스 반경 남은 거리 계산 및 업데이트 380 | radRemainingDistance = geoRemainingDistance - geofenceRadius.length; 381 | geofenceRadius.updateRemainingDistance(radRemainingDistance); 382 | 383 | // 상태 변경이 빈번하게 발생하지 않도록 딜레이 적용 384 | if (geofenceStatus != GeofenceStatus.DWELL && 385 | radTimestamp != null && 386 | diffTimestamp.inMilliseconds < _options.statusChangeDelayMs) { 387 | continue; 388 | } 389 | 390 | // 지오펜스 반경 상태 업데이트 391 | if (!geofenceRadius.updateStatus( 392 | geofenceStatus, _activity, location.speed, currTimestamp)) { 393 | continue; 394 | } 395 | 396 | // 지오펜스 상태 변화 알림 397 | for (final listener in _geofenceStatusChangeListeners) { 398 | await listener(geofence, geofenceRadius, geofenceStatus, location) 399 | .catchError(_handleStreamError); 400 | } 401 | } 402 | } 403 | 404 | // Service resumes when the location processing is complete. 405 | _locationSubscription?.resume(); 406 | } 407 | 408 | void _onLocationServicesStatusChange(bool status) { 409 | for (final listener in _locationServicesStatusChangeListeners) { 410 | listener(status); 411 | } 412 | } 413 | 414 | void _onActivityReceive(Activity activity) { 415 | if (_activity == activity) return; 416 | 417 | for (final listener in _activityChangeListeners) { 418 | listener(_activity, activity); 419 | } 420 | _activity = activity; 421 | } 422 | 423 | void _handleStreamError(dynamic error) { 424 | for (final listener in _streamErrorListeners) { 425 | listener(error); 426 | } 427 | } 428 | 429 | void _printDevLog(String message) { 430 | if (kReleaseMode) return; 431 | if (!_options.printDevLog) return; 432 | 433 | final nowDateTime = DateTime.now().toString(); 434 | dev.log('$nowDateTime\t$message'); 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /lib/models/geofence.dart: -------------------------------------------------------------------------------- 1 | import 'package:geofence_service/models/geofence_radius.dart'; 2 | import 'package:geofence_service/models/geofence_status.dart'; 3 | 4 | /// A model representing a geofence. 5 | class Geofence { 6 | /// Identifier for [Geofence]. 7 | final String id; 8 | 9 | /// Custom data for [Geofence]. 10 | final dynamic data; 11 | 12 | /// The latitude of geofence center. 13 | final double latitude; 14 | 15 | /// The longitude of geofence center. 16 | final double longitude; 17 | 18 | /// The radius of [Geofence]. 19 | final List radius; 20 | 21 | /// Returns the status of [Geofence]. 22 | GeofenceStatus get status => _getStatus(); 23 | 24 | /// Returns the timestamp of [Geofence]. 25 | DateTime? get timestamp => _getTimestamp(); 26 | 27 | /// The remaining distance to the destination. 28 | double? _remainingDistance; 29 | 30 | /// Returns the remaining distance to the destination. 31 | double? get remainingDistance => _remainingDistance; 32 | 33 | /// Constructs an instance of [Geofence]. 34 | Geofence({ 35 | required this.id, 36 | this.data, 37 | required this.latitude, 38 | required this.longitude, 39 | required this.radius, 40 | }) : assert(id.isNotEmpty), 41 | assert(radius.isNotEmpty); 42 | 43 | /// Returns the data fields of [Geofence] in JSON format. 44 | Map toJson() { 45 | return { 46 | 'id': id, 47 | 'data': data, 48 | 'latitude': latitude, 49 | 'longitude': longitude, 50 | 'radius': radius.map((e) => e.toJson()).toList(), 51 | 'status': status, 52 | 'timestamp': timestamp, 53 | 'remainingDistance': _remainingDistance 54 | }; 55 | } 56 | 57 | /// Update the remaining distance of [Geofence]. 58 | void updateRemainingDistance(double distance) { 59 | if (distance < 0.0) _remainingDistance = 0.0; 60 | _remainingDistance = distance; 61 | } 62 | 63 | /// Gets the status of [Geofence]. 64 | GeofenceStatus _getStatus() { 65 | final innerRadius = radius.where((e) => e.status != GeofenceStatus.EXIT); 66 | final dwellRadius = 67 | innerRadius.where((e) => e.status == GeofenceStatus.DWELL); 68 | 69 | if (innerRadius.isNotEmpty) { 70 | return dwellRadius.isNotEmpty 71 | ? GeofenceStatus.DWELL 72 | : GeofenceStatus.ENTER; 73 | } else { 74 | return GeofenceStatus.EXIT; 75 | } 76 | } 77 | 78 | /// Gets the timestamp of [Geofence]. 79 | DateTime? _getTimestamp() { 80 | final timestampList = []; 81 | DateTime? timestamp; 82 | for (var i = 0; i < radius.length; i++) { 83 | timestamp = radius[i].timestamp; 84 | if (timestamp != null) timestampList.add(timestamp); 85 | } 86 | 87 | timestampList.sort((a, b) => a.compareTo(b)); 88 | if (timestampList.isEmpty) return null; 89 | 90 | if (_getStatus() != GeofenceStatus.EXIT) { 91 | return timestampList.first; 92 | } else { 93 | return timestampList.last; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/models/geofence_radius.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_activity_recognition/models/activity.dart'; 2 | import 'package:geofence_service/models/geofence_status.dart'; 3 | 4 | /// A model representing the radius of [Geofence]. 5 | class GeofenceRadius { 6 | /// Identifier for [GeofenceRadius]. 7 | final String id; 8 | 9 | /// Custom data for [GeofenceRadius]. 10 | final dynamic data; 11 | 12 | /// The length of the radius in meters. 13 | /// The best result should be set between 100 and 150 meters in radius. 14 | /// If Wi-FI is available, it can be set up to 20~40m. 15 | final double length; 16 | 17 | /// The status of [GeofenceRadius]. 18 | GeofenceStatus _status = GeofenceStatus.EXIT; 19 | 20 | /// Returns the status of [GeofenceRadius]. 21 | GeofenceStatus get status => _status; 22 | 23 | /// The user activity when geofence status changes. 24 | Activity? _activity; 25 | 26 | /// Returns the user activity when geofence status changes. 27 | Activity? get activity => _activity; 28 | 29 | /// The passing speed when geofence status changes. 30 | double? _speed; 31 | 32 | /// Returns the passing speed when geofence status changes. 33 | double? get speed => _speed; 34 | 35 | /// The timestamp when geofence status changes. 36 | DateTime? _timestamp; 37 | 38 | /// Returns the timestamp when geofence status changes. 39 | DateTime? get timestamp => _timestamp; 40 | 41 | /// The remaining distance to the radius. 42 | double? _remainingDistance; 43 | 44 | /// Returns the remaining distance to the radius. 45 | double? get remainingDistance => _remainingDistance; 46 | 47 | /// Constructs an instance of [GeofenceRadius]. 48 | GeofenceRadius({required this.id, this.data, required this.length}) 49 | : assert(id.isNotEmpty), 50 | assert(length > 0.0); 51 | 52 | /// Returns the data fields of [GeofenceRadius] in JSON format. 53 | Map toJson() { 54 | return { 55 | 'id': id, 56 | 'data': data, 57 | 'length': length, 58 | 'status': _status, 59 | 'activity': _activity?.toJson(), 60 | 'speed': _speed, 61 | 'timestamp': _timestamp, 62 | 'remainingDistance': _remainingDistance 63 | }; 64 | } 65 | 66 | /// Update the remaining distance of [GeofenceRadius]. 67 | void updateRemainingDistance(double distance) { 68 | if (distance < 0.0) _remainingDistance = 0.0; 69 | _remainingDistance = distance; 70 | } 71 | 72 | /// Update the status of [GeofenceRadius]. 73 | /// Returns true if the status changes, false otherwise. 74 | bool updateStatus(GeofenceStatus status, Activity activity, double? speed, 75 | DateTime? timestamp) { 76 | if (status != _status) { 77 | _status = status; 78 | _activity = activity; 79 | _speed = speed; 80 | _timestamp = timestamp; 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/models/geofence_radius_sort_type.dart: -------------------------------------------------------------------------------- 1 | /// Defines the sort type of the geofence radius. 2 | /// If you have set multiple radius for one geofence, multiple radius can come in at the same time. 3 | /// At this time, you can control the order in which the radius comes in by referring to the radius meters. 4 | enum GeofenceRadiusSortType { 5 | /// Sort the meters in ascending order. 6 | ASC, 7 | 8 | /// Sort the meters in descending order. 9 | DESC 10 | } 11 | -------------------------------------------------------------------------------- /lib/models/geofence_service_options.dart: -------------------------------------------------------------------------------- 1 | import 'package:geofence_service/models/geofence_radius_sort_type.dart'; 2 | 3 | /// Options for [GeofenceService]. 4 | class GeofenceServiceOptions { 5 | /// The time interval in milliseconds to check the geofence status. 6 | /// The default is `5000`. 7 | int _interval = 5000; 8 | 9 | /// Geo-fencing error range in meters. 10 | /// The default is `100`. 11 | int _accuracy = 100; 12 | 13 | /// Sets the delay between [GeofenceStatus.ENTER] and [GeofenceStatus.DWELL] in milliseconds. 14 | /// The default is `300000`. 15 | int _loiteringDelayMs = 300000; 16 | 17 | /// Sets the status change delay in milliseconds. 18 | /// [GeofenceStatus.ENTER] and [GeofenceStatus.EXIT] events may be called frequently 19 | /// when the location is near the boundary of the geofence. Use this option to minimize event calls at this time. 20 | /// If the option value is too large, realtime geo-fencing is not possible, so use it carefully. 21 | /// The default is `10000`. 22 | int _statusChangeDelayMs = 10000; 23 | 24 | /// Whether to use the activity recognition API. 25 | /// The default is `true`. 26 | bool _useActivityRecognition = true; 27 | 28 | /// Whether to allow mock locations. 29 | /// The default is `false`. 30 | bool _allowMockLocations = false; 31 | 32 | /// Whether to show the developer log. 33 | /// If this value is set to true, logs for geofence service activities (start, stop, etc.) can be viewed. 34 | /// It does not work in release mode. 35 | /// The default is `false`. 36 | bool _printDevLog = false; 37 | 38 | /// Sets the sort type of the geofence radius. 39 | /// The default is `GeofenceRadiusSortType.DESC`. 40 | GeofenceRadiusSortType _geofenceRadiusSortType = GeofenceRadiusSortType.DESC; 41 | 42 | int get interval => _interval; 43 | set interval(int? value) => _interval = value ?? _interval; 44 | 45 | int get accuracy => _accuracy; 46 | set accuracy(int? value) => _accuracy = value ?? _accuracy; 47 | 48 | int get loiteringDelayMs => _loiteringDelayMs; 49 | set loiteringDelayMs(int? value) => 50 | _loiteringDelayMs = value ?? _loiteringDelayMs; 51 | 52 | int get statusChangeDelayMs => _statusChangeDelayMs; 53 | set statusChangeDelayMs(int? value) => 54 | _statusChangeDelayMs = value ?? _statusChangeDelayMs; 55 | 56 | bool get useActivityRecognition => _useActivityRecognition; 57 | set useActivityRecognition(bool? value) => 58 | _useActivityRecognition = value ?? _useActivityRecognition; 59 | 60 | bool get allowMockLocations => _allowMockLocations; 61 | set allowMockLocations(bool? value) => 62 | _allowMockLocations = value ?? _allowMockLocations; 63 | 64 | bool get printDevLog => _printDevLog; 65 | set printDevLog(bool? value) => _printDevLog = value ?? _printDevLog; 66 | 67 | GeofenceRadiusSortType get geofenceRadiusSortType => _geofenceRadiusSortType; 68 | set geofenceRadiusSortType(GeofenceRadiusSortType? value) => 69 | _geofenceRadiusSortType = value ?? _geofenceRadiusSortType; 70 | } 71 | -------------------------------------------------------------------------------- /lib/models/geofence_status.dart: -------------------------------------------------------------------------------- 1 | /// Defines the type of the geofence status. 2 | enum GeofenceStatus { 3 | /// Occurs when entering the geofence radius. 4 | ENTER, 5 | 6 | /// Occurs when exiting the geofence radius. 7 | EXIT, 8 | 9 | /// Occurs when the loitering delay elapses after entering the geofence area. 10 | DWELL 11 | } 12 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: geofence_service 2 | description: This plugin is a geofence service with activity recognition API. 3 | version: 6.0.0+1 4 | homepage: https://github.com/Dev-hwang/geofence_service 5 | 6 | environment: 7 | sdk: ">=2.18.0 <4.0.0" 8 | flutter: ">=3.3.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | fl_location: ^3.1.0 15 | flutter_activity_recognition: ^3.1.0 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter_lints: ^2.0.1 22 | 23 | flutter: 24 | plugin: 25 | platforms: 26 | android: 27 | package: com.pravera.geofence_service 28 | pluginClass: GeofenceServicePlugin 29 | ios: 30 | pluginClass: GeofenceServicePlugin 31 | -------------------------------------------------------------------------------- /test/geofence_service_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | --------------------------------------------------------------------------------