feat: add metadata api service and models

This commit is contained in:
Kingkor Roy Tirtho 2025-05-01 13:20:05 +06:00
parent 4f72f3b424
commit 7491175bb6
21 changed files with 2528 additions and 0 deletions

View File

@ -5,6 +5,7 @@
"ambiguate", "ambiguate",
"Amoled", "Amoled",
"Buildless", "Buildless",
"configurators",
"danceability", "danceability",
"fuzzywuzzy", "fuzzywuzzy",
"gapless", "gapless",

View File

@ -1,3 +1,12 @@
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-keep class de.prosiebensat1digital.** { *; }
-keep class androidx.lifecycle.DefaultLifecycleObserver -keep class androidx.lifecycle.DefaultLifecycleObserver
-keepnames class kotlinx.serialization.** { *; } -keepnames class kotlinx.serialization.** { *; }

View File

@ -13,6 +13,9 @@ class FontFamily {
/// Font family: BootstrapIcons /// Font family: BootstrapIcons
static const String bootstrapIcons = 'BootstrapIcons'; static const String bootstrapIcons = 'BootstrapIcons';
/// Font family: Cookie
static const String cookie = 'Cookie';
/// Font family: RadixIcons /// Font family: RadixIcons
static const String radixIcons = 'RadixIcons'; static const String radixIcons = 'RadixIcons';
} }

View File

@ -0,0 +1,22 @@
part of 'metadata.dart';
enum SpotubeAlbumType {
album,
single,
}
@freezed
class SpotubeAlbumObject with _$SpotubeAlbumObject {
factory SpotubeAlbumObject({
required final String uid,
required final String title,
required final SpotubeArtistObject artist,
@Default([]) final List<SpotubeImageObject> images,
required final String releaseDate,
required final String externalUrl,
required final SpotubeAlbumType type,
}) = _SpotubeAlbumObject;
factory SpotubeAlbumObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeAlbumObjectFromJson(json);
}

View File

@ -0,0 +1,14 @@
part of 'metadata.dart';
@freezed
class SpotubeArtistObject with _$SpotubeArtistObject {
factory SpotubeArtistObject({
required final String uid,
required final String name,
@Default([]) final List<SpotubeImageObject> images,
required final String externalUrl,
}) = _SpotubeArtistObject;
factory SpotubeArtistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeArtistObjectFromJson(json);
}

View File

@ -0,0 +1,13 @@
part of 'metadata.dart';
@freezed
class SpotubeImageObject with _$SpotubeImageObject {
factory SpotubeImageObject({
required final String url,
required final int width,
required final int height,
}) = _SpotubeImageObject;
factory SpotubeImageObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeImageObjectFromJson(json);
}

View File

@ -0,0 +1,14 @@
library metadata_objects;
import 'package:freezed_annotation/freezed_annotation.dart';
part 'metadata.g.dart';
part 'metadata.freezed.dart';
part 'album.dart';
part 'artist.dart';
part 'image.dart';
part 'playlist.dart';
part 'search.dart';
part 'track.dart';
part 'user.dart';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'metadata.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeAlbumObjectImpl _$$SpotubeAlbumObjectImplFromJson(Map json) =>
_$SpotubeAlbumObjectImpl(
uid: json['uid'] as String,
title: json['title'] as String,
artist: SpotubeArtistObject.fromJson(
Map<String, dynamic>.from(json['artist'] as Map)),
images: (json['images'] as List<dynamic>?)
?.map((e) => SpotubeImageObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
releaseDate: json['releaseDate'] as String,
externalUrl: json['externalUrl'] as String,
type: $enumDecode(_$SpotubeAlbumTypeEnumMap, json['type']),
);
Map<String, dynamic> _$$SpotubeAlbumObjectImplToJson(
_$SpotubeAlbumObjectImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'title': instance.title,
'artist': instance.artist.toJson(),
'images': instance.images.map((e) => e.toJson()).toList(),
'releaseDate': instance.releaseDate,
'externalUrl': instance.externalUrl,
'type': _$SpotubeAlbumTypeEnumMap[instance.type]!,
};
const _$SpotubeAlbumTypeEnumMap = {
SpotubeAlbumType.album: 'album',
SpotubeAlbumType.single: 'single',
};
_$SpotubeArtistObjectImpl _$$SpotubeArtistObjectImplFromJson(Map json) =>
_$SpotubeArtistObjectImpl(
uid: json['uid'] as String,
name: json['name'] as String,
images: (json['images'] as List<dynamic>?)
?.map((e) => SpotubeImageObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
externalUrl: json['externalUrl'] as String,
);
Map<String, dynamic> _$$SpotubeArtistObjectImplToJson(
_$SpotubeArtistObjectImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'name': instance.name,
'images': instance.images.map((e) => e.toJson()).toList(),
'externalUrl': instance.externalUrl,
};
_$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) =>
_$SpotubeImageObjectImpl(
url: json['url'] as String,
width: (json['width'] as num).toInt(),
height: (json['height'] as num).toInt(),
);
Map<String, dynamic> _$$SpotubeImageObjectImplToJson(
_$SpotubeImageObjectImpl instance) =>
<String, dynamic>{
'url': instance.url,
'width': instance.width,
'height': instance.height,
};
_$SpotubePlaylistObjectImpl _$$SpotubePlaylistObjectImplFromJson(Map json) =>
_$SpotubePlaylistObjectImpl(
uid: json['uid'] as String,
name: json['name'] as String,
images: (json['images'] as List<dynamic>?)
?.map((e) => SpotubeImageObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
description: json['description'] as String,
externalUrl: json['externalUrl'] as String,
owner: SpotubeUserObject.fromJson(
Map<String, dynamic>.from(json['owner'] as Map)),
collaborators: (json['collaborators'] as List<dynamic>?)
?.map((e) => SpotubeUserObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
);
Map<String, dynamic> _$$SpotubePlaylistObjectImplToJson(
_$SpotubePlaylistObjectImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'name': instance.name,
'images': instance.images.map((e) => e.toJson()).toList(),
'description': instance.description,
'externalUrl': instance.externalUrl,
'owner': instance.owner.toJson(),
'collaborators': instance.collaborators.map((e) => e.toJson()).toList(),
};
_$SpotubeSearchResponseObjectImpl _$$SpotubeSearchResponseObjectImplFromJson(
Map json) =>
_$SpotubeSearchResponseObjectImpl(
tracks: (json['tracks'] as List<dynamic>?)
?.map((e) => SpotubeTrackObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
albums: (json['albums'] as List<dynamic>?)
?.map((e) => SpotubeAlbumObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
artists: (json['artists'] as List<dynamic>?)
?.map((e) => SpotubeArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
playlists: (json['playlists'] as List<dynamic>?)
?.map((e) => SpotubePlaylistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
);
Map<String, dynamic> _$$SpotubeSearchResponseObjectImplToJson(
_$SpotubeSearchResponseObjectImpl instance) =>
<String, dynamic>{
'tracks': instance.tracks.map((e) => e.toJson()).toList(),
'albums': instance.albums.map((e) => e.toJson()).toList(),
'artists': instance.artists.map((e) => e.toJson()).toList(),
'playlists': instance.playlists.map((e) => e.toJson()).toList(),
};
_$SpotubeTrackObjectImpl _$$SpotubeTrackObjectImplFromJson(Map json) =>
_$SpotubeTrackObjectImpl(
uid: json['uid'] as String,
title: json['title'] as String,
artists: (json['artists'] as List<dynamic>?)
?.map((e) => SpotubeArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
album: SpotubeAlbumObject.fromJson(
Map<String, dynamic>.from(json['album'] as Map)),
durationMs: (json['durationMs'] as num).toInt(),
isrc: json['isrc'] as String,
externalUrl: json['externalUrl'] as String,
);
Map<String, dynamic> _$$SpotubeTrackObjectImplToJson(
_$SpotubeTrackObjectImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'title': instance.title,
'artists': instance.artists.map((e) => e.toJson()).toList(),
'album': instance.album.toJson(),
'durationMs': instance.durationMs,
'isrc': instance.isrc,
'externalUrl': instance.externalUrl,
};
_$SpotubeUserObjectImpl _$$SpotubeUserObjectImplFromJson(Map json) =>
_$SpotubeUserObjectImpl(
uid: json['uid'] as String,
name: json['name'] as String,
avatars: (json['avatars'] as List<dynamic>?)
?.map((e) => SpotubeImageObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList() ??
const [],
externalUrl: json['externalUrl'] as String,
displayName: json['displayName'] as String,
);
Map<String, dynamic> _$$SpotubeUserObjectImplToJson(
_$SpotubeUserObjectImpl instance) =>
<String, dynamic>{
'uid': instance.uid,
'name': instance.name,
'avatars': instance.avatars.map((e) => e.toJson()).toList(),
'externalUrl': instance.externalUrl,
'displayName': instance.displayName,
};

View File

@ -0,0 +1,17 @@
part of 'metadata.dart';
@freezed
class SpotubePlaylistObject with _$SpotubePlaylistObject {
factory SpotubePlaylistObject({
required final String uid,
required final String name,
@Default([]) final List<SpotubeImageObject> images,
required final String description,
required final String externalUrl,
required final SpotubeUserObject owner,
@Default([]) final List<SpotubeUserObject> collaborators,
}) = _SpotubePlaylistObject;
factory SpotubePlaylistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubePlaylistObjectFromJson(json);
}

View File

@ -0,0 +1,14 @@
part of 'metadata.dart';
@freezed
class SpotubeSearchResponseObject with _$SpotubeSearchResponseObject {
factory SpotubeSearchResponseObject({
@Default([]) final List<SpotubeTrackObject> tracks,
@Default([]) final List<SpotubeAlbumObject> albums,
@Default([]) final List<SpotubeArtistObject> artists,
@Default([]) final List<SpotubePlaylistObject> playlists,
}) = _SpotubeSearchResponseObject;
factory SpotubeSearchResponseObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeSearchResponseObjectFromJson(json);
}

View File

@ -0,0 +1,17 @@
part of 'metadata.dart';
@freezed
class SpotubeTrackObject with _$SpotubeTrackObject {
factory SpotubeTrackObject({
required final String uid,
required final String title,
@Default([]) final List<SpotubeArtistObject> artists,
required final SpotubeAlbumObject album,
required final int durationMs,
required final String isrc,
required final String externalUrl,
}) = _SpotubeTrackObject;
factory SpotubeTrackObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeTrackObjectFromJson(json);
}

View File

@ -0,0 +1,15 @@
part of 'metadata.dart';
@freezed
class SpotubeUserObject with _$SpotubeUserObject {
factory SpotubeUserObject({
required final String uid,
required final String name,
@Default([]) final List<SpotubeImageObject> avatars,
required final String externalUrl,
required final String displayName,
}) = _SpotubeUserObject;
factory SpotubeUserObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeUserObjectFromJson(json);
}

View File

@ -0,0 +1,308 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter_js/flutter_js.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/services/logger/logger.dart';
const int defaultMetadataLimit = 20;
const int defaultMetadataOffset = 0;
/// Signature for metadata and related methods that will return Spotube native
/// objects e.g. SpotubeTrack, SpotubePlaylist, etc.
class MetadataApiSignature {
late final JavascriptRuntime runtime;
MetadataApiSignature(String libraryCode) {
runtime = getJavascriptRuntime(xhr: true);
runtime.enableHandlePromises();
Timer.periodic(
const Duration(milliseconds: 100),
(timer) {
runtime.executePendingJob();
},
);
runtime.evaluate(
"""
;$libraryCode;
const metadataApi = new MetadataApi();
""",
);
}
void dispose() {
runtime.dispose();
}
Future invoke(String method, [List? args]) async {
final completer = Completer();
runtime.onMessage(method, (result) {
try {
if (result == null) {
completer.completeError("Result is null");
} else {
completer.complete(result is String ? jsonDecode(result) : result);
}
} catch (e, stack) {
AppLogger.reportError(e, stack);
}
});
final code = """
$method(...${args != null ? jsonEncode(args) : "[]"})
.then((res) => {
sendMessage("$method", JSON.stringify(res));
}).catch((err) => {
sendMessage("$method", null);
async}){
} final res"metadataApi.=>", [limit, offset] ;= await invoke()
return res.map(es.fromJson).toList();
""";
final res = await runtime.evaluateAsync(code);
if (res.isError) {
AppLogger.reportError("Error evaluating code: $code\n${res.rawResult}");
completer.completeError("Error evaluating code: $code\n${res.rawResult}");
return completer.future;
}
return completer.future;
}
// ----- Track ------
Future<SpotubeTrackObject> getTrack(String id) async {
final result = await invoke("metadataApi.getTrack", [id]);
return SpotubeTrackObject.fromJson(result);
}
Future<List<SpotubeTrackObject>> listTracks({
List<String>? ids,
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final result = await invoke(
"metadataApi.listTracks",
[
ids,
limit,
offset,
],
);
return result.map(SpotubeTrackObject.fromJson).toList();
}
Future<List<SpotubeTrackObject>> listTracksByAlbum(
String albumId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listTracksByAlbum",
[albumId, limit, offset],
);
return res.map(SpotubeTrackObject.fromJson).toList();
}
Future<List<SpotubeTrackObject>> listTopTracksByArtist(
String artistId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listTopTracksByArtist",
[artistId, limit, offset],
);
return res.map(SpotubeTrackObject.fromJson).toList();
}
Future<List<SpotubeTrackObject>> listTracksByPlaylist(
String playlistId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listTracksByPlaylist",
[playlistId, limit, offset],
);
return res.map(SpotubeTrackObject.fromJson).toList();
}
Future<List<SpotubeTrackObject>> listUserSavedTracks(
String userId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listUserSavedTracks",
[userId, limit, offset],
);
return res.map(SpotubeTrackObject.fromJson).toList();
}
// ----- Album ------
Future<SpotubeAlbumObject> getAlbum(String id) async {
final res = await invoke("metadataApi.getAlbum", [id]);
return SpotubeAlbumObject.fromJson(res);
}
Future<List<SpotubeAlbumObject>> listAlbums({
List<String>? ids,
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listAlbums",
[ids, limit, offset],
);
return res.map(SpotubeAlbumObject.fromJson).toList();
}
Future<List<SpotubeAlbumObject>> listAlbumsByArtist(
String artistId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listAlbumsByArtist",
[artistId, limit, offset],
);
return res.map(SpotubeAlbumObject.fromJson).toList();
}
Future<List<SpotubeAlbumObject>> listUserSavedAlbums(
String userId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listUserSavedAlbums",
[userId, limit, offset],
);
return res.map(SpotubeAlbumObject.fromJson).toList();
}
// ----- Playlist ------
Future<SpotubePlaylistObject> getPlaylist(String id) async {
final res = await invoke("metadataApi.getPlaylist", [id]);
return SpotubePlaylistObject.fromJson(res);
}
Future<List<SpotubePlaylistObject>> listPlaylists({
List<String>? ids,
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listPlaylists",
[ids, limit, offset],
);
return res.map(SpotubePlaylistObject.fromJson).toList();
}
Future<List<SpotubePlaylistObject>> listUserSavedPlaylists(
String userId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listUserSavedPlaylists",
[userId, limit, offset],
);
return res.map(SpotubePlaylistObject.fromJson).toList();
}
// ----- Artist ------
Future<SpotubeArtistObject> getArtist(String id) async {
final res = await invoke("metadataApi.getArtist", [id]);
return SpotubeArtistObject.fromJson(res);
}
Future<List<SpotubeArtistObject>> listArtists({
List<String>? ids,
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listArtists",
[ids, limit, offset],
);
return res.map(SpotubeArtistObject.fromJson).toList();
}
Future<List<SpotubeArtistObject>> listUserSavedArtists(
String userId, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.listUserSavedArtists",
[userId, limit, offset],
);
return res.map(SpotubeArtistObject.fromJson).toList();
}
// ----- Search ------
Future<SpotubeSearchResponseObject> search(
String query, {
int limit = defaultMetadataLimit,
int offset = defaultMetadataOffset,
}) async {
final res = await invoke(
"metadataApi.search",
[query, limit, offset],
);
return res.map(SpotubeSearchResponseObject.fromJson).toList();
}
// ----- User ------
Future<void> followArtist(String userId, String artistId) async {
await invoke("metadataApi.followArtist", [userId, artistId]);
}
Future<void> unfollowArtist(String userId, String artistId) async {
await invoke("metadataApi.unfollowArtist", [userId, artistId]);
}
Future<void> savePlaylist(String userId, String playlistId) async {
await invoke("metadataApi.savePlaylist", [userId, playlistId]);
}
Future<void> unsavePlaylist(String userId, String playlistId) async {
await invoke("metadataApi.unsavePlaylist", [userId, playlistId]);
}
Future<void> saveAlbum(String userId, String albumId) async {
await invoke("metadataApi.saveAlbum", [userId, albumId]);
}
Future<void> unsaveAlbum(String userId, String albumId) async {
await invoke("metadataApi.unsaveAlbum", [userId, albumId]);
}
Future<void> saveTrack(String userId, String trackId) async {
await invoke("metadataApi.saveTrack", [userId, trackId]);
}
Future<void> unsaveTrack(String userId, String trackId) async {
await invoke("metadataApi.unsaveTrack", [userId, trackId]);
}
}

View File

@ -8,6 +8,7 @@
#include <desktop_webview_window/desktop_webview_window_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_js/flutter_js_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>
#include <local_notifier/local_notifier_plugin.h> #include <local_notifier/local_notifier_plugin.h>
@ -27,6 +28,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
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);
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
flutter_js_plugin_register_with_registrar(flutter_js_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);

View File

@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window desktop_webview_window
file_selector_linux file_selector_linux
flutter_js
flutter_secure_storage_linux flutter_secure_storage_linux
gtk gtk
local_notifier local_notifier

View File

@ -14,6 +14,7 @@ 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
import flutter_js
import flutter_secure_storage_macos import flutter_secure_storage_macos
import local_notifier import local_notifier
import media_kit_libs_macos_audio import media_kit_libs_macos_audio
@ -39,6 +40,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
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"))
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin")) MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))

View File

@ -909,6 +909,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.0"
flutter_js:
dependency: "direct main"
description:
name: flutter_js
sha256: "6b777cd4e468546f046a2f114d078a4596143269f6fa6bad5c29611d5b896369"
url: "https://pub.dev"
source: hosted
version: "0.8.2"
flutter_launcher_icons: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:

View File

@ -142,6 +142,7 @@ dependencies:
collection: any collection: any
otp_util: ^1.0.2 otp_util: ^1.0.2
dio_http2_adapter: ^2.6.0 dio_http2_adapter: ^2.6.0
flutter_js: ^0.8.2
dev_dependencies: dev_dependencies:
build_runner: ^2.4.13 build_runner: ^2.4.13

View File

@ -12,6 +12,7 @@
#include <desktop_webview_window/desktop_webview_window_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_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h> #include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_js/flutter_js_plugin.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>
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h> #include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
@ -36,6 +37,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterJsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
LocalNotifierPluginRegisterWithRegistrar( LocalNotifierPluginRegisterWithRegistrar(

View File

@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window desktop_webview_window
file_selector_windows file_selector_windows
flutter_inappwebview_windows flutter_inappwebview_windows
flutter_js
flutter_secure_storage_windows flutter_secure_storage_windows
local_notifier local_notifier
media_kit_libs_windows_audio media_kit_libs_windows_audio