Add url_launcher_windows (#3015)
Adds a federated Windows implementation of url_launcher (not yet endorsed).
Since this is the first C++ plugin, this also adds a .clang-format file to the repo root for formatting C++ plugins.
Part of flutter/flutter#41721 (will need follow-up to endorse it in url_launcher)
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..f6cb8ad
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1 @@
+BasedOnStyle: Google
diff --git a/packages/url_launcher/url_launcher_windows/.gitignore b/packages/url_launcher/url_launcher_windows/.gitignore
new file mode 100644
index 0000000..53e92cc
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/.gitignore
@@ -0,0 +1,3 @@
+.packages
+.flutter-plugins
+pubspec.lock
diff --git a/packages/url_launcher/url_launcher_windows/.metadata b/packages/url_launcher/url_launcher_windows/.metadata
new file mode 100644
index 0000000..720a459
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 6d1c244b79f3a2747281f718297ce248bd5ad099
+ channel: master
+
+project_type: plugin
diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
new file mode 100644
index 0000000..dd3395a
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* Initial Windows implementation of `url_launcher`.
diff --git a/packages/url_launcher/url_launcher_windows/LICENSE b/packages/url_launcher/url_launcher_windows/LICENSE
new file mode 100644
index 0000000..5075698
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/LICENSE
@@ -0,0 +1,25 @@
+Copyright 2019 The Chromium Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/url_launcher/url_launcher_windows/README.md b/packages/url_launcher/url_launcher_windows/README.md
new file mode 100644
index 0000000..685adc5
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/README.md
@@ -0,0 +1,31 @@
+# url_launcher_windows
+
+The Windows implementation of [`url_launcher`][1].
+
+## Backward compatible 1.0.0 version is coming
+The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.0.y+z`. If you use
+url_launcher_windows directly, rather than as an implementation detail
+of `url_launcher`, please use `url_launcher_windows: '>=0.0.y+x <2.0.0'`
+as your dependency constraint to allow a smoother ecosystem migration.
+For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
+
+## Usage
+
+### Import the package
+
+This package has not yet been endorsed. Once it is you only need to add
+`url_launcher` as a dependency in your `pubspec.yaml`, but for now you
+need to include both `url_launcher` and `url_launcher_windows`.
+
+This is what the above means to your `pubspec.yaml`:
+
+```yaml
+...
+dependencies:
+ ...
+ url_launcher: ^5.5.3
+ url_launcher_windows: ^0.0.1
+ ...
+```
+
+[1]: ../url_launcher/url_launcher
diff --git a/packages/url_launcher/url_launcher_windows/example/.gitignore b/packages/url_launcher/url_launcher_windows/example/.gitignore
new file mode 100644
index 0000000..9d532b1
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/.gitignore
@@ -0,0 +1,41 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
diff --git a/packages/url_launcher/url_launcher_windows/example/.metadata b/packages/url_launcher/url_launcher_windows/example/.metadata
new file mode 100644
index 0000000..82cce8b
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 6d1c244b79f3a2747281f718297ce248bd5ad099
+ channel: master
+
+project_type: app
diff --git a/packages/url_launcher/url_launcher_windows/example/README.md b/packages/url_launcher/url_launcher_windows/example/README.md
new file mode 100644
index 0000000..e444852
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/README.md
@@ -0,0 +1,3 @@
+# url_launcher_windows_example
+
+Demonstrates the url_launcher_windows plugin.
diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart
new file mode 100644
index 0000000..db59af1
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart
@@ -0,0 +1,93 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: public_member_api_docs
+
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
+
+void main() {
+ runApp(MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'URL Launcher',
+ theme: ThemeData(
+ primarySwatch: Colors.blue,
+ ),
+ home: MyHomePage(title: 'URL Launcher'),
+ );
+ }
+}
+
+class MyHomePage extends StatefulWidget {
+ MyHomePage({Key key, this.title}) : super(key: key);
+ final String title;
+
+ @override
+ _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+ Future<void> _launched;
+
+ Future<void> _launchInBrowser(String url) async {
+ if (await UrlLauncherPlatform.instance.canLaunch(url)) {
+ await UrlLauncherPlatform.instance.launch(
+ url,
+ useSafariVC: false,
+ useWebView: false,
+ enableJavaScript: false,
+ enableDomStorage: false,
+ universalLinksOnly: false,
+ headers: <String, String>{},
+ );
+ } else {
+ throw 'Could not launch $url';
+ }
+ }
+
+ Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
+ if (snapshot.hasError) {
+ return Text('Error: ${snapshot.error}');
+ } else {
+ return const Text('');
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ const String toLaunch = 'https://www.cylog.org/headers/';
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.title),
+ ),
+ body: ListView(
+ children: <Widget>[
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: <Widget>[
+ const Padding(
+ padding: EdgeInsets.all(16.0),
+ child: Text(toLaunch),
+ ),
+ RaisedButton(
+ onPressed: () => setState(() {
+ _launched = _launchInBrowser(toLaunch);
+ }),
+ child: const Text('Launch in browser'),
+ ),
+ const Padding(padding: EdgeInsets.all(16.0)),
+ FutureBuilder<void>(future: _launched, builder: _launchStatus),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
new file mode 100644
index 0000000..8d6c220
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml
@@ -0,0 +1,19 @@
+name: url_launcher_example
+description: Demonstrates the Windows implementation of the url_launcher plugin.
+
+dependencies:
+ flutter:
+ sdk: flutter
+ url_launcher_platform_interface: any
+ url_launcher_windows:
+ path: ../
+
+dev_dependencies:
+ integration_test:
+ path: ../../../integration_test
+ flutter_driver:
+ sdk: flutter
+ pedantic: ^1.8.0
+
+flutter:
+ uses-material-design: true
diff --git a/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e.dart b/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e.dart
new file mode 100644
index 0000000..7a33e00
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e.dart
@@ -0,0 +1,20 @@
+// Copyright 2019, the Chromium project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ test('canLaunch', () async {
+ UrlLauncherPlatform launcher = UrlLauncherPlatform.instance;
+
+ expect(await launcher.canLaunch('randomstring'), false);
+
+ // Generally all devices should have some default browser.
+ expect(await launcher.canLaunch('http://flutter.dev'), true);
+ });
+}
diff --git a/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e_test.dart b/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e_test.dart
new file mode 100644
index 0000000..7a2c213
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/example/test_driver/url_launcher_e2e_test.dart
@@ -0,0 +1,17 @@
+// Copyright 2019, the Chromium project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'package:flutter_driver/flutter_driver.dart';
+
+Future<void> main() async {
+ final FlutterDriver driver = await FlutterDriver.connect();
+ final String data =
+ await driver.requestData(null, timeout: const Duration(minutes: 1));
+ await driver.close();
+ final Map<String, dynamic> result = jsonDecode(data);
+ exit(result['result'] == 'true' ? 0 : 1);
+}
diff --git a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart
new file mode 100644
index 0000000..83435f9
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart
@@ -0,0 +1,3 @@
+// The url_launcher_platform_interface defaults to MethodChannelUrlLauncher
+// as its instance, which is all the Windows implementation needs. This file
+// is here to silence warnings when publishing to pub.
diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml
new file mode 100644
index 0000000..8a0beb7
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml
@@ -0,0 +1,21 @@
+name: url_launcher_windows
+description: Windows implementation of the url_launcher plugin.
+# 0.0.y+z is compatible with 1.0.0, if you land a breaking change bump
+# the version to 2.0.0.
+# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0
+version: 0.0.1
+homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_windows
+
+flutter:
+ plugin:
+ platforms:
+ windows:
+ pluginClass: UrlLauncherPlugin
+
+environment:
+ sdk: ">=2.1.0 <3.0.0"
+ flutter: ">=1.12.8 <2.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
diff --git a/packages/url_launcher/url_launcher_windows/windows/.gitignore b/packages/url_launcher/url_launcher_windows/windows/.gitignore
new file mode 100644
index 0000000..b3eb2be
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt
new file mode 100644
index 0000000..57d87e3
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.10)
+set(PROJECT_NAME "url_launcher_windows")
+project(${PROJECT_NAME} LANGUAGES CXX)
+
+set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
+
+add_library(${PLUGIN_NAME} SHARED
+ "url_launcher_plugin.cpp"
+)
+apply_standard_settings(${PLUGIN_NAME})
+set_target_properties(${PLUGIN_NAME} PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+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
+)
diff --git a/packages/url_launcher/url_launcher_windows/windows/include/url_launcher_windows/url_launcher_plugin.h b/packages/url_launcher/url_launcher_windows/windows/include/url_launcher_windows/url_launcher_plugin.h
new file mode 100644
index 0000000..cdd018a
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/include/url_launcher_windows/url_launcher_plugin.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef PACKAGES_URL_LAUNCHER_URL_LAUNCHER_WINDOWS_WINDOWS_INCLUDE_URL_LAUNCHER_WINDOWS_URL_LAUNCHER_PLUGIN_H_
+#define PACKAGES_URL_LAUNCHER_URL_LAUNCHER_WINDOWS_WINDOWS_INCLUDE_URL_LAUNCHER_WINDOWS_URL_LAUNCHER_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 UrlLauncherPluginRegisterWithRegistrar(
+ FlutterDesktopPluginRegistrarRef registrar);
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif // PACKAGES_URL_LAUNCHER_URL_LAUNCHER_WINDOWS_WINDOWS_INCLUDE_URL_LAUNCHER_WINDOWS_URL_LAUNCHER_PLUGIN_H_
diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp
new file mode 100644
index 0000000..dfb4063
--- /dev/null
+++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp
@@ -0,0 +1,148 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "include/url_launcher_windows/url_launcher_plugin.h"
+
+#include <flutter/method_channel.h>
+#include <flutter/plugin_registrar_windows.h>
+#include <flutter/standard_method_codec.h>
+#include <windows.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace {
+
+using flutter::EncodableMap;
+using flutter::EncodableValue;
+
+// Converts the given UTF-8 string to UTF-16.
+std::wstring Utf16FromUtf8(const std::string& utf8_string) {
+ if (utf8_string.empty()) {
+ return std::wstring();
+ }
+ int target_length =
+ ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
+ static_cast<int>(utf8_string.length()), nullptr, 0);
+ if (target_length == 0) {
+ return std::wstring();
+ }
+ std::wstring utf16_string;
+ utf16_string.resize(target_length);
+ int converted_length =
+ ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(),
+ static_cast<int>(utf8_string.length()),
+ utf16_string.data(), target_length);
+ if (converted_length == 0) {
+ return std::wstring();
+ }
+ return utf16_string;
+}
+
+// Returns the URL argument from |method_call| if it is present, otherwise
+// returns an empty string.
+std::string GetUrlArgument(const flutter::MethodCall<>& method_call) {
+ std::string url;
+ const auto* arguments = std::get_if<EncodableMap>(method_call.arguments());
+ if (arguments) {
+ auto url_it = arguments->find(EncodableValue("url"));
+ if (url_it != arguments->end()) {
+ url = std::get<std::string>(url_it->second);
+ }
+ }
+ return url;
+}
+
+class UrlLauncherPlugin : public flutter::Plugin {
+ public:
+ static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar);
+
+ virtual ~UrlLauncherPlugin();
+
+ private:
+ UrlLauncherPlugin();
+
+ // Called when a method is called on plugin channel;
+ void HandleMethodCall(const flutter::MethodCall<>& method_call,
+ std::unique_ptr<flutter::MethodResult<>> result);
+};
+
+// static
+void UrlLauncherPlugin::RegisterWithRegistrar(
+ flutter::PluginRegistrar* registrar) {
+ auto channel = std::make_unique<flutter::MethodChannel<>>(
+ registrar->messenger(), "plugins.flutter.io/url_launcher",
+ &flutter::StandardMethodCodec::GetInstance());
+
+ // Uses new instead of make_unique due to private constructor.
+ std::unique_ptr<UrlLauncherPlugin> plugin(new UrlLauncherPlugin());
+
+ channel->SetMethodCallHandler(
+ [plugin_pointer = plugin.get()](const auto& call, auto result) {
+ plugin_pointer->HandleMethodCall(call, std::move(result));
+ });
+
+ registrar->AddPlugin(std::move(plugin));
+}
+
+UrlLauncherPlugin::UrlLauncherPlugin() = default;
+
+UrlLauncherPlugin::~UrlLauncherPlugin() = default;
+
+void UrlLauncherPlugin::HandleMethodCall(
+ const flutter::MethodCall<>& method_call,
+ std::unique_ptr<flutter::MethodResult<>> result) {
+ if (method_call.method_name().compare("launch") == 0) {
+ std::string url = GetUrlArgument(method_call);
+ if (url.empty()) {
+ result->Error("argument_error", "No URL provided");
+ return;
+ }
+ std::wstring url_wide = Utf16FromUtf8(url);
+
+ int status = static_cast<int>(reinterpret_cast<INT_PTR>(
+ ::ShellExecute(nullptr, TEXT("open"), url_wide.c_str(), nullptr,
+ nullptr, SW_SHOWNORMAL)));
+
+ if (status <= 32) {
+ std::ostringstream error_message;
+ error_message << "Failed to open " << url << ": ShellExecute error code "
+ << status;
+ result->Error("open_error", error_message.str());
+ return;
+ }
+ result->Success(EncodableValue(true));
+ } else if (method_call.method_name().compare("canLaunch") == 0) {
+ std::string url = GetUrlArgument(method_call);
+ if (url.empty()) {
+ result->Error("argument_error", "No URL provided");
+ return;
+ }
+
+ bool can_launch = false;
+ size_t separator_location = url.find(":");
+ if (separator_location != std::string::npos) {
+ std::wstring scheme = Utf16FromUtf8(url.substr(0, separator_location));
+ HKEY key = nullptr;
+ if (::RegOpenKeyEx(HKEY_CLASSES_ROOT, scheme.c_str(), 0, KEY_QUERY_VALUE,
+ &key) == ERROR_SUCCESS) {
+ can_launch = ::RegQueryValueEx(key, L"URL Protocol", nullptr, nullptr,
+ nullptr, nullptr) == ERROR_SUCCESS;
+ ::RegCloseKey(key);
+ }
+ }
+ result->Success(EncodableValue(can_launch));
+ } else {
+ result->NotImplemented();
+ }
+}
+
+} // namespace
+
+void UrlLauncherPluginRegisterWithRegistrar(
+ FlutterDesktopPluginRegistrarRef registrar) {
+ UrlLauncherPlugin::RegisterWithRegistrar(
+ flutter::PluginRegistrarManager::GetInstance()
+ ->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
+}