mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat: supabase integration
This commit is contained in:
parent
fb780da327
commit
8bcce9282e
@ -1,3 +1,6 @@
|
||||
SUPABASE_URL=
|
||||
SUPABASE_API_KEY=
|
||||
|
||||
# The format:
|
||||
# SPOTIFY_SECRETS=clintId1:clientSecret1,clientId2:clientSecret2
|
||||
SPOTIFY_SECRETS=
|
||||
|
@ -4,6 +4,12 @@ part 'env.g.dart';
|
||||
|
||||
@Envied(obfuscate: true, requireEnvFile: true, path: ".env")
|
||||
abstract class Env {
|
||||
@EnviedField(varName: 'SUPABASE_URL')
|
||||
static final supabaseUrl = _Env.supabaseUrl;
|
||||
|
||||
@EnviedField(varName: 'SUPABASE_API_KEY')
|
||||
static final supabaseAnonKey = _Env.supabaseAnonKey;
|
||||
|
||||
@EnviedField(varName: 'SPOTIFY_SECRETS')
|
||||
static final spotifySecrets = _Env.spotifySecrets.split(',').map((e) {
|
||||
final secrets = e.trim().split(":").map((e) => e.trim());
|
||||
|
@ -59,6 +59,16 @@ class UniversalImage extends HookWidget {
|
||||
height: height,
|
||||
width: width,
|
||||
placeholder: AssetImage(placeholder ?? Assets.placeholder.path),
|
||||
imageErrorBuilder: (context, error, stackTrace) {
|
||||
return Image.asset(
|
||||
placeholder ?? Assets.placeholder.path,
|
||||
width: width,
|
||||
height: height,
|
||||
cacheHeight: height?.toInt(),
|
||||
cacheWidth: width?.toInt(),
|
||||
scale: scale,
|
||||
);
|
||||
},
|
||||
fit: fit,
|
||||
);
|
||||
} else if (Uri.tryParse(path) != null && !path.startsWith("assets")) {
|
||||
|
@ -15,6 +15,7 @@ import 'package:media_kit/media_kit.dart';
|
||||
import 'package:metadata_god/metadata_god.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotube/collections/env.dart';
|
||||
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
|
||||
import 'package:spotube/collections/routes.dart';
|
||||
import 'package:spotube/collections/intents.dart';
|
||||
@ -28,6 +29,7 @@ import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
import 'package:spotube/services/youtube.dart';
|
||||
import 'package:spotube/themes/theme.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:spotube/hooks/use_init_sys_tray.dart';
|
||||
@ -71,6 +73,11 @@ Future<void> main(List<String> rawArgs) async {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
await Supabase.initialize(
|
||||
url: Env.supabaseUrl,
|
||||
anonKey: Env.supabaseAnonKey,
|
||||
);
|
||||
|
||||
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
|
@ -9,9 +9,37 @@ class MatchedTrack {
|
||||
@HiveField(1)
|
||||
String spotifyId;
|
||||
|
||||
String? id;
|
||||
DateTime? createdAt;
|
||||
|
||||
bool get isSynced => id != null;
|
||||
|
||||
static const boxName = "oss.krtirtho.spotube.matched_tracks";
|
||||
|
||||
static LazyBox<MatchedTrack> get box => Hive.lazyBox<MatchedTrack>(boxName);
|
||||
|
||||
MatchedTrack({required this.youtubeId, required this.spotifyId});
|
||||
MatchedTrack({
|
||||
required this.youtubeId,
|
||||
required this.spotifyId,
|
||||
this.id,
|
||||
this.createdAt,
|
||||
});
|
||||
|
||||
factory MatchedTrack.fromJson(Map<String, dynamic> json) {
|
||||
return MatchedTrack(
|
||||
youtubeId: json["youtube_id"],
|
||||
spotifyId: json["spotify_id"],
|
||||
id: json["id"],
|
||||
createdAt: DateTime.parse(json["created_at"]),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"youtube_id": youtubeId,
|
||||
"spotify_id": spotifyId,
|
||||
"id": id,
|
||||
"created_at": createdAt?.toString()
|
||||
}..removeWhere((key, value) => value == null);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:spotube/extensions/album_simple.dart';
|
||||
import 'package:spotube/extensions/artist_simple.dart';
|
||||
import 'package:spotube/models/matched_track.dart';
|
||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/supabase.dart';
|
||||
import 'package:spotube/services/youtube.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:spotube/utils/service_utils.dart';
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:catcher/catcher.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/models/local_track.dart';
|
||||
import 'package:spotube/models/matched_track.dart';
|
||||
import 'package:spotube/models/spotube_track.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart';
|
||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||
import 'package:spotube/services/supabase.dart';
|
||||
|
||||
mixin NextFetcher on StateNotifier<ProxyPlaylist> {
|
||||
Future<List<SpotubeTrack>> fetchTracks(
|
||||
@ -78,4 +81,19 @@ mixin NextFetcher on StateNotifier<ProxyPlaylist> {
|
||||
return "https://youtube.com/unplayable.m4a?id=${track.id}";
|
||||
}
|
||||
}
|
||||
|
||||
/// This method must be called after any playback operation as
|
||||
/// it can increase the latency
|
||||
Future<void> storeTrack(Track track, SpotubeTrack spotubeTrack) async {
|
||||
if (track is! SpotubeTrack) {
|
||||
await supabase
|
||||
.insertTrack(
|
||||
MatchedTrack(
|
||||
youtubeId: spotubeTrack.ytTrack.id,
|
||||
spotifyId: spotubeTrack.id!,
|
||||
),
|
||||
)
|
||||
.catchError(Catcher.reportCheckedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
|
||||
isPreSearching = true;
|
||||
|
||||
// TODO: Make repeat mode sensitive changes later
|
||||
final oldTrack =
|
||||
state.tracks.elementAtOrNull(audioPlayer.currentIndex);
|
||||
final track =
|
||||
await ensureNthSourcePlayable(audioPlayer.currentIndex + 1);
|
||||
|
||||
@ -92,6 +94,13 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
|
||||
if (audioPlayer.isPaused) {
|
||||
await audioPlayer.resume();
|
||||
}
|
||||
|
||||
if (oldTrack != null && track != null) {
|
||||
await storeTrack(
|
||||
oldTrack,
|
||||
track,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
isPreSearching = false;
|
||||
}
|
||||
@ -120,9 +129,10 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
|
||||
return null;
|
||||
}
|
||||
|
||||
final nthFetchedTrack = nthTrack is SpotubeTrack
|
||||
? nthTrack
|
||||
: await SpotubeTrack.fetchFromTrack(nthTrack, preferences);
|
||||
final nthFetchedTrack = switch (nthTrack.runtimeType) {
|
||||
SpotubeTrack => nthTrack as SpotubeTrack,
|
||||
_ => await SpotubeTrack.fetchFromTrack(nthTrack, preferences),
|
||||
};
|
||||
|
||||
if (nthSource == nthFetchedTrack.ytUri) return null;
|
||||
|
||||
@ -196,14 +206,27 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
|
||||
initialIndex: initialIndex,
|
||||
autoPlay: autoPlay,
|
||||
);
|
||||
|
||||
await storeTrack(
|
||||
tracks[initialIndex],
|
||||
addableTrack,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> jumpTo(int index) async {
|
||||
final oldTrack = state.tracks.elementAtOrNull(audioPlayer.currentIndex);
|
||||
final track = await ensureNthSourcePlayable(index);
|
||||
if (track != null) {
|
||||
state = state.copyWith(tracks: mergeTracks([track], state.tracks));
|
||||
}
|
||||
await audioPlayer.jumpTo(index);
|
||||
|
||||
if (oldTrack != null && track != null) {
|
||||
await storeTrack(
|
||||
oldTrack,
|
||||
track,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> jumpToTrack(Track track) async {
|
||||
@ -234,19 +257,34 @@ class ProxyPlaylistNotifier extends StateNotifier<ProxyPlaylist>
|
||||
Future<void> swapSibling(PipedSearchItem video) async {}
|
||||
|
||||
Future<void> next() async {
|
||||
final oldTrack = state.tracks.elementAtOrNull(audioPlayer.currentIndex + 1);
|
||||
final track = await ensureNthSourcePlayable(audioPlayer.currentIndex + 1);
|
||||
if (track != null) {
|
||||
state = state.copyWith(tracks: mergeTracks([track], state.tracks));
|
||||
}
|
||||
await audioPlayer.skipToNext();
|
||||
|
||||
if (oldTrack != null && track != null) {
|
||||
await storeTrack(
|
||||
oldTrack,
|
||||
track,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> previous() async {
|
||||
final oldTrack = state.tracks.elementAtOrNull(audioPlayer.currentIndex - 1);
|
||||
final track = await ensureNthSourcePlayable(audioPlayer.currentIndex - 1);
|
||||
if (track != null) {
|
||||
state = state.copyWith(tracks: mergeTracks([track], state.tracks));
|
||||
}
|
||||
await audioPlayer.skipToPrevious();
|
||||
if (oldTrack != null && track != null) {
|
||||
await storeTrack(
|
||||
oldTrack,
|
||||
track,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
|
12
lib/services/supabase.dart
Normal file
12
lib/services/supabase.dart
Normal file
@ -0,0 +1,12 @@
|
||||
import 'package:spotube/models/matched_track.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
class SupabaseService {
|
||||
static SupabaseClient get api => Supabase.instance.client;
|
||||
|
||||
Future<void> insertTrack(MatchedTrack track) async {
|
||||
await api.from("tracks").insert(track.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
final supabase = SupabaseService();
|
@ -5,6 +5,7 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import app_links
|
||||
import audio_service
|
||||
import audio_session
|
||||
import catcher
|
||||
@ -16,6 +17,7 @@ import package_info_plus
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import shared_preferences_foundation
|
||||
import sign_in_with_apple
|
||||
import sqflite
|
||||
import system_theme
|
||||
import system_tray
|
||||
@ -24,6 +26,7 @@ import window_manager
|
||||
import window_size
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
|
||||
AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin"))
|
||||
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
|
||||
CatcherPlugin.register(with: registry.registrar(forPlugin: "CatcherPlugin"))
|
||||
@ -35,6 +38,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin"))
|
||||
SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin"))
|
||||
|
144
pubspec.lock
144
pubspec.lock
@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.0"
|
||||
app_links:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links
|
||||
sha256: d572dcdff49c4cfcfa6f315e2683e518ec6eb54e084d01e51d9631a4dcc1b5e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.2"
|
||||
app_package_maker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -777,6 +785,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
functions_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: functions_client
|
||||
sha256: "26059c5fb000ffd0986ae3144d43c2a6f54931610fd61c2584e18e308c7eaa52"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
fuzzywuzzy:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -801,6 +817,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.6"
|
||||
gotrue:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gotrue
|
||||
sha256: c08f5ac76dcae2dd06cc7f8e80a8ede12c66454fef06caac3b191c8c7a603811
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.7.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -966,6 +990,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.7"
|
||||
jwt_decode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: jwt_decode
|
||||
sha256: d2e9f68c052b2225130977429d30f187aa1981d789c76ad104a32243cfdebfbb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1312,6 +1344,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.8+1"
|
||||
postgrest:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: postgrest
|
||||
sha256: "7b91eb7b40621d07aaae687f47f3032f30e1b86a9ccebfcfca52d001223f8b6e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.4"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1376,6 +1416,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
realtime_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: realtime_client
|
||||
sha256: "0f2614f72e5639ddd7abc3dede336f23554f9f744d0b064d41009f9ca94a53d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
retry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: retry
|
||||
sha256: a8a1e475a100a0bdc73f529ca8ea1e9c9c76bec8ad86a1f47780139a34ce7aea
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1512,6 +1568,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.0"
|
||||
sign_in_with_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple
|
||||
sha256: ac3b113767dfdd765078c507dad9d4d9fe96b669cc7bd88fc36fc15376fb3400
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
sign_in_with_apple_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_platform_interface
|
||||
sha256: a5883edee09ed6be19de19e7d9f618a617fe41a6fa03f76d082dfb787e9ea18d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
sign_in_with_apple_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_web
|
||||
sha256: "44b66528f576e77847c14999d5e881e17e7223b7b0625a185417829e5306f47a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
simple_circular_progress_bar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1606,6 +1686,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2+1"
|
||||
storage_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: storage_client
|
||||
sha256: "4ed4dc8a990d178c96962319d6d8c267c3e206fca2c2b98660bad6e001220ffc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1630,6 +1718,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
supabase:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: supabase
|
||||
sha256: "403739cdfea48ba633450e5b191ceeaae81ac10ec89166c0e109235b3e1532f3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
supabase_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: supabase_flutter
|
||||
sha256: "7cbdd9a7264dd5b7ab5a6e2da63346054b8e5ddf358467c7f2bc23d5c14d732c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
sync_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1878,6 +1982,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
webview_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "4646bb68297803bdbb96d46853e8fcb560d6cb5e04153fa64581535767875dfe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.3"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1935,6 +2071,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
yet_another_json_isolate:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yet_another_json_isolate
|
||||
sha256: "94ba4947ac1ce44bd6a1634d9df712e07b9b5025ba12abd6750be77ba5c08f18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
@ -101,6 +101,7 @@ dependencies:
|
||||
git:
|
||||
url: https://github.com/KRTirtho/piped_client
|
||||
ref: 2036a78d3414a0fc7fe3b081f1029dd086352fcd
|
||||
supabase_flutter: ^1.9.1
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.3.2
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <app_links/app_links_plugin_c_api.h>
|
||||
#include <catcher/catcher_plugin.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <local_notifier/local_notifier_plugin.h>
|
||||
@ -19,6 +20,8 @@
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AppLinksPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
|
||||
CatcherPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("CatcherPlugin"));
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
app_links
|
||||
catcher
|
||||
flutter_secure_storage_windows
|
||||
local_notifier
|
||||
|
Loading…
Reference in New Issue
Block a user