feat(desktop): implement webview based login

This commit is contained in:
Kingkor Roy Tirtho 2024-07-05 11:21:32 +06:00
parent 3bdc46da4d
commit 15bd58a955
15 changed files with 73 additions and 293 deletions

View File

@ -34,12 +34,9 @@ import 'package:spotube/pages/stats/streams/streams.dart';
import 'package:spotube/pages/track/track.dart'; import 'package:spotube/pages/track/track.dart';
import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/components/spotube_page_route.dart'; import 'package:spotube/components/spotube_page_route.dart';
import 'package:spotube/pages/artist/artist.dart'; import 'package:spotube/pages/artist/artist.dart';
import 'package:spotube/pages/library/library.dart'; import 'package:spotube/pages/library/library.dart';
import 'package:spotube/pages/desktop_login/login_tutorial.dart';
import 'package:spotube/pages/desktop_login/desktop_login.dart';
import 'package:spotube/pages/lyrics/lyrics.dart'; import 'package:spotube/pages/lyrics/lyrics.dart';
import 'package:spotube/pages/root/root_app.dart'; import 'package:spotube/pages/root/root_app.dart';
import 'package:spotube/pages/settings/settings.dart'; import 'package:spotube/pages/settings/settings.dart';
@ -313,16 +310,8 @@ final routerProvider = Provider((ref) {
path: "/login", path: "/login",
name: WebViewLogin.name, name: WebViewLogin.name,
parentNavigatorKey: rootNavigatorKey, parentNavigatorKey: rootNavigatorKey,
pageBuilder: (context, state) => SpotubePage(
child: kIsMobile ? const WebViewLogin() : const DesktopLoginPage(),
),
),
GoRoute(
path: "/login-tutorial",
name: LoginTutorial.name,
parentNavigatorKey: rootNavigatorKey,
pageBuilder: (context, state) => const SpotubePage( pageBuilder: (context, state) => const SpotubePage(
child: LoginTutorial(), child: WebViewLogin(),
), ),
), ),
GoRoute( GoRoute(

View File

@ -65,7 +65,7 @@ Future<void> main(List<String> rawArgs) async {
await FlutterDisplayMode.setHighRefreshRate(); await FlutterDisplayMode.setHighRefreshRate();
} }
if (kIsDesktop) { if (kIsDesktop && !kIsMacOS) {
await windowManager.setPreventClose(true); await windowManager.setPreventClose(true);
} }

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/authentication/authentication.dart';
class TokenLoginForm extends HookConsumerWidget {
final void Function()? onDone;
const TokenLoginForm({
super.key,
this.onDone,
});
@override
Widget build(BuildContext context, ref) {
final authenticationNotifier = ref.watch(authenticationProvider.notifier);
final directCodeController = useTextEditingController();
final isLoading = useState(false);
return ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 400,
),
child: Column(
children: [
TextField(
controller: directCodeController,
decoration: InputDecoration(
hintText: context.l10n.spotify_cookie("\"sp_dc\""),
labelText: context.l10n.cookie_name_cookie("sp_dc"),
),
keyboardType: TextInputType.visiblePassword,
),
const SizedBox(height: 10),
FilledButton(
onPressed: isLoading.value
? null
: () async {
try {
isLoading.value = true;
if (directCodeController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.l10n.fill_in_all_fields),
behavior: SnackBarBehavior.floating,
),
);
return;
}
final cookieHeader =
"sp_dc=${directCodeController.text.trim()}";
await authenticationNotifier.login(cookieHeader);
if (context.mounted) {
onDone?.call();
}
} finally {
isLoading.value = false;
}
},
child: Text(context.l10n.submit),
)
],
),
);
}
}

View File

@ -1,78 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/modules/desktop_login/login_form.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/pages/mobile_login/mobile_login.dart';
class DesktopLoginPage extends HookConsumerWidget {
static const name = WebViewLogin.name;
const DesktopLoginPage({super.key});
@override
Widget build(BuildContext context, ref) {
final mediaQuery = MediaQuery.of(context);
final theme = Theme.of(context);
final color = theme.colorScheme.surfaceVariant.withOpacity(.3);
return SafeArea(
child: Scaffold(
appBar: const PageWindowTitleBar(
leading: BackButton(),
),
body: SingleChildScrollView(
child: Center(
child: Container(
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
Assets.spotubeLogoPng.image(
width: MediaQuery.of(context).size.width *
(mediaQuery.mdAndDown ? .5 : .3),
),
Text(
context.l10n.add_spotify_credentials,
style: theme.textTheme.titleMedium,
),
Text(
context.l10n.credentials_will_not_be_shared_disclaimer,
style: theme.textTheme.labelMedium,
),
const SizedBox(height: 10),
TokenLoginForm(
onDone: () => GoRouter.of(context).go("/"),
),
const SizedBox(height: 10),
Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(context.l10n.know_how_to_login),
TextButton(
child: Text(
context.l10n.follow_step_by_step_guide,
),
onPressed: () => GoRouter.of(context).push(
"/login-tutorial",
),
),
],
),
],
),
),
),
),
),
);
}
}

View File

@ -1,125 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/modules/desktop_login/login_form.dart';
import 'package:spotube/components/links/hyper_link.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/pages/home/home.dart';
import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/utils/service_utils.dart';
class LoginTutorial extends ConsumerWidget {
static const name = "login_tutorial";
const LoginTutorial({super.key});
@override
Widget build(BuildContext context, ref) {
final auth = ref.watch(authenticationProvider);
final key = GlobalKey<State<IntroductionScreen>>();
final theme = Theme.of(context);
final pageDecoration = PageDecoration(
bodyTextStyle: theme.textTheme.bodyMedium!,
titleTextStyle: theme.textTheme.headlineMedium!,
);
return Scaffold(
appBar: PageWindowTitleBar(
leading: TextButton(
child: Text(context.l10n.exit),
onPressed: () {
Navigator.of(context).pop();
},
),
),
body: IntroductionScreen(
key: key,
globalBackgroundColor: theme.scaffoldBackgroundColor,
overrideBack: OutlinedButton(
child: Center(child: Text(context.l10n.previous)),
onPressed: () {
(key.currentState as IntroductionScreenState).previous();
},
),
overrideNext: FilledButton(
child: Center(child: Text(context.l10n.next)),
onPressed: () {
(key.currentState as IntroductionScreenState).next();
},
),
showBackButton: true,
overrideDone: FilledButton(
onPressed: auth.asData?.value != null
? () {
ServiceUtils.pushNamed(context, HomePage.name);
}
: null,
child: Center(child: Text(context.l10n.done)),
),
pages: [
PageViewModel(
decoration: pageDecoration,
title: context.l10n.step_1,
image: Assets.tutorial.step1.image(),
bodyWidget: Wrap(
children: [
Text(context.l10n.first_go_to),
const SizedBox(width: 5),
const Hyperlink(
"accounts.spotify.com ",
"https://accounts.spotify.com",
),
Text(context.l10n.login_if_not_logged_in),
],
),
),
PageViewModel(
decoration: pageDecoration,
title: context.l10n.step_2,
image: Assets.tutorial.step2.image(),
bodyWidget:
Text(context.l10n.step_2_steps, textAlign: TextAlign.left),
),
PageViewModel(
decoration: pageDecoration,
title: context.l10n.step_3,
image: Assets.tutorial.step3.image(),
bodyWidget:
Text(context.l10n.step_3_steps, textAlign: TextAlign.left),
),
if (auth.asData?.value != null)
PageViewModel(
decoration: pageDecoration.copyWith(
bodyAlignment: Alignment.center,
),
title: context.l10n.success_emoji,
image: Assets.success.image(),
body: context.l10n.success_message,
)
else
PageViewModel(
decoration: pageDecoration,
title: context.l10n.step_4,
bodyWidget: Column(
children: [
Text(
context.l10n.step_4_steps,
style: theme.textTheme.labelMedium,
),
const SizedBox(height: 10),
TokenLoginForm(
onDone: () {
GoRouter.of(context).go("/");
},
),
],
),
),
],
),
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -8,10 +9,12 @@ import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart'; import 'package:spotube/extensions/image.dart';
import 'package:spotube/pages/mobile_login/mobile_login.dart';
import 'package:spotube/pages/profile/profile.dart'; import 'package:spotube/pages/profile/profile.dart';
import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/scrobbler/scrobbler.dart'; import 'package:spotube/provider/scrobbler/scrobbler.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/utils/platform.dart';
import 'package:spotube/utils/service_utils.dart'; import 'package:spotube/utils/service_utils.dart';
class SettingsAccountSection extends HookConsumerWidget { class SettingsAccountSection extends HookConsumerWidget {
@ -23,6 +26,7 @@ class SettingsAccountSection extends HookConsumerWidget {
final router = GoRouter.of(context); final router = GoRouter.of(context);
final auth = ref.watch(authenticationProvider); final auth = ref.watch(authenticationProvider);
final authNotifier = ref.watch(authenticationProvider.notifier);
final scrobbler = ref.watch(scrobblerProvider); final scrobbler = ref.watch(scrobblerProvider);
final me = ref.watch(meProvider); final me = ref.watch(meProvider);
final meData = me.asData?.value; final meData = me.asData?.value;
@ -32,6 +36,36 @@ class SettingsAccountSection extends HookConsumerWidget {
foregroundColor: Colors.white, foregroundColor: Colors.white,
); );
void onLogin() async {
if (kIsMobile) {
router.pushNamed(WebViewLogin.name);
return;
}
final webview = await WebviewWindow.create();
webview.setOnUrlRequestCallback((url) {
final exp = RegExp(r"https:\/\/accounts.spotify.com\/.+\/status");
if (exp.hasMatch(url)) {
webview.getAllCookies().then((cookies) async {
final cookieHeader =
"sp_dc=${cookies.firstWhere((element) => element.name == "sp_dc").value}";
await authNotifier.login(cookieHeader);
webview.close();
if (context.mounted) {
context.go("/");
}
});
return true;
}
return false;
});
webview.launch("https://accounts.spotify.com/");
}
return SectionCardWithHeading( return SectionCardWithHeading(
heading: context.l10n.account, heading: context.l10n.account,
children: [ children: [
@ -70,17 +104,11 @@ class SettingsAccountSection extends HookConsumerWidget {
), ),
), ),
), ),
onTap: constrains.mdAndUp onTap: constrains.mdAndUp ? null : onLogin,
? null
: () {
router.push("/login");
},
trailing: constrains.smAndDown trailing: constrains.smAndDown
? null ? null
: FilledButton( : FilledButton(
onPressed: () { onPressed: onLogin,
router.push("/login");
},
style: ButtonStyle( style: ButtonStyle(
shape: MaterialStateProperty.all( shape: MaterialStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart'; import 'package:dio/io.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
@ -160,6 +161,9 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
WebStorageManager.instance().deleteAllData(); WebStorageManager.instance().deleteAllData();
CookieManager.instance().deleteAllCookies(); CookieManager.instance().deleteAllCookies();
} }
if (kIsDesktop) {
await WebviewWindow.clearAll();
}
} }
} }

View File

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <dart_discord_rpc/dart_discord_rpc_plugin.h> #include <dart_discord_rpc/dart_discord_rpc_plugin.h>
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <gtk/gtk_plugin.h> #include <gtk/gtk_plugin.h>
@ -24,6 +25,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dart_discord_rpc_registrar = g_autoptr(FlPluginRegistrar) dart_discord_rpc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DartDiscordRpcPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "DartDiscordRpcPlugin");
dart_discord_rpc_plugin_register_with_registrar(dart_discord_rpc_registrar); dart_discord_rpc_plugin_register_with_registrar(dart_discord_rpc_registrar);
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); file_selector_plugin_register_with_registrar(file_selector_linux_registrar);

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dart_discord_rpc dart_discord_rpc
desktop_webview_window
file_selector_linux file_selector_linux
flutter_secure_storage_linux flutter_secure_storage_linux
gtk gtk

View File

@ -9,6 +9,7 @@ import app_links
import audio_service import audio_service
import audio_session import audio_session
import bonsoir_darwin import bonsoir_darwin
import desktop_webview_window
import device_info_plus import device_info_plus
import file_selector_macos import file_selector_macos
import flutter_inappwebview_macos import flutter_inappwebview_macos
@ -32,6 +33,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin")) SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))

View File

@ -8,6 +8,8 @@ PODS:
- bonsoir_darwin (0.0.1): - bonsoir_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- desktop_webview_window (0.0.1):
- FlutterMacOS
- device_info_plus (0.0.1): - device_info_plus (0.0.1):
- FlutterMacOS - FlutterMacOS
- file_selector_macos (0.0.1): - file_selector_macos (0.0.1):
@ -70,6 +72,7 @@ DEPENDENCIES:
- audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`) - audio_service (from `Flutter/ephemeral/.symlinks/plugins/audio_service/macos`)
- audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`)
- bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`) - bonsoir_darwin (from `Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin`)
- desktop_webview_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
@ -105,6 +108,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos
bonsoir_darwin: bonsoir_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin :path: Flutter/ephemeral/.symlinks/plugins/bonsoir_darwin/darwin
desktop_webview_window:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_webview_window/macos
device_info_plus: device_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
file_selector_macos: file_selector_macos:
@ -151,6 +156,7 @@ SPEC CHECKSUMS:
audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9 audio_service: b88ff778e0e3915efd4cd1a5ad6f0beef0c950a9
audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842 bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d

View File

@ -474,6 +474,15 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" version: "0.7.10"
desktop_webview_window:
dependency: "direct main"
description:
path: "packages/desktop_webview_window"
ref: "feat/cookies"
resolved-ref: f20e433d4a948515b35089d40069f7dd9bced9e4
url: "https://github.com/KRTirtho/flutter-plugins.git"
source: git
version: "0.2.4"
device_info_plus: device_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -24,6 +24,11 @@ dependencies:
collection: ^1.15.0 collection: ^1.15.0
curved_navigation_bar: ^1.0.3 curved_navigation_bar: ^1.0.3
dbus: ^0.7.8 dbus: ^0.7.8
desktop_webview_window:
git:
url: https://github.com/KRTirtho/flutter-plugins.git
ref: feat/cookies
path: packages/desktop_webview_window
device_info_plus: ^10.1.0 device_info_plus: ^10.1.0
dio: ^5.4.3+1 dio: ^5.4.3+1
disable_battery_optimization: ^1.1.1 disable_battery_optimization: ^1.1.1

View File

@ -9,6 +9,7 @@
#include <app_links/app_links_plugin_c_api.h> #include <app_links/app_links_plugin_c_api.h>
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h> #include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
#include <dart_discord_rpc/dart_discord_rpc_plugin.h> #include <dart_discord_rpc/dart_discord_rpc_plugin.h>
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <local_notifier/local_notifier_plugin.h> #include <local_notifier/local_notifier_plugin.h>
@ -29,6 +30,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi")); registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
DartDiscordRpcPluginRegisterWithRegistrar( DartDiscordRpcPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DartDiscordRpcPlugin")); registry->GetRegistrarForPlugin("DartDiscordRpcPlugin"));
DesktopWebviewWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(

View File

@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
app_links app_links
bonsoir_windows bonsoir_windows
dart_discord_rpc dart_discord_rpc
desktop_webview_window
file_selector_windows file_selector_windows
flutter_secure_storage_windows flutter_secure_storage_windows
local_notifier local_notifier