diff --git a/example/lib/packages/embedder_texture/model.dart b/example/lib/packages/embedder_texture/model.dart new file mode 100644 index 0000000..f315f29 --- /dev/null +++ b/example/lib/packages/embedder_texture/model.dart @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC +// SPDX-License-Identifier: BSD-3-Clause +import 'package:flutter/widgets.dart'; +import 'package:scoped_model/scoped_model.dart'; +import 'package:xdga_directories/xdga_directories.dart' as xdga; + +/// Model for [EmbedderTexturePage] +class EmbedderTextureModel extends Model { + /// Get [ScopedModel] + static EmbedderTextureModel of(BuildContext context) => + ScopedModel.of(context); + + /// Error + String? _error; + + /// Public error + String? get error => _error; + + /// Public is error + bool get isError => _error != null; + +} diff --git a/example/lib/packages/embedder_texture/package.dart b/example/lib/packages/embedder_texture/package.dart new file mode 100644 index 0000000..1f45200 --- /dev/null +++ b/example/lib/packages/embedder_texture/package.dart @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC +// SPDX-License-Identifier: BSD-3-Clause +import 'package:flutter_example_packages/base/package/package_page.dart'; +import 'package:flutter_example_packages/packages/embedder_texture/page.dart'; +import 'package:get_it/get_it.dart'; + +import 'model.dart'; + +/// Package values +final packageEmbedderTexture = PackagePage( + key: 'Embedder Texture', + descEN: ''' + Test Embedder texture plugin + ''', + descRU: ''' + Тестирование текстур Embedder + ''', + version: '0.0.1', + isPlatformDependent: true, + page: () => EmbedderTexturePage(), + init: () { + GetIt.instance + .registerFactory(() => EmbedderTextureModel()); + }, +); diff --git a/example/lib/packages/embedder_texture/page.dart b/example/lib/packages/embedder_texture/page.dart new file mode 100644 index 0000000..4f68ba0 --- /dev/null +++ b/example/lib/packages/embedder_texture/page.dart @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC +// SPDX-License-Identifier: BSD-3-Clause +import 'package:flutter/material.dart'; +import 'package:flutter_example_packages/base/di/app_di.dart'; +import 'package:flutter_example_packages/base/package/package.dart'; +import 'package:flutter_example_packages/packages/embedder_texture/model.dart'; +import 'package:flutter_example_packages/packages/embedder_texture/package.dart'; +import 'package:flutter_example_packages/widgets/base/export.dart'; +import 'package:flutter_example_packages/widgets/blocks/block_alert.dart'; +import 'package:flutter_example_packages/widgets/blocks/block_info_package.dart'; +import 'package:flutter_example_packages/widgets/blocks/block_item.dart'; +import 'package:flutter_example_packages/widgets/layouts/block_layout.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:embedder_texture/embedder_texture.dart'; + +class EmbedderTexturePage extends AppStatefulWidget { + EmbedderTexturePage({ + super.key, + }); + + final Package package = packageEmbedderTexture; + + @override + State createState() => _EmbedderTexturePageState(); +} + +class _EmbedderTexturePageState extends AppState { + @override + Widget buildWide( + BuildContext context, + MediaQueryData media, + AppLocalizations l10n, + ) { + return BlockLayout( + model: getIt(), + title: widget.package.key, + builder: (context, child, model) { + return const Padding( + padding: EdgeInsets.all(20), + child: Center( + child: EmbedderTexture( + width: 320, + height: 320, + ), + ), + ); + }, + ); + } +} diff --git a/example/lib/packages/packages.dart b/example/lib/packages/packages.dart index f1a3ad7..20e671f 100644 --- a/example/lib/packages/packages.dart +++ b/example/lib/packages/packages.dart @@ -34,9 +34,11 @@ import 'package:flutter_example_packages/packages/sqflite/package.dart'; import 'package:flutter_example_packages/packages/universal_io/package.dart'; import 'package:flutter_example_packages/packages/wakelock_plus/package.dart'; import 'package:flutter_example_packages/packages/xdga_directories/package.dart'; +import 'package:flutter_example_packages/packages/embedder_texture/package.dart'; /// List app packages final packages = [ + packageEmbedderTexture, packageBatteryPlus, packageBuildRunner, packageCachedNetworkImage, diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt index 7d94d0e..3628d64 100644 --- a/example/linux/CMakeLists.txt +++ b/example/linux/CMakeLists.txt @@ -41,7 +41,7 @@ endif() # of modifying this function. function(APPLY_STANDARD_SETTINGS TARGET) target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) +# target_compile_options(${TARGET} PRIVATE -Wall -Werror) target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") endfunction() diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc index d0e7f79..98072de 100644 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -6,9 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) embedder_texture_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "EmbedderTexturePlugin"); + embedder_texture_plugin_register_with_registrar(embedder_texture_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake index b29e9ba..fc23779 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/example/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + embedder_texture flutter_secure_storage_linux ) diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 746b27b..b59032c 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -163,6 +163,10 @@ dependencies: # ref: master # path: packages/wakelock_plus/wakelock_plus_aurora + ## Test embedder texture plugin + embedder_texture: + path: ../packages/embedder_texture + dev_dependencies: flutter_test: sdk: diff --git a/packages/embedder_texture/.gitignore b/packages/embedder_texture/.gitignore new file mode 100644 index 0000000..d920ae6 --- /dev/null +++ b/packages/embedder_texture/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/packages/embedder_texture/analysis_options.yaml b/packages/embedder_texture/analysis_options.yaml new file mode 100644 index 0000000..f9b3034 --- /dev/null +++ b/packages/embedder_texture/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/packages/embedder_texture/lib/embedder_texture.dart b/packages/embedder_texture/lib/embedder_texture.dart new file mode 100644 index 0000000..ebd067d --- /dev/null +++ b/packages/embedder_texture/lib/embedder_texture.dart @@ -0,0 +1,54 @@ + +import 'package:flutter/material.dart'; + +import 'embedder_texture_platform_interface.dart'; + +class EmbedderTexture extends StatefulWidget { + const EmbedderTexture({ + super.key, + required this.width, + required this.height, + }); + + final double width; + final double height; + + @override + State createState() => _EmbedderTextureState(); +} + +class _EmbedderTextureState extends State { + int _textureID = 0; + + @override + initState() { + super.initState(); + EmbedderTexturePlatform.instance + .create(widget.width, widget.height) + .then((value) => setState(() { + if (mounted) { + _textureID = value!; + } + })); + } + + @override + void dispose() { + super.dispose(); + EmbedderTexturePlatform.instance.remove(_textureID); + } + + @override + Widget build(BuildContext context) { + if (_textureID != 0) { + return SizedBox( + width: widget.width, + height: widget.height, + child: Center( + child: Texture(textureId: _textureID) + ), + ); + } + return const SizedBox.shrink(); + } +} diff --git a/packages/embedder_texture/lib/embedder_texture_method_channel.dart b/packages/embedder_texture/lib/embedder_texture_method_channel.dart new file mode 100644 index 0000000..d73f946 --- /dev/null +++ b/packages/embedder_texture/lib/embedder_texture_method_channel.dart @@ -0,0 +1,27 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'embedder_texture_platform_interface.dart'; + +/// An implementation of [LinuxOpenglPlatform] that uses method channels. +class MethodChannelEmbedderTexture extends EmbedderTexturePlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('embedder_texture'); + + @override + Future create(double width, double height) async { + final version = await methodChannel.invokeMethod('create', { + 'width': width, + 'height': height, + }); + return version; + } + + @override + Future remove(int textureId) async { + await methodChannel.invokeMethod('remove', { + 'textureId': textureId, + }); + } +} diff --git a/packages/embedder_texture/lib/embedder_texture_platform_interface.dart b/packages/embedder_texture/lib/embedder_texture_platform_interface.dart new file mode 100644 index 0000000..1337c50 --- /dev/null +++ b/packages/embedder_texture/lib/embedder_texture_platform_interface.dart @@ -0,0 +1,33 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'embedder_texture_method_channel.dart'; + +abstract class EmbedderTexturePlatform extends PlatformInterface { + /// Constructs a LinuxOpenglPlatform. + EmbedderTexturePlatform() : super(token: _token); + + static final Object _token = Object(); + + static EmbedderTexturePlatform _instance = MethodChannelEmbedderTexture(); + + /// The default instance of [LinuxOpenglPlatform] to use. + /// + /// Defaults to [MethodChannelLinuxOpengl]. + static EmbedderTexturePlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [LinuxOpenglPlatform] when + /// they register themselves. + static set instance(EmbedderTexturePlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future create(double width, double height) { + throw UnimplementedError('create() has not been implemented.'); + } + + Future remove(int textureId) { + throw UnimplementedError('remove() has not been implemented.'); + } +} diff --git a/packages/embedder_texture/linux/CMakeLists.txt b/packages/embedder_texture/linux/CMakeLists.txt new file mode 100644 index 0000000..980b84a --- /dev/null +++ b/packages/embedder_texture/linux/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.10) + +set(PROJECT_NAME "embedder_texture") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "embedder_texture_plugin") + +list(APPEND PLUGIN_SOURCES + "fl_my_texture_gl.cc" + "embedder_texture_plugin.cc" +) + +add_library(${PLUGIN_NAME} SHARED + ${PLUGIN_SOURCES} +) + +apply_standard_settings(${PLUGIN_NAME}) + +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + +# sudo apt-get install libglew-dev +target_link_libraries(${PLUGIN_NAME} PUBLIC + EGL + GLEW + GL +) diff --git a/packages/embedder_texture/linux/embedder_texture_plugin.cc b/packages/embedder_texture/linux/embedder_texture_plugin.cc new file mode 100644 index 0000000..fc79fcf --- /dev/null +++ b/packages/embedder_texture/linux/embedder_texture_plugin.cc @@ -0,0 +1,198 @@ +#include "include/embedder_texture/embedder_texture_plugin.h" +#include "include/fl_my_texture_gl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "embedder_texture_plugin_private.h" + +#define EMBEDDER_TEXTURE_PLUGIN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), embedder_texture_plugin_get_type(), \ + LinuxOpenglPlugin)) + +struct _LinuxOpenglPlugin +{ + GObject parent_instance; + + unsigned int width = 0; + unsigned int height = 0; + unsigned int texture_id = 0; + + FlTexture *texture = nullptr; + FlTextureRegistrar *texture_registrar = nullptr; + + GdkGLContext *context = nullptr; + GdkWindow *window = nullptr; + FlMyTextureGL *myTexture = nullptr; + FlView *fl_view = nullptr; +}; + +G_DEFINE_TYPE(LinuxOpenglPlugin, embedder_texture_plugin, g_object_get_type()) + +// Called when a method call is received from Flutter. +static void embedder_texture_plugin_handle_method_call( + LinuxOpenglPlugin *self, + FlMethodCall *method_call) +{ + + g_autoptr(FlMethodResponse) response = nullptr; + + const gchar *method = fl_method_call_get_name(method_call); + + if (strcmp(method, "create") == 0) + { + response = create(self, method_call); + } + else if (strcmp(method, "remove") == 0) + { + response = remove(self, method_call); + } + else + { + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + } + + fl_method_call_respond(method_call, response, nullptr); +} + +void set_color(LinuxOpenglPlugin *self) +{ + gdk_gl_context_make_current(self->context); + + static GLfloat pixels[] = + { + 1, 0, 0, + 0, 1, 0, + 0, 0, 1, + 1, 1, 1 + }; + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_FLOAT, pixels); + + fl_texture_registrar_mark_texture_frame_available(self->texture_registrar, self->texture); +} + +FlMethodResponse *create( + LinuxOpenglPlugin *self, + FlMethodCall *method_call) +{ + GError *error = NULL; + FlValue *args = fl_method_call_get_args(method_call); + FlValue *w = fl_value_lookup_string(args, "width"); + FlValue *h = fl_value_lookup_string(args, "height"); + + if (w != nullptr) + { + self->width = fl_value_get_float(w); + } + + if (h != nullptr) + { + self->height = fl_value_get_float(h); + } + + if (self->width != 0 && self->height != 0) + { + self->window = gtk_widget_get_parent_window(GTK_WIDGET(self->fl_view)); + self->context = gdk_window_create_gl_context(self->window, &error); + + gdk_gl_context_make_current(self->context); + + glGenTextures(1, &self->texture_id); + glBindTexture(GL_TEXTURE_2D, self->texture_id); + + // drawing on the texture + set_color(self); + + self->myTexture = fl_my_texture_gl_new(GL_TEXTURE_2D, self->texture_id, self->width, self->height); + self->texture = FL_TEXTURE(self->myTexture); + + fl_texture_registrar_register_texture(self->texture_registrar, self->texture); + gdk_gl_context_clear_current(); + + g_autoptr(FlValue) result = fl_value_new_int(reinterpret_cast(self->texture)); + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); + } + + return FL_METHOD_RESPONSE(fl_method_error_response_new( + "500", + "Method create() called without passing width or height parameters!", + nullptr)); +} + +FlMethodResponse *remove( + LinuxOpenglPlugin *self, + FlMethodCall *method_call) +{ + FlValue *args = fl_method_call_get_args(method_call); + FlValue *id = fl_value_lookup_string(args, "textureId"); + + if (id != nullptr) + { + int textureId = fl_value_get_int(id); + fl_texture_registrar_unregister_texture(self->texture_registrar, self->texture); + g_autoptr(FlValue) result = fl_value_new_null(); + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); + } + + return FL_METHOD_RESPONSE(fl_method_error_response_new( + "500", + "Method remove() called without passing textureId parameters!", + nullptr)); +} + +static void embedder_texture_plugin_dispose(GObject *object) +{ + G_OBJECT_CLASS(embedder_texture_plugin_parent_class)->dispose(object); +} + +static void embedder_texture_plugin_class_init(LinuxOpenglPluginClass *klass) +{ + G_OBJECT_CLASS(klass)->dispose = embedder_texture_plugin_dispose; +} + +static void embedder_texture_plugin_init(LinuxOpenglPlugin *self) {} + +static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, + gpointer user_data) +{ + LinuxOpenglPlugin *plugin = EMBEDDER_TEXTURE_PLUGIN(user_data); + embedder_texture_plugin_handle_method_call(plugin, method_call); +} + +void embedder_texture_plugin_register_with_registrar(FlPluginRegistrar *registrar) +{ + LinuxOpenglPlugin *plugin = EMBEDDER_TEXTURE_PLUGIN( + g_object_new(embedder_texture_plugin_get_type(), nullptr)); + + plugin->fl_view = fl_plugin_registrar_get_view(registrar); + plugin->texture_registrar = fl_plugin_registrar_get_texture_registrar(registrar); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(FlMethodChannel) channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + "embedder_texture", + FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(channel, method_call_cb, + g_object_ref(plugin), + g_object_unref); + + GLenum err = glewInit(); + if (GLEW_OK != err) + { + printf("%s", "Error!"); + printf("%s", glewGetErrorString(err)); + return; + } + + g_object_unref(plugin); +} diff --git a/packages/embedder_texture/linux/embedder_texture_plugin_private.h b/packages/embedder_texture/linux/embedder_texture_plugin_private.h new file mode 100644 index 0000000..c5f5e75 --- /dev/null +++ b/packages/embedder_texture/linux/embedder_texture_plugin_private.h @@ -0,0 +1,7 @@ +#include + +#include "include/embedder_texture/embedder_texture_plugin.h" + +FlMethodResponse *create(LinuxOpenglPlugin *self, FlMethodCall *method_call); + +FlMethodResponse *remove(LinuxOpenglPlugin *self, FlMethodCall *method_call); diff --git a/packages/embedder_texture/linux/fl_my_texture_gl.cc b/packages/embedder_texture/linux/fl_my_texture_gl.cc new file mode 100644 index 0000000..6b2eea0 --- /dev/null +++ b/packages/embedder_texture/linux/fl_my_texture_gl.cc @@ -0,0 +1,44 @@ +#include "include/fl_my_texture_gl.h" + +G_DEFINE_TYPE(FlMyTextureGL, + fl_my_texture_gl, + fl_texture_gl_get_type()) + +static gboolean fl_my_texture_gl_populate(FlTextureGL *texture, + uint32_t *target, + uint32_t *name, + uint32_t *width, + uint32_t *height, + GError **error) +{ + FlMyTextureGL* f = (FlMyTextureGL*) texture; + *target = f->target; + *name = f->name; + *width = f->width; + *height = f->height; + return true; +} + +FlMyTextureGL *fl_my_texture_gl_new(uint32_t target, + uint32_t name, + uint32_t width, + uint32_t height) +{ + auto r = FL_MY_TEXTURE_GL(g_object_new(fl_my_texture_gl_get_type(), nullptr)); + r->target = target; + r->name = name; + r->width = width; + r->height = height; + return r; +} + +static void fl_my_texture_gl_class_init( + FlMyTextureGLClass *klass) +{ + FL_TEXTURE_GL_CLASS(klass)->populate = + fl_my_texture_gl_populate; +} + +static void fl_my_texture_gl_init(FlMyTextureGL *self) +{ +} diff --git a/packages/embedder_texture/linux/include/embedder_texture/embedder_texture_plugin.h b/packages/embedder_texture/linux/include/embedder_texture/embedder_texture_plugin.h new file mode 100644 index 0000000..ccaef59 --- /dev/null +++ b/packages/embedder_texture/linux/include/embedder_texture/embedder_texture_plugin.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_ +#define FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_ + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +typedef struct _LinuxOpenglPlugin LinuxOpenglPlugin; +typedef struct { + GObjectClass parent_class; +} LinuxOpenglPluginClass; + +FLUTTER_PLUGIN_EXPORT GType embedder_texture_plugin_get_type(); + +FLUTTER_PLUGIN_EXPORT void embedder_texture_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_ diff --git a/packages/embedder_texture/linux/include/fl_my_texture_gl.h b/packages/embedder_texture/linux/include/fl_my_texture_gl.h new file mode 100644 index 0000000..d79d762 --- /dev/null +++ b/packages/embedder_texture/linux/include/fl_my_texture_gl.h @@ -0,0 +1,28 @@ +#ifndef FLUTTER_MY_TEXTURE_H +#define FLUTTER_MY_TEXTURE_H + +#include +#include +#include "embedder_texture/embedder_texture_plugin.h" +#include + +G_DECLARE_FINAL_TYPE(FlMyTextureGL, + fl_my_texture_gl, + FL, + MY_TEXTURE_GL, + FlTextureGL) + +struct _FlMyTextureGL +{ + FlTextureGL parent_instance; + uint32_t target; + uint32_t name; + uint32_t width; + uint32_t height; +}; + +FlMyTextureGL *fl_my_texture_gl_new(uint32_t target, + uint32_t name, + uint32_t width, + uint32_t height); +#endif // FLUTTER_MY_TEXTURE_H diff --git a/packages/embedder_texture/pubspec.yaml b/packages/embedder_texture/pubspec.yaml new file mode 100644 index 0000000..e398273 --- /dev/null +++ b/packages/embedder_texture/pubspec.yaml @@ -0,0 +1,23 @@ +name: embedder_texture +description: Example create texture. +version: 0.0.1 + +environment: + sdk: '>=3.0.6 <4.0.0' + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +flutter: + plugin: + platforms: + linux: + pluginClass: EmbedderTexturePlugin