diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46c9879c..afeb49ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - FLUTTER_VERSION: 2.10.5 + FLUTTER_VERSION: 3.0.0 image: cirrusci/flutter:${FLUTTER_VERSION} @@ -303,7 +303,6 @@ upload-linux-arm64: curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file appimage/FluffyChat-arm64.AppImage ${PACKAGE_REGISTRY_URL}/FluffyChat-arm64.AppImage curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file appimage/FluffyChat-arm64.AppImage.zsync ${PACKAGE_REGISTRY_URL}/FluffyChat-arm64.AppImage.zsync - upload-windows: extends: .release image: alpine:latest diff --git a/.metadata b/.metadata index e8149129..ce13b420 100644 --- a/.metadata +++ b/.metadata @@ -1,10 +1,45 @@ # 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. +# This file should be version controlled. version: - revision: 657830b4c77aecfd0e32ec6504c859213dded97a - channel: master + revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + channel: stable project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: android + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: ios + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: linux + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: macos + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: web + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + - platform: windows + create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6faacc94..db30b324 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -28,7 +28,6 @@ tools:overrideLibrary="io.wazo.callkeep, net.touchcapture.qr.flutterqr, com.cloudwebrtc.webrtc, org.webrtc, com.it_nomads.fluttersecurestorage, com.pichillilorenzo.flutter_inappwebview, com.example.video_compress, com.otaliastudios.transcoder, com.otaliastudios.opengl, com.kineapps.flutter_file_dialog"/> { _router ??= GlobalKey(); if (columnMode != newColumns > 1) { Logs().v('Set Column Mode = $columnMode'); - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { _initialUrl = _router?.currentState?.url; columnMode = newColumns > 1; @@ -131,7 +130,6 @@ class _FluffyChatAppState extends State { darkTheme: darkTheme, localizationsDelegates: const [ ...L10n.localizationsDelegates, - GaMaterialLocalizations.delegate ], supportedLocales: L10n.supportedLocales, initialUrl: _initialUrl ?? '/', @@ -141,7 +139,7 @@ class _FluffyChatAppState extends State { LoadingDialog.defaultBackLabel = L10n.of(context)!.close; LoadingDialog.defaultOnError = (e) => (e as Object?)!.toLocalizedString(context); - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle( statusBarColor: Colors.transparent, diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index f5bdf875..bace36c9 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -147,27 +147,27 @@ class _BootstrapDialogState extends State { case BootstrapState.loading: break; case BootstrapState.askWipeSsss: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.wipeSsss(_wipe!), ); break; case BootstrapState.askBadSsss: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.ignoreBadSecrets(true), ); break; case BootstrapState.askUseExistingSsss: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.useExistingSsss(!_wipe!), ); break; case BootstrapState.askUnlockSsss: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.unlockedSsss(), ); break; case BootstrapState.askNewSsss: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.newSsss(), ); break; @@ -312,12 +312,12 @@ class _BootstrapDialogState extends State { ), ); case BootstrapState.askWipeCrossSigning: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.wipeCrossSigning(_wipe!), ); break; case BootstrapState.askSetupCrossSigning: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.askSetupCrossSigning( setupMasterKey: true, setupSelfSigningKey: true, @@ -326,13 +326,13 @@ class _BootstrapDialogState extends State { ); break; case BootstrapState.askWipeOnlineKeyBackup: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.wipeOnlineKeyBackup(_wipe!), ); break; case BootstrapState.askSetupOnlineKeyBackup: - WidgetsBinding.instance!.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => bootstrap.askSetupOnlineKeyBackup(true), ); break; diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 0d2908e1..a97ff1bd 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -171,7 +171,7 @@ class ChatController extends State { final voipPlugin = Matrix.of(context).voipPlugin; if (voipPlugin != null) { - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { CallKeepManager().setVoipPlugin(voipPlugin); CallKeepManager().initialize().catchError((_) => true); }); @@ -210,7 +210,7 @@ class ChatController extends State { // when the scroll controller is attached we want to scroll to an event id, if specified // and update the scroll controller...which will trigger a request history, if the // "load more" button is visible on the screen - SchedulerBinding.instance!.addPostFrameCallback((_) async { + SchedulerBinding.instance.addPostFrameCallback((_) async { if (mounted) { final event = VRouter.of(context).queryParameters['event']; if (event != null) { diff --git a/lib/pages/chat/events/image_bubble.dart b/lib/pages/chat/events/image_bubble.dart index 0943a476..27872f32 100644 --- a/lib/pages/chat/events/image_bubble.dart +++ b/lib/pages/chat/events/image_bubble.dart @@ -91,7 +91,7 @@ class _ImageBubbleState extends State { try { final res = await widget.event .downloadAndDecryptAttachmentCached(getThumbnail: getThumbnail); - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { if (getThumbnail) { if (mounted) { setState(() => _thumbnail = res); @@ -106,7 +106,7 @@ class _ImageBubbleState extends State { } }); } catch (err) { - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { setState(() => _error = err); } @@ -278,7 +278,7 @@ class _ImageBubbleState extends State { errorBuilder: (context, error, stacktrace) { if (widget.event.hasThumbnail && !_requestedThumbnailOnFailure) { _requestedThumbnailOnFailure = true; - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { _file = null; _requestFile(getThumbnail: true); @@ -352,7 +352,7 @@ class _ImageBubbleState extends State { // the image failed to load but the event has a thumbnail attached....so we can // try to load this one! _requestedThumbnailOnFailure = true; - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { thumbnailUrl = widget.event .getAttachmentUrl( diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 04292f45..fcdf0182 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -26,6 +26,7 @@ import '../../widgets/matrix.dart'; import '../bootstrap/bootstrap_dialog.dart'; enum SelectMode { normal, share, select } + enum PopupMenuAction { settings, invite, @@ -171,7 +172,7 @@ class ChatListController extends State with TickerProviderStateMixin { void _processIncomingUris(String? text) async { if (text == null) return; VRouter.of(context).to('/rooms'); - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { UrlLauncher(context, text).openMatrixToUrl(); }); } @@ -475,7 +476,7 @@ class ChatListController extends State with TickerProviderStateMixin { setState(() { waitForFirstSync = true; }); - WidgetsBinding.instance!.addPostFrameCallback((_) => checkBootstrap()); + WidgetsBinding.instance.addPostFrameCallback((_) => checkBootstrap()); return; } @@ -572,7 +573,7 @@ class ChatListController extends State with TickerProviderStateMixin { } void resetActiveBundle() { - WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { setState(() { Matrix.of(context).activeBundle = null; }); diff --git a/lib/pages/search/search.dart b/lib/pages/search/search.dart index b579c560..12816558 100644 --- a/lib/pages/search/search.dart +++ b/lib/pages/search/search.dart @@ -110,7 +110,7 @@ class SearchController extends State { @override void initState() { super.initState(); - WidgetsBinding.instance?.addPostFrameCallback((_) async { + WidgetsBinding.instance.addPostFrameCallback((_) async { controller.text = VRouter.of(context).queryParameters['query'] ?? ''; final server = await Store().getItem(_serverStoreNamespace); if (server?.isNotEmpty ?? false) { diff --git a/lib/pages/settings_stories/settings_stories.dart b/lib/pages/settings_stories/settings_stories.dart index a9f666cf..b0a4818d 100644 --- a/lib/pages/settings_stories/settings_stories.dart +++ b/lib/pages/settings_stories/settings_stories.dart @@ -83,7 +83,7 @@ class SettingsStoriesController extends State { @override void initState() { super.initState(); - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { loadUsers = _loadUsers(); }); diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index b56a3785..f784eef5 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -359,7 +359,7 @@ class StoryPageController extends State { } void _setLoadingMode(bool mode) => loadingMode != mode - ? WidgetsBinding.instance?.addPostFrameCallback((_) { + ? WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { loadingMode = mode; }); diff --git a/lib/utils/background_push.dart b/lib/utils/background_push.dart index 4c6fb465..cd79b0ab 100644 --- a/lib/utils/background_push.dart +++ b/lib/utils/background_push.dart @@ -71,7 +71,7 @@ class BackgroundPush { bool upAction = false; - BackgroundPush._(this.client, {this.onFcmError}) { + BackgroundPush._(this.client) { onLogin ??= client.onLoginStateChanged.stream.listen(handleLoginStateChanged); onRoomSync ??= client.onSync.stream diff --git a/lib/utils/voip_plugin.dart b/lib/utils/voip_plugin.dart index c529de9e..101ccd45 100644 --- a/lib/utils/voip_plugin.dart +++ b/lib/utils/voip_plugin.dart @@ -28,10 +28,8 @@ class VoipPlugin extends WidgetsBindingObserver implements WebRTCDelegate { .catchError((e) => _currentConnectivity = ConnectivityResult.none); if (!kIsWeb) { final wb = WidgetsBinding.instance; - wb?.addObserver(this); - if (wb != null) { - didChangeAppLifecycleState(wb.lifecycleState!); - } + wb.addObserver(this); + didChangeAppLifecycleState(wb.lifecycleState!); } } diff --git a/lib/widgets/layouts/loading_view.dart b/lib/widgets/layouts/loading_view.dart index 176708cc..e593fbca 100644 --- a/lib/widgets/layouts/loading_view.dart +++ b/lib/widgets/layouts/loading_view.dart @@ -11,7 +11,7 @@ class LoadingView extends StatelessWidget { @override Widget build(BuildContext context) { - WidgetsBinding.instance?.addPostFrameCallback( + WidgetsBinding.instance.addPostFrameCallback( (_) => VRouter.of(context).to( Matrix.of(context) .widget diff --git a/lib/widgets/layouts/login_scaffold.dart b/lib/widgets/layouts/login_scaffold.dart index b738a71c..90cc5f35 100644 --- a/lib/widgets/layouts/login_scaffold.dart +++ b/lib/widgets/layouts/login_scaffold.dart @@ -13,7 +13,7 @@ class LoginScaffold extends StatelessWidget { @override Widget build(BuildContext context) { - WidgetsBinding.instance?.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarIconBrightness: Brightness.light, diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index f8c3b3b4..1bb296b7 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -253,7 +253,7 @@ class MatrixState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); - WidgetsBinding.instance!.addObserver(this); + WidgetsBinding.instance.addObserver(this); initMatrix(); if (PlatformInfos.isWeb) { initConfig().then((_) => initSettings()); @@ -382,7 +382,7 @@ class MatrixState extends State with WidgetsBindingObserver { void initMatrix() { // Display the app lock if (PlatformInfos.isMobile) { - WidgetsBinding.instance!.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) { ([TargetPlatform.linux].contains(Theme.of(context).platform) ? SharedPreferences.getInstance() .then((prefs) => prefs.getString(SettingKeys.appLockKey)) @@ -513,7 +513,7 @@ class MatrixState extends State with WidgetsBindingObserver { @override void dispose() { - WidgetsBinding.instance!.removeObserver(this); + WidgetsBinding.instance.removeObserver(this); onRoomKeyRequestSub.values.map((s) => s.cancel()); onKeyVerificationRequestSub.values.map((s) => s.cancel()); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 818fa7f6..43424f5e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -10,6 +10,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_linux ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -18,3 +21,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/pubspec.lock b/pubspec.lock index 8d0f0049..5c3d042d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -56,7 +56,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.6" + version: "3.1.11" args: dependency: transitive description: @@ -182,7 +182,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" connectivity_plus: dependency: "direct main" description: @@ -240,7 +240,7 @@ packages: name: coverage url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.2.0" cross_file: dependency: transitive description: @@ -394,7 +394,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" ffi: dependency: transitive description: @@ -687,7 +687,7 @@ packages: name: flutter_typeahead url: "https://pub.dartlang.org" source: hosted - version: "3.2.4" + version: "3.2.5" flutter_web_auth: dependency: "direct main" description: @@ -726,13 +726,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.3" - gaeilge_flutter_l10n: - dependency: "direct main" - description: - name: gaeilge_flutter_l10n - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" geolocator: dependency: "direct main" description: @@ -905,7 +898,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" just_audio: dependency: "direct main" description: @@ -1005,7 +998,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" matrix: dependency: "direct main" description: @@ -1161,7 +1154,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_drawing: dependency: transitive description: @@ -1361,9 +1354,11 @@ packages: qr_code_scanner: dependency: "direct main" description: - name: qr_code_scanner - url: "https://pub.dartlang.org" - source: hosted + path: "." + ref: fix_break_changes_platform + resolved-ref: "0feca6f15042c279ff575c559a3430df917b623d" + url: "https://github.com/xeinebiu/qr_code_scanner.git" + source: git version: "0.7.0" qr_flutter: dependency: "direct main" @@ -1616,7 +1611,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" sqflite: dependency: transitive description: @@ -1686,21 +1681,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.5" + version: "1.20.2" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.11" timezone: dependency: transitive description: @@ -1882,7 +1877,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" very_good_analysis: dependency: transitive description: @@ -1945,7 +1940,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "7.5.0" + version: "8.2.2" vrouter: dependency: "direct main" description: @@ -2059,5 +2054,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.16.1 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 79e6f5bd..c317760d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none version: 1.4.0+2323 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=2.12.0 <3.0.0" dependencies: adaptive_dialog: ^1.5.1 @@ -43,11 +43,10 @@ dependencies: flutter_secure_storage: ^5.0.2 flutter_slidable: ^1.2.0 flutter_svg: ^0.22.0 - flutter_typeahead: ^3.2.4 + flutter_typeahead: ^3.2.5 flutter_web_auth: ^0.4.0 flutter_webrtc: ^0.8.2 future_loading_dialog: ^0.2.3 - gaeilge_flutter_l10n: ^1.0.0 geolocator: ^7.6.2 hive_flutter: ^1.1.0 image: ^3.1.1 @@ -147,9 +146,15 @@ dependency_overrides: url: https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git ref: null-safety provider: 5.0.0 + # For Flutter 3.0.0 compatibility + # https://github.com/juliuscanute/qr_code_scanner/issues/532 + qr_code_scanner: + git: + url: https://github.com/xeinebiu/qr_code_scanner.git + ref: fix_break_changes_platform # wating for `Listenable` implementation # Upstream pull request: https://github.com/AdamJonsson/snapping_sheet/pull/84 snapping_sheet: git: url: https://github.com/TheOneWithTheBraid/snapping_sheet.git - ref: listenable \ No newline at end of file + ref: listenable diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index fec9fdda..355f8578 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -12,6 +12,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -20,3 +23,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin)