Implement background notifications, I think
This commit is contained in:
parent
3270f5d888
commit
4ec79e09e0
|
@ -1,6 +1,7 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="xyz.janderedev.feet">
|
package="xyz.janderedev.feet">
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
@ -41,5 +42,12 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
|
||||||
|
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
part 'feed.dart';
|
part 'feed.dart';
|
||||||
part 'item.dart';
|
part 'item.dart';
|
||||||
|
@ -21,12 +22,16 @@ class FeverAPI {
|
||||||
String? apiKey;
|
String? apiKey;
|
||||||
String? apiUrl;
|
String? apiUrl;
|
||||||
|
|
||||||
|
late final APICache cache;
|
||||||
|
|
||||||
/// We use a shared HTTP client for all requests
|
/// We use a shared HTTP client for all requests
|
||||||
final _httpClient = http.Client();
|
final _httpClient = http.Client();
|
||||||
|
|
||||||
late final APICache cache;
|
/// If set, the `newestKnownPost` value will be updated automatically.
|
||||||
|
/// This is used for notifications.
|
||||||
|
SharedPreferences? sharedPrefs;
|
||||||
|
|
||||||
FeverAPI({this.apiKey, this.apiUrl}) {
|
FeverAPI({this.apiKey, this.apiUrl, this.sharedPrefs}) {
|
||||||
cache = APICache(this);
|
cache = APICache(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,19 +69,20 @@ class FeverAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
APIRequestBuilder request() {
|
APIRequestBuilder request() {
|
||||||
return APIRequestBuilder(this, _httpClient);
|
return APIRequestBuilder(this, _httpClient, sharedPrefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class APIRequestBuilder {
|
class APIRequestBuilder {
|
||||||
final FeverAPI api;
|
final FeverAPI api;
|
||||||
|
final SharedPreferences? _sharedPrefs;
|
||||||
final http.Client _httpClient;
|
final http.Client _httpClient;
|
||||||
|
|
||||||
List<String> args = [];
|
List<String> args = [];
|
||||||
Item? _markedItem;
|
Item? _markedItem;
|
||||||
ItemMarkType? _markedItemAs;
|
ItemMarkType? _markedItemAs;
|
||||||
|
|
||||||
APIRequestBuilder(this.api, this._httpClient);
|
APIRequestBuilder(this.api, this._httpClient, this._sharedPrefs);
|
||||||
|
|
||||||
void _addArg(String arg) {
|
void _addArg(String arg) {
|
||||||
if (!args.contains(arg)) args.add(arg);
|
if (!args.contains(arg)) args.add(arg);
|
||||||
|
@ -173,12 +179,19 @@ class APIRequestBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.containsKey('items')) {
|
if (data.containsKey('items')) {
|
||||||
|
var currentHighest = _sharedPrefs?.getInt('newestKnownPost') ?? 0;
|
||||||
|
|
||||||
List<Item> items = (data['items'] as List<dynamic>)
|
List<Item> items = (data['items'] as List<dynamic>)
|
||||||
.map((json) => Item.fromJSON(api, json))
|
.map((json) => Item.fromJSON(api, json))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (var item in items) {
|
for (var item in items) {
|
||||||
|
if (item.id > currentHighest) {
|
||||||
|
currentHighest = item.id;
|
||||||
|
}
|
||||||
|
|
||||||
api.cache.items.set(item.id, item);
|
api.cache.items.set(item.id, item);
|
||||||
|
_sharedPrefs?.setInt('newestKnownPost', currentHighest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:feet/notifications/tasks.dart';
|
||||||
import 'package:feet/widgets/centered_page_hint.dart';
|
import 'package:feet/widgets/centered_page_hint.dart';
|
||||||
import 'package:feet/widgets/filter_menu.dart';
|
import 'package:feet/widgets/filter_menu.dart';
|
||||||
import 'package:feet/widgets/homepage_post.dart';
|
import 'package:feet/widgets/homepage_post.dart';
|
||||||
|
@ -15,6 +16,7 @@ import 'api/api.dart';
|
||||||
const FETCH_MAX_POSTS = 1000;
|
const FETCH_MAX_POSTS = 1000;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
setupBackgroundTasks();
|
||||||
runApp(MyApp());
|
runApp(MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +29,7 @@ class MyApp extends StatelessWidget {
|
||||||
Future<bool> _loadPrefs() async {
|
Future<bool> _loadPrefs() async {
|
||||||
var prefs = await SharedPreferences.getInstance();
|
var prefs = await SharedPreferences.getInstance();
|
||||||
this.prefs = prefs;
|
this.prefs = prefs;
|
||||||
|
api.sharedPrefs = prefs;
|
||||||
var apiKey = prefs.getString('apiKey'), apiUrl = prefs.getString('apiUrl');
|
var apiKey = prefs.getString('apiKey'), apiUrl = prefs.getString('apiUrl');
|
||||||
|
|
||||||
if (apiUrl != null && apiKey != null) {
|
if (apiUrl != null && apiKey != null) {
|
||||||
|
|
116
lib/notifications/tasks.dart
Normal file
116
lib/notifications/tasks.dart
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:feet/api/api.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart';
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
void callbackDispatcher() {
|
||||||
|
Workmanager().executeTask((taskName, inputData) async {
|
||||||
|
print("Native called background task: $taskName");
|
||||||
|
|
||||||
|
if (taskName == 'fetchNotifications') {
|
||||||
|
final sharedPreferences = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var initialized = await FlutterLocalNotificationsPlugin().initialize(
|
||||||
|
const InitializationSettings(
|
||||||
|
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (initialized == false) {
|
||||||
|
print("Notification plugin did not initialize; cancelling task");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiUrl = sharedPreferences.getString('apiUrl'),
|
||||||
|
apiKey = sharedPreferences.getString('apiKey'),
|
||||||
|
latest = sharedPreferences.getInt('newestKnownPost');
|
||||||
|
|
||||||
|
if (apiUrl == null || apiKey == null) {
|
||||||
|
print("API URL or API key not set; cancelling task");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var api = FeverAPI(
|
||||||
|
apiKey: apiKey,
|
||||||
|
apiUrl: apiUrl,
|
||||||
|
sharedPrefs: sharedPreferences,
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.request().withItems().sinceId(latest ?? 0).execute();
|
||||||
|
var items =
|
||||||
|
api.cache.items.getAll().values.where((post) => !post.isRead);
|
||||||
|
|
||||||
|
// Only fetch feeds and favicons if necessary to avoid wasting bandwidth
|
||||||
|
if (items.isNotEmpty) {
|
||||||
|
print("New posts found, fetching feeds and favicons");
|
||||||
|
await api.request().withFeeds().withFavicons().execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var item in items) {
|
||||||
|
var feed = api.cache.feeds.get(item.feedId);
|
||||||
|
print("Notifying for post: ${item.id}");
|
||||||
|
|
||||||
|
AndroidBitmap<Object>? iconData;
|
||||||
|
var favicon = api.cache.favicons.get(feed?.faviconId ?? -1);
|
||||||
|
if (favicon != null) {
|
||||||
|
try {
|
||||||
|
iconData = ByteArrayAndroidBitmap.fromBase64String(
|
||||||
|
favicon.data.split(',')[1]);
|
||||||
|
} catch (e) {
|
||||||
|
print("Failed to decode favicon: $e - Continuing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await FlutterLocalNotificationsPlugin().show(
|
||||||
|
Random().nextInt(10000000), // Can't use the post ID here because
|
||||||
|
// FreshRSS's IDs are too large
|
||||||
|
feed != null ? 'New post - ${feed.title}' : 'New post',
|
||||||
|
item.title,
|
||||||
|
NotificationDetails(
|
||||||
|
android: AndroidNotificationDetails(
|
||||||
|
'new_posts',
|
||||||
|
'New posts',
|
||||||
|
autoCancel: true,
|
||||||
|
channelShowBadge: true,
|
||||||
|
importance: Importance.low,
|
||||||
|
largeIcon: iconData,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupBackgroundTasks() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
Workmanager().initialize(
|
||||||
|
callbackDispatcher,
|
||||||
|
isInDebugMode: kDebugMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Permission.notification.request();
|
||||||
|
|
||||||
|
// Runs every 15 minutes
|
||||||
|
Workmanager().registerPeriodicTask(
|
||||||
|
'fetch-notifications',
|
||||||
|
'fetchNotifications',
|
||||||
|
initialDelay: const Duration(seconds: 30),
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import dynamic_color
|
import dynamic_color
|
||||||
|
import flutter_local_notifications
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
import share_plus
|
import share_plus
|
||||||
import shared_preferences_macos
|
import shared_preferences_macos
|
||||||
|
@ -14,6 +15,7 @@ import wakelock_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin"))
|
||||||
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
|
104
pubspec.lock
104
pubspec.lock
|
@ -1,6 +1,14 @@
|
||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -89,6 +97,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "3350efa144252eaa4264055dded4404a94b770cfe914f1d08c20953aee55cac2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.4"
|
||||||
dynamic_color:
|
dynamic_color:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -150,6 +166,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_local_notifications:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications
|
||||||
|
sha256: "11d12ac8c713d28a654036c6e61464ef039eb560296282b17b933682ede0b4b8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.4"
|
||||||
|
flutter_local_notifications_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_linux
|
||||||
|
sha256: "0824c36855ef1f159e1b957e4ff43dfd171372d6915424bc5004e277db4f1bc5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
|
flutter_local_notifications_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_platform_interface
|
||||||
|
sha256: "21bceee103a66a53b30ea9daf677f990e5b9e89b62f222e60dd241cd08d63d3a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
flutter_math_fork:
|
flutter_math_fork:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -344,6 +384,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
pedantic:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pedantic
|
||||||
|
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.1"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.2.0"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.2.0"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.7"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.9.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -525,6 +613,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.16"
|
version: "0.4.16"
|
||||||
|
timezone:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: "57b35f6e8ef731f18529695bffc62f92c6189fac2e52c12d478dec1931afb66e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.0"
|
||||||
tuple:
|
tuple:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -741,6 +837,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
|
workmanager:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: workmanager
|
||||||
|
sha256: e0be7e35d644643f164ee45d2ce14414f0e0fdde19456aa66065f35a0b1d2ea1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -42,6 +42,9 @@ dependencies:
|
||||||
share_plus: ^6.3.0
|
share_plus: ^6.3.0
|
||||||
flutter_html: ^2.2.1
|
flutter_html: ^2.2.1
|
||||||
url_launcher: ^6.1.7
|
url_launcher: ^6.1.7
|
||||||
|
workmanager: ^0.5.1
|
||||||
|
flutter_local_notifications: ^9.1.4
|
||||||
|
permission_handler: ^10.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -7,12 +7,15 @@
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
#include <share_plus/share_plus_windows_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
SharePlusWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
dynamic_color
|
dynamic_color
|
||||||
|
permission_handler_windows
|
||||||
share_plus
|
share_plus
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue