fix: Récupérer l'opération active depuis la table operations

- Corrige l'erreur SQL 'Unknown column fk_operation in users'
- L'opération active est récupérée depuis operations.chk_active = 1
- Jointure avec users pour filtrer par entité de l'admin créateur
- Query: SELECT o.id FROM operations o INNER JOIN users u ON u.fk_entite = o.fk_entite WHERE u.id = ? AND o.chk_active = 1
This commit is contained in:
2026-01-26 16:57:08 +01:00
parent c24a3afe6a
commit 0687900564
3040 changed files with 77204 additions and 1578 deletions

View File

@@ -1 +0,0 @@
/home/pierre/.pub-cache/hosted/pub.dev/geolocator_windows-0.2.5/

View File

@@ -0,0 +1,7 @@
# Below is a list of people and organizations that have contributed
# to the Flutter project. Names should be added to the list like so:
#
# Name/Organization <email address>
Alexandre Zollinger Chohfi <alzollin@microsoft.com>

View File

@@ -0,0 +1,44 @@
## 0.2.5
- Bump `flutter_lints` to version 5.0.0
## 0.2.4
* Updates dart SDk to `sdk: ^3.5.0`
* Fixed analyzzer issues example project
## 0.2.3
* Fixes crash under Windows when RequestAcess is called while the Geolocation Service is disabled. (#1455)
## 0.2.2
* Fixes crash under Windows when getCurrentPosition method is called. (#1240)
## 0.2.1
* Exposes altitude accuracy to the `Position` class.
## 0.2.0
* **BREAKING CHANGE:** Synchronizes the default values of `Position.altitude`, `Position.heading` and `Position.speed` with the other platforms and return 0.0 if the value is not known.
* Migrates the `target_compile_options` to use `/await:strict` to make the options compatible with C++ 20.
## 0.1.3
* Adds a `PositionStatus::Initializing` status consideration since it triggers the state change
if "Let apps access your location" toggle option is switched ON from OFF.
## 0.1.2
* Adds improved Flutter analysis and resolved warnings.
* Updates dependencies to current versions.
* Fixes plugin registration in `dart_plugin_registrant.dart`.
## 0.1.1
* Fixes repository URL of the package.
## 0.1.0
* Adds an initial implementation of Windows support for the geolocator plugin.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Baseflow
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,23 @@
# geolocator_windows
[![pub package](https://img.shields.io/pub/v/geolocator.svg)](https://pub.dartlang.org/packages/geolocator) ![Build status](https://github.com/Baseflow/flutter-geolocator/workflows/geolocator_windows/badge.svg?branch=master) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart)
The official Windows implementation of the [geolocator](https://pub.dev/packages/geolocator) plugin by [Baseflow](https://baseflow.com).
## Usage
Since version 8.1.0 of the [geolocator](https://pub.dev/packages/geolocator) plugin this is the endorsed Windows implementation. This means it will automatically be added to your dependencies when you depend on `geolocator: ^8.1.0` in your applications pubspec.yaml.
More detailed instructions on using the API can be found in the [README.md](../geolocator/README.md) of the [geolocator](https://pub.dev/packages/geolocator) package.
## Issues
Please file any issues, bugs or feature requests as an issue on our [GitHub](https://github.com/Baseflow/flutter-geolocator/issues) page. Commercial support is available, you can contact us at <hello@baseflow.com>.
## Want to contribute
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](../CONTRIBUTING.md) and send us your [pull request](https://github.com/Baseflow/flutter-geolocator/pulls).
## Author
This Geolocator plugin for Flutter is developed by [Baseflow](https://baseflow.com).

View File

@@ -0,0 +1,10 @@
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
# Ignore generated files
- '**/*.g.dart'
- 'lib/src/generated/*.dart'
linter:
rules:
- public_member_api_docs

View File

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

View File

@@ -0,0 +1,404 @@
import 'dart:async';
import 'package:baseflow_plugin_template/baseflow_plugin_template.dart';
import 'package:flutter/material.dart';
import 'package:geolocator_platform_interface/geolocator_platform_interface.dart';
/// Defines the main theme color.
final MaterialColor themeMaterialColor =
BaseflowPluginExample.createMaterialColor(
const Color.fromRGBO(48, 49, 60, 1));
void main() {
runApp(const GeolocatorWidget());
}
/// Example [Widget] showing the functionalities of the geolocator plugin
class GeolocatorWidget extends StatefulWidget {
/// Creates a new GeolocatorWidget.
const GeolocatorWidget({super.key});
/// Utility method to create a page with the Baseflow templating.
static ExamplePage createPage() {
return ExamplePage(
Icons.location_on, (context) => const GeolocatorWidget());
}
@override
State<GeolocatorWidget> createState() => _GeolocatorWidgetState();
}
class _GeolocatorWidgetState extends State<GeolocatorWidget> {
static const String _kLocationServicesDisabledMessage =
'Location services are disabled.';
static const String _kPermissionDeniedMessage = 'Permission denied.';
static const String _kPermissionDeniedForeverMessage =
'Permission denied forever.';
static const String _kPermissionGrantedMessage = 'Permission granted.';
final GeolocatorPlatform geolocatorWindows = GeolocatorPlatform.instance;
final List<_PositionItem> _positionItems = <_PositionItem>[];
StreamSubscription<Position>? _positionStreamSubscription;
StreamSubscription<ServiceStatus>? _serviceStatusStreamSubscription;
@override
void initState() {
super.initState();
_toggleServiceStatusStream();
}
PopupMenuButton _createActions() {
return PopupMenuButton(
elevation: 40,
onSelected: (value) async {
switch (value) {
case 1:
_getLocationAccuracy();
break;
case 2:
_openAppSettings();
break;
case 3:
_openLocationSettings();
break;
case 4:
setState(_positionItems.clear);
break;
default:
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 1,
child: Text('Get Location Accuracy'),
),
const PopupMenuItem(
value: 2,
child: Text('Open App Settings'),
),
const PopupMenuItem(
value: 3,
child: Text('Open Location Settings'),
),
const PopupMenuItem(
value: 4,
child: Text('Clear'),
)
]);
}
@override
Widget build(BuildContext context) {
const sizedBox = SizedBox(
height: 10,
);
return BaseflowPluginExample(
pluginName: 'Geolocator',
githubURL: 'https://github.com/Baseflow/flutter-geolocator',
pubDevURL: 'https://pub.dev/packages/geolocator',
appBarActions: [
_createActions()
],
pages: [
ExamplePage(
Icons.location_on,
(context) => Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
body: ListView.builder(
itemCount: _positionItems.length,
itemBuilder: (context, index) {
final positionItem = _positionItems[index];
if (positionItem.type == _PositionItemType.log) {
return ListTile(
title: Text(positionItem.displayValue,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
)),
);
} else {
return Card(
child: ListTile(
tileColor: themeMaterialColor,
title: Text(
positionItem.displayValue,
style: const TextStyle(color: Colors.white),
),
),
);
}
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: _toggleListening,
tooltip: (_positionStreamSubscription == null)
? 'Start position updates'
: _positionStreamSubscription!.isPaused
? 'Resume'
: 'Pause',
backgroundColor: _determineButtonColor(),
child: (_positionStreamSubscription == null ||
_positionStreamSubscription!.isPaused)
? const Icon(Icons.play_arrow)
: const Icon(Icons.pause),
),
sizedBox,
FloatingActionButton(
onPressed: _getCurrentPosition,
child: const Icon(Icons.my_location),
),
sizedBox,
FloatingActionButton(
onPressed: _getLastKnownPosition,
child: const Icon(Icons.bookmark),
),
],
),
),
)
]);
}
Future<void> _getCurrentPosition() async {
final hasPermission = await _handlePermission();
if (!hasPermission) {
return;
}
final position = await geolocatorWindows.getCurrentPosition();
_updatePositionList(
_PositionItemType.position,
'$position (Heading: ${position.heading}, Speed: ${position.speed})',
);
}
Future<bool> _handlePermission() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await geolocatorWindows.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
_updatePositionList(
_PositionItemType.log,
_kLocationServicesDisabledMessage,
);
return false;
}
permission = await geolocatorWindows.checkPermission();
if (permission == LocationPermission.denied) {
permission = await geolocatorWindows.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
_updatePositionList(
_PositionItemType.log,
_kPermissionDeniedMessage,
);
return false;
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
_updatePositionList(
_PositionItemType.log,
_kPermissionDeniedForeverMessage,
);
return false;
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
_updatePositionList(
_PositionItemType.log,
_kPermissionGrantedMessage,
);
return true;
}
void _updatePositionList(_PositionItemType type, String displayValue) {
_positionItems.add(_PositionItem(type, displayValue));
setState(() {});
}
bool _isListening() => !(_positionStreamSubscription == null ||
_positionStreamSubscription!.isPaused);
Color _determineButtonColor() {
return _isListening() ? Colors.green : Colors.red;
}
void _toggleServiceStatusStream() {
if (_serviceStatusStreamSubscription == null) {
final serviceStatusStream = geolocatorWindows.getServiceStatusStream();
_serviceStatusStreamSubscription =
serviceStatusStream.handleError((error) {
_serviceStatusStreamSubscription?.cancel();
_serviceStatusStreamSubscription = null;
}).listen((serviceStatus) {
String serviceStatusValue;
if (serviceStatus == ServiceStatus.enabled) {
serviceStatusValue = 'enabled';
} else {
serviceStatusValue = 'disabled';
}
_updatePositionList(
_PositionItemType.log,
'Location service has been $serviceStatusValue',
);
});
}
}
void _toggleListening() {
Exception? error;
if (_positionStreamSubscription == null) {
final Stream<Position> positionStream =
geolocatorWindows.getPositionStream();
_positionStreamSubscription = positionStream.handleError((e) {
_positionStreamSubscription?.cancel();
_positionStreamSubscription = null;
error = e;
}).listen((position) => _updatePositionList(
_PositionItemType.position,
position.toString(),
));
_positionStreamSubscription?.pause();
}
setState(() {
if (_positionStreamSubscription == null) {
return;
}
if (error != null) {
_updatePositionList(_PositionItemType.log, error.toString());
return;
}
String statusDisplayValue;
if (_positionStreamSubscription!.isPaused) {
_positionStreamSubscription!.resume();
statusDisplayValue = 'resumed';
} else {
_positionStreamSubscription!.pause();
statusDisplayValue = 'paused';
}
_updatePositionList(
_PositionItemType.log,
'Listening for position updates $statusDisplayValue',
);
});
}
@override
void dispose() {
if (_positionStreamSubscription != null) {
_positionStreamSubscription!.cancel();
_positionStreamSubscription = null;
}
super.dispose();
}
void _getLastKnownPosition() async {
final position = await geolocatorWindows.getLastKnownPosition();
if (position != null) {
_updatePositionList(
_PositionItemType.position,
position.toString(),
);
} else {
_updatePositionList(
_PositionItemType.log,
'No last known position available',
);
}
}
void _getLocationAccuracy() async {
final status = await geolocatorWindows.getLocationAccuracy();
_handleLocationAccuracyStatus(status);
}
void _handleLocationAccuracyStatus(LocationAccuracyStatus status) {
String locationAccuracyStatusValue;
if (status == LocationAccuracyStatus.precise) {
locationAccuracyStatusValue = 'Precise';
} else if (status == LocationAccuracyStatus.reduced) {
locationAccuracyStatusValue = 'Reduced';
} else {
locationAccuracyStatusValue = 'Unknown';
}
_updatePositionList(
_PositionItemType.log,
'$locationAccuracyStatusValue location accuracy granted.',
);
}
void _openAppSettings() async {
final opened = await geolocatorWindows.openAppSettings();
String displayValue;
if (opened) {
displayValue = 'Opened Application Settings.';
} else {
displayValue = 'Error opening Application Settings.';
}
_updatePositionList(
_PositionItemType.log,
displayValue,
);
}
void _openLocationSettings() async {
final opened = await geolocatorWindows.openLocationSettings();
String displayValue;
if (opened) {
displayValue = 'Opened Location Settings';
} else {
displayValue = 'Error opening Location Settings';
}
_updatePositionList(
_PositionItemType.log,
displayValue,
);
}
}
enum _PositionItemType {
log,
position,
}
class _PositionItem {
_PositionItem(this.type, this.displayValue);
final _PositionItemType type;
final String displayValue;
}

View File

@@ -0,0 +1,43 @@
name: geolocator_windows_example
description: Demonstrates how to use the geolocator_windows plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ^3.5.0
dependencies:
baseflow_plugin_template: ^2.1.2
flutter:
sdk: flutter
geolocator_platform_interface: ^4.0.7
geolocator_windows:
# When depending on this package from a real application you should use:
# geolocator_windows: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.1+1
# The following adds the URL Launcher plugin which is used by
# the demo application to open the links in the web browser.
url_launcher: ^6.0.18
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
uses-material-design: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,95 @@
cmake_minimum_required(VERSION 3.14)
project(example LANGUAGES CXX)
set(BINARY_NAME "example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)

View File

@@ -0,0 +1,103 @@
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

View File

@@ -0,0 +1,17 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <geolocator_windows/geolocator_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@@ -0,0 +1,25 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
geolocator_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
apply_standard_settings(${BINARY_NAME})
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@@ -0,0 +1,121 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "example" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "example" "\0"
VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "example.exe" "\0"
VALUE "ProductName", "example" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@@ -0,0 +1,61 @@
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}

View File

@@ -0,0 +1,33 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_

View File

@@ -0,0 +1,43 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"example", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

View File

@@ -0,0 +1,64 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) {
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
if (target_length == 0) {
return std::string();
}
std::string utf8_string;
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, utf8_string.data(),
target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}

View File

@@ -0,0 +1,19 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_

View File

@@ -0,0 +1,245 @@
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace {
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}

View File

@@ -0,0 +1,98 @@
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_

View File

@@ -0,0 +1,28 @@
name: geolocator_windows
description: Geolocation Windows plugin for Flutter. This plugin provides the Windows implementation for the geolocator.
repository: https://github.com/baseflow/flutter-geolocators
issue_tracker: https://github.com/baseflow/flutter-geolocator/issues?q=is%3Aissue+is%3Aopen
version: 0.2.5
environment:
sdk: ^3.5.0
flutter: ">=2.8.0"
flutter:
plugin:
implements: geolocator
platforms:
windows:
pluginClass: GeolocatorWindows
dependencies:
flutter:
sdk: flutter
geolocator_platform_interface: ^4.1.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ">=3.0.1 <5.0.0"
mockito: ^5.0.17
plugin_platform_interface: ^2.1.2

View File

@@ -0,0 +1,107 @@
cmake_minimum_required(VERSION 3.15)
set(PROJECT_NAME "geolocator_windows")
cmake_policy(VERSION 3.15...3.24)
set(CPPWINRT_VERSION "2.0.220418.1")
project(${PROJECT_NAME} LANGUAGES CXX)
include(FetchContent)
set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
list(APPEND PLUGIN_SOURCES
"geolocator_plugin.cpp"
"geolocator_plugin.h"
"geolocator_enums.h"
)
FetchContent_Declare(nuget
URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe"
URL_HASH SHA256=04eb6c4fe4213907e2773e1be1bbbd730e9a655a3c9c58387ce8d4a714a5b9e1
DOWNLOAD_NO_EXTRACT true
)
find_program(NUGET nuget)
if (NOT NUGET)
message("Nuget.exe not found, trying to download or use cached version.")
FetchContent_MakeAvailable(nuget)
set(NUGET ${nuget_SOURCE_DIR}/nuget.exe)
endif()
execute_process(COMMAND
${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE ret)
if (NOT ret EQUAL 0)
message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}")
endif()
set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe)
execute_process(COMMAND
${CPPWINRT} -input sdk -output include
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
RESULT_VARIABLE ret)
if (NOT ret EQUAL 0)
message(FATAL_ERROR "Failed to run cppwinrt.exe")
endif()
include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include)
add_library(${PLUGIN_NAME} SHARED
"include/geolocator_windows/geolocator_windows.h"
"geolocator_windows.cpp"
${PLUGIN_SOURCES}
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_20)
target_compile_options(${PLUGIN_NAME} PRIVATE /await:strict)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
# List of absolute paths to libraries that should be bundled with the plugin
set(file_chooser_bundled_libraries
""
PARENT_SCOPE
)
# === Tests ===
if (${include_${PROJECT_NAME}_tests})
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest
# instance rather than downloading for each plugin. This approach makes sense
# for a template, but not for a monorepo with many plugins.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's C API is not very useful for unit testing, so build the sources
# directly into the test binary rather than using the DLL.
add_executable(${TEST_RUNNER}
test/geolocator_windows_test.cpp
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
)
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif()

View File

@@ -0,0 +1,36 @@
namespace geolocator_plugin {
enum ErrorCode {
PermissionDefinitionsNotFound,
OperationCanceled,
UnknownError
};
enum LocationPermission {
Denied,
DeniedForever,
WhileInUse,
Always,
UnableToDetermine
};
enum LocationAccuracyStatus {
Reduced,
Precise
};
enum LocationAccuracy {
Lowest,
Low,
Medium,
High,
Best,
BestForNavigation
};
enum ServiceStatus {
Disabled,
Enabled
};
} // namespace geolocator_plugin

View File

@@ -0,0 +1,314 @@
#include "geolocator_plugin.h"
namespace geolocator_plugin {
using namespace flutter;
using namespace winrt;
using namespace winrt::Windows::Devices::Geolocation;
namespace {
template<typename T>
T GetArgument(const std::string arg, const EncodableValue* args, T fallback) {
T result {fallback};
const auto* arguments = std::get_if<EncodableMap>(args);
if (arguments) {
auto result_it = arguments->find(EncodableValue(arg));
if (result_it != arguments->end()) {
result = std::get<T>(result_it->second);
}
}
return result;
}
std::string ErrorCodeToString(ErrorCode errorCode) {
switch (errorCode) {
case ErrorCode::PermissionDefinitionsNotFound:
return "PERMISSION_DEFINITIONS_NOT_FOUND";
case ErrorCode::OperationCanceled:
return "OPERATION_CANCELED";
case ErrorCode::UnknownError:
return "UNKNOWN_ERROR";
default:
throw std::logic_error("unexcepted value" + static_cast<int>(errorCode));
}
}
} // namespace
// static
void GeolocatorPlugin::RegisterWithRegistrar(
PluginRegistrar* registrar) {
auto channel = std::make_unique<MethodChannel<>>(
registrar->messenger(), "flutter.baseflow.com/geolocator",
&StandardMethodCodec::GetInstance());
auto geolocatorStreamChannel = std::make_unique<EventChannel<EncodableValue>>(
registrar->messenger(), "flutter.baseflow.com/geolocator_updates",
&StandardMethodCodec::GetInstance());
auto geolocatorServiceStreamChannel = std::make_unique<EventChannel<EncodableValue>>(
registrar->messenger(), "flutter.baseflow.com/geolocator_service_updates",
&StandardMethodCodec::GetInstance());
std::unique_ptr<GeolocatorPlugin> plugin = std::make_unique<GeolocatorPlugin>();
auto geolocatorHandler = std::make_unique<
StreamHandlerFunctions<EncodableValue>>(
[plugin_pointer = plugin.get()](
const EncodableValue* arguments,
std::unique_ptr<EventSink<EncodableValue>>&& events)
-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
return plugin_pointer->OnListen(arguments, std::move(events));
},
[plugin_pointer = plugin.get()](const EncodableValue* arguments)
-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
return plugin_pointer->OnCancel(arguments);
});
geolocatorStreamChannel->SetStreamHandler(std::move(geolocatorHandler));
auto geolocatorServiceHandler = std::make_unique<
StreamHandlerFunctions<EncodableValue>>(
[plugin_pointer = plugin.get()](
const EncodableValue* arguments,
std::unique_ptr<EventSink<EncodableValue>>&& events)
-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
return plugin_pointer->OnServiceListen(arguments, std::move(events));
},
[plugin_pointer = plugin.get()](const EncodableValue* arguments)
-> std::unique_ptr<StreamHandlerError<EncodableValue>> {
return plugin_pointer->OnServiceCancel(arguments);
});
geolocatorServiceStreamChannel->SetStreamHandler(std::move(geolocatorServiceHandler));
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto& call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
GeolocatorPlugin::GeolocatorPlugin(){}
GeolocatorPlugin::~GeolocatorPlugin() = default;
void GeolocatorPlugin::HandleMethodCall(
const MethodCall<>& method_call,
std::unique_ptr<MethodResult<>> result) {
auto methodName = method_call.method_name();
if (methodName.compare("checkPermission") == 0) {
OnCheckPermission(std::move(result));
} else if (methodName.compare("isLocationServiceEnabled") == 0) {
OnIsLocationServiceEnabled(std::move(result));
} else if (methodName.compare("requestPermission") == 0) {
OnRequestPermission(std::move(result));
} else if (methodName.compare("getLastKnownPosition") == 0) {
OnGetLastKnownPosition(method_call, std::move(result));
} else if (methodName.compare("getLocationAccuracy") == 0) {
GetLocationAccuracy(std::move(result));
} else if (methodName.compare("getCurrentPosition") == 0) {
OnGetCurrentPosition(method_call, std::move(result));
} else if (methodName.compare("openAppSettings") == 0
|| methodName.compare("openLocationSettings") == 0) {
OpenPrivacyLocationSettings(std::move(result));
} else {
result->NotImplemented();
}
}
void GeolocatorPlugin::OpenPrivacyLocationSettings(std::unique_ptr<MethodResult<>> result) {
std::wstring url {L"ms-settings:privacy-location"};
int status = static_cast<int>(reinterpret_cast<INT_PTR>(
::ShellExecuteW(nullptr, TEXT("open"), url.c_str(),
nullptr, nullptr, SW_SHOWNORMAL)));
// Per ::ShellExecuteW documentation, anything >32 indicates success.
result->Success(status > 32);
}
winrt::fire_and_forget GeolocatorPlugin::RequestAccessAsync(std::unique_ptr<MethodResult<>> result) {
try {
auto access = co_await geolocator.RequestAccessAsync();
if(access == GeolocationAccessStatus::Allowed)
result->Success(EncodableValue((int)LocationPermission::WhileInUse));
else if(access == GeolocationAccessStatus::Denied)
result->Success(EncodableValue((int)LocationPermission::Denied));
else if(access == GeolocationAccessStatus::Unspecified)
result->Success(EncodableValue((int)LocationPermission::DeniedForever));
}
catch(const std::exception& ex) {
result->Error(ErrorCodeToString(ErrorCode::PermissionDefinitionsNotFound), ex.what());
}
catch (...) {
result->Error(ErrorCodeToString(ErrorCode::UnknownError), "RequestAccess failed. Check the Geolocation Service is running.");
}
}
void GeolocatorPlugin::OnCheckPermission(std::unique_ptr<MethodResult<>> result) {
RequestAccessAsync(std::move(result));
}
bool isLocationStatusValid (const PositionStatus& status) {
return status != PositionStatus::Disabled
&& status != PositionStatus::NotAvailable;
}
void GeolocatorPlugin::OnIsLocationServiceEnabled(std::unique_ptr<MethodResult<>> result) {
result->Success(EncodableValue(isLocationStatusValid(geolocator.LocationStatus())));
}
void GeolocatorPlugin::OnRequestPermission(std::unique_ptr<MethodResult<>> result) {
RequestAccessAsync(std::move(result));
}
winrt::fire_and_forget GeolocatorPlugin::OnGetLastKnownPosition(const MethodCall<>& method_call,
std::unique_ptr<MethodResult<>> result) {
try {
auto location = co_await geolocator.GetGeopositionAsync(std::chrono::hours(1), std::chrono::milliseconds::zero());
result->Success(LocationToEncodableMap(location));
}
catch (hresult_canceled const& error) {
result->Error(ErrorCodeToString(ErrorCode::OperationCanceled),
to_string(error.message()));
}
catch (hresult_error const& error) {
result->Error(ErrorCodeToString(ErrorCode::UnknownError),
to_string(error.message()));
}
}
void GeolocatorPlugin::GetLocationAccuracy(std::unique_ptr<MethodResult<>> result) {
result->Success(EncodableValue((int)(
geolocator.DesiredAccuracy() == PositionAccuracy::High
? LocationAccuracyStatus::Precise
: LocationAccuracyStatus::Reduced)));
}
winrt::fire_and_forget GeolocatorPlugin::OnGetCurrentPosition(const MethodCall<>& method_call,
std::unique_ptr<MethodResult<>> result) {
try {
auto location = co_await geolocator.GetGeopositionAsync();
result->Success(LocationToEncodableMap(location));
}
catch (hresult_canceled const& error) {
result->Error(ErrorCodeToString(ErrorCode::OperationCanceled),
to_string(error.message()));
}
catch (hresult_error const& error) {
result->Error(ErrorCodeToString(ErrorCode::UnknownError),
to_string(error.message()));
}
}
std::unique_ptr<StreamHandlerError<EncodableValue>> GeolocatorPlugin::OnListen(
const EncodableValue* arguments,
std::unique_ptr<EventSink<EncodableValue>>&& events){
auto accuracy = LocationAccuracy::Best;
long distanceFilter = 0;
long timeInterval = 1;
if (arguments != nullptr) {
accuracy = (LocationAccuracy)GetArgument<int>("accuracy", arguments, accuracy);
distanceFilter = GetArgument<int>("distanceFilter", arguments, distanceFilter);
timeInterval = GetArgument<int>("timeInterval", arguments, timeInterval);
}
m_positionChangedRevoker.revoke();
geolocator.DesiredAccuracy(accuracy < LocationAccuracy::Medium
? PositionAccuracy::Default
: PositionAccuracy::High);
geolocator.MovementThreshold(distanceFilter);
geolocator.ReportInterval(timeInterval);
m_positionChangedRevoker = geolocator.PositionChanged(winrt::auto_revoke,
[events = std::move(events)](Geolocator const& geolocator, PositionChangedEventArgs e)
{
events->Success(LocationToEncodableMap(e.Position()));
});
return nullptr;
}
std::unique_ptr<StreamHandlerError<EncodableValue>> GeolocatorPlugin::OnCancel(const EncodableValue* arguments){
m_positionChangedRevoker.revoke();
return nullptr;
}
std::unique_ptr<StreamHandlerError<EncodableValue>> GeolocatorPlugin::OnServiceListen(
const EncodableValue* arguments,
std::unique_ptr<EventSink<EncodableValue>>&& events){
m_currentStatus = statusGeolocator.LocationStatus();
if (m_currentStatus != PositionStatus::Disabled) {
m_currentStatus = PositionStatus::Ready;
}
m_statusChangedRevoker = statusGeolocator.StatusChanged(winrt::auto_revoke,
[events = std::move(events), this](Geolocator const& geolocator, StatusChangedEventArgs e)
{
if (m_currentStatus != PositionStatus::Disabled && e.Status() == PositionStatus::Disabled
|| m_currentStatus != PositionStatus::Ready && e.Status() == PositionStatus::Ready
|| m_currentStatus != PositionStatus::Initializing && e.Status() == PositionStatus::Initializing ) {
auto status = e.Status() == PositionStatus::NotAvailable
? ServiceStatus::Disabled
: ServiceStatus::Enabled;
events->Success(EncodableValue((int)status));
m_currentStatus = e.Status();
}
});
return nullptr;
}
std::unique_ptr<StreamHandlerError<EncodableValue>> GeolocatorPlugin::OnServiceCancel(const EncodableValue* arguments){
m_statusChangedRevoker.revoke();
return nullptr;
}
EncodableMap GeolocatorPlugin::LocationToEncodableMap(Geoposition const& location) {
if (location == nullptr) {
return EncodableMap();
}
auto position = EncodableMap();
position.insert(std::make_pair(EncodableValue("latitude"), EncodableValue(location.Coordinate().Latitude())));
position.insert(std::make_pair(EncodableValue("longitude"), EncodableValue(location.Coordinate().Longitude())));
position.insert(std::make_pair(EncodableValue("timestamp"), EncodableValue(clock::to_time_t(location.Coordinate().Timestamp()))));
double altitude = location.Coordinate().Altitude() != nullptr && !std::isnan(location.Coordinate().Altitude().GetDouble())
? location.Coordinate().Altitude().GetDouble()
: 0;
position.insert(std::make_pair(EncodableValue("altitude"), EncodableValue(altitude)));
double altitudeAccuracy = location.Coordinate().AltitudeAccuracy() != nullptr && !std::isnan(location.Coordinate().AltitudeAccuracy().GetDouble())
? location.Coordinate().AltitudeAccuracy().GetDouble()
: 0;
position.insert(std::make_pair(EncodableValue("altitude_accuracy"), EncodableValue(altitudeAccuracy)));
position.insert(std::make_pair(EncodableValue("accuracy"), EncodableValue(location.Coordinate().Accuracy())));
double heading = location.Coordinate().Heading() != nullptr && !std::isnan(location.Coordinate().Heading().GetDouble())
? location.Coordinate().Heading().GetDouble()
: 0;
position.insert(std::make_pair(EncodableValue("heading"), EncodableValue(heading)));
double speed = location.Coordinate().Speed() != nullptr && !std::isnan(location.Coordinate().Speed().GetDouble())
? location.Coordinate().Speed().GetDouble()
: 0;
position.insert(std::make_pair(EncodableValue("speed"), EncodableValue(speed)));
position.insert(std::make_pair(EncodableValue("is_mocked"), EncodableValue(false)));
return position;
}
} // namespace geolocator_plugin

View File

@@ -0,0 +1,74 @@
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/event_channel.h>
#include <flutter/event_stream_handler.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/encodable_value.h>
#include <windows.h>
#include <memory>
#include <optional>
#include <sstream>
#include <map>
#include <string>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Devices.Geolocation.h>
#include "geolocator_enums.h"
namespace geolocator_plugin {
class GeolocatorPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar);
GeolocatorPlugin();
virtual ~GeolocatorPlugin();
// Disallow copy and move.
GeolocatorPlugin(const GeolocatorPlugin&) = delete;
GeolocatorPlugin& operator=(const GeolocatorPlugin&) = delete;
// Called when a method is called on the plugin channel.
void HandleMethodCall(const flutter::MethodCall<>&,
std::unique_ptr<flutter::MethodResult<>>);
private:
void OnCheckPermission(std::unique_ptr<flutter::MethodResult<>>);
void OnIsLocationServiceEnabled(std::unique_ptr<flutter::MethodResult<>>);
void OnRequestPermission(std::unique_ptr<flutter::MethodResult<>>);
winrt::fire_and_forget OnGetLastKnownPosition(const flutter::MethodCall<>&,
std::unique_ptr<flutter::MethodResult<>>);
void GetLocationAccuracy(std::unique_ptr<flutter::MethodResult<>>);
winrt::fire_and_forget OnGetCurrentPosition(const flutter::MethodCall<>&,
std::unique_ptr<flutter::MethodResult<>>);
winrt::fire_and_forget RequestAccessAsync(std::unique_ptr<flutter::MethodResult<>>);
void OpenPrivacyLocationSettings(std::unique_ptr<flutter::MethodResult<>>);
winrt::Windows::Devices::Geolocation::Geolocator::PositionChanged_revoker m_positionChangedRevoker;
std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> OnListen(
const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events);
std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> OnCancel(
const flutter::EncodableValue* arguments);
winrt::Windows::Devices::Geolocation::Geolocator::StatusChanged_revoker m_statusChangedRevoker;
std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> OnServiceListen(
const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events);
std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> OnServiceCancel(
const flutter::EncodableValue* arguments);
static flutter::EncodableMap LocationToEncodableMap(winrt::Windows::Devices::Geolocation::Geoposition const&);
winrt::Windows::Devices::Geolocation::Geolocator geolocator;
winrt::Windows::Devices::Geolocation::Geolocator statusGeolocator;
winrt::Windows::Devices::Geolocation::PositionStatus m_currentStatus;
};
} // namespace geolocator_plugin

View File

@@ -0,0 +1,12 @@
#include "include/geolocator_windows/geolocator_windows.h"
#include <flutter/plugin_registrar_windows.h>
#include "geolocator_plugin.h"
void GeolocatorWindowsRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
geolocator_plugin::GeolocatorPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}

View File

@@ -0,0 +1,23 @@
#ifndef PACKAGES_GEOLOCATOR_GEOLOCATOR_WINDOWS_WINDOWS_INCLUDE_GEOLOCATOR_WINDOWS_GEOLOCATOR_plugin_H_
#define PACKAGES_GEOLOCATOR_GEOLOCATOR_WINDOWS_WINDOWS_INCLUDE_GEOLOCATOR_WINDOWS_GEOLOCATOR_plugin_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
FLUTTER_PLUGIN_EXPORT void GeolocatorWindowsRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // PACKAGES_GEOLOCATOR_GEOLOCATOR_WINDOWS_WINDOWS_INCLUDE_GEOLOCATOR_WINDOWS_GEOLOCATOR_plugin_H_