Flutter Interact
не только представляет новые средства разработки, но и включает в себя выпуск стабильной версии 1.12.
В отличие от предыдущих версий, версия 1.12.x внесла значительное количество небинарно-совместимых изменений в Flutter Framework
, такие как добавление параметра DecoderCallback
в метод load
класса ImageProvider
на уровне Dart, увеличение минимальной высоты TextField с 40 до 48, переход PageView на использование SliverLayoutBuilder вместо RenderSliverFillViewport и другие изменения обратной совместимости.
Однако указанные проблемы не являются критическими, так как они могут быть решены путём корректировки соответствующего кода на Dart. Однако самое существенное изменение заключается в обновлении API плагинов для Android, которое требует от пользователей выполнить адаптацию кода модулей Android и плагинов проекта Flutter.
С версии Flutter 1.12 команда Flutter начала использовать новую реализацию API плагинов для Android. После версии 1.12 Android начал использовать новый API плагинов, основанный на старом PluginRegistry.Registrar
. Этот последний не будет немедленно прекращен, однако рекомендован переход на новый API FlutterPlugin
. Кроме того, официальные плагины теперь полностью используют библиотеку AndroidX
.По сравнению со старым API, преимущества нового API заключаются в том, что он предоставляет более декомпозированное использование жизненного цикла для зависимостей плагинов. Например, ранее при использовании PluginRegistry.Registrar.activity()
, если Flutter ещё не был добавлен в Activity
, то вызов мог вернуть null
. Также плагины не знали, когда они были загружены и использованы движком. Новый API решил эти проблемы.
Для использования нового API Android-плагины должны реализовать FlutterPlugin
и MethodCallHandler
, а также предоставляют ActivityAware
для управления жизненным циклом Activity
и получения доступа к нему, а также ServiceAware
для управления жизненным циклом Service
и получения доступа к нему. Конкретные шаги миграции следуют:
*Plugin.java
) для реализации FlutterPlugin
, то есть обычно Android-плагины должны наследовать FlutterPlugin
и MethodCallHandler
. Если требуется использование Activity
, то следует наследовать также ActivityAware
.Перевод выполнен согласно правилам, указанному типу документа (markdown) и целевому языку (русский).Раньше плагины Flutter были непосредственными наследниками MethodCallHandler
, предоставляя метод registerWith
; после обновления код выглядит следующим образом. В новой версии также сохранён метод registerWith
для обеспечения обратной совместимости с предыдущими версиями. В новом API метод MethodCallHandler
инициализируется и создаётся в методе onAttachedToEngine
, а освобождается в методе onDetachedFromEngine
. Также предоставлены четыре реализованных метода, связанных с жизненным циклом активности.```java
public class FlutterPluginTestNewPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
private static MethodChannel channel;
// Сохранение обратной совместимости со старыми версиями public static void registerWith(Registrar registrar) { Log.e("registerWith", "registerWith"); channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test_new"); channel.setMethodCallHandler(new FlutterPluginTestNewPlugin()); }
@Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { if (call.method.equals("getPlatformVersion")) { Log.e("onMethodCall", call.method); result.success("Android " + android.os.Build.VERSION.RELEASE); Map<String, String> map = new HashMap<>(); map.put("message", "сообщение"); channel.invokeMethod("onMessageTest", map); } else { result.notImplemented(); } }
//// Два метода из интерфейса FlutterPlugin @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { Log.e("onAttachedToEngine", "onAttachedToEngine"); channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_plugin_test_new"); channel.setMethodCallHandler(new FlutterPluginTestNewPlugin()); }
@Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { Log.e("onDetachedFromEngine", "onDetachedFromEngine"); }
/// Методы жизненного цикла активности @Override public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) { Log.e("onAttachedToActivity", "onAttachedToActivity"); }
@Override public void onDetachedFromActivityForConfigChanges() { Log.e("onDetachedFromActivityForConfigChanges", "onDetachedFromActivityForConfigChanges"); }
@Override public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) { Log.e("onReattachedToActivityForConfigChanges", "onReattachedToActivityForConfigChanges"); } }
@Override
public void onDetachedFromActivity() {
Log.e("onDetachedFromActivity", "onDetachedFromActivity");
}
}
```---
**Кратко говоря, вам нужно реализовать множественное наследование от интерфейса `FlutterPlugin`, а затем в методе `onAttachedToEngine` создать объект `MethodCallHandler` и вызвать `setMethodCallHandler`. После этого аналогичным образом выполните инициализацию в оставшемся методе `registerWith`.**
При нормальной работе плагин будет вызывать следующие входные данные:
```
2019-12-19 18:01:31.481 24809-24809/? E/onAttachedToEngine: onAttachedToEngine
2019-12-19 18:01:31.481 24809-24809/? E/onAttachedToActivity: onAttachedToActivity
2019-12-19 18:01:31.830 24809-24809/? E/onMethodCall: getPlatformVersion
2019-12-19 18:05:48.051 24809-24809/com.shuyu.flutter_plugin_test_new_example E/onDetachedFromActivity: onDetachedFromActivity
2019-12-19 18:05:48.052 24809-24809/com.shuyu.flutter_plugin_test_new_example E/onDetachedFromEngine: onDetachedFromEngine
```
Кроме того, **если ваш плагин предназначен для лучшей совместимости с более старыми версиями Flutter Plugin, то статический метод `registerWith` следует изменить на следующий код:**
```
public static void registerWith(Registrar registrar) {
channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test_new");
channel.startListening(registrar.messenger());
}
```
---
Если плагин написан на Kotlin, изменения могут выглядеть следующим образом.
2\. Если это возможно, можно изменить объект `MainActivity` основного проекта, **заменив наследование от `FlutterActivity` из пакета `io.flutter.app.FlutterActivity` на `io.flutter.embedding.android.FlutterActivity`. В этом случае плагины будут автоматически регистрироваться; если же это невозможно, придется самостоятельно вызвать метод `GeneratedPluginRegistrant.registerWith()`.** Конечно, при этом может возникнуть предупреждение о неправильном вызове `registerWith()`.```
/// Этот метод требует самостоятельного вызова, если в трех вариантах AndroidManifest.xml не включена конфигурация flutterEmbedding v2
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
```
> Если вы используете три варианта AndroidManifest.xml с включенной конфигурацией flutterEmbedding v2, тогда сгенерированный `GeneratedPluginRegistrant` будет использовать `FlutterEngine`; если же конфигурация v2 не используется, то будет использоваться `PluginRegistry`. После этого потребуется также отредактировать файл `AndroidManifest.xml`, как показано ниже. Необходимо удалить существующий `meta-data` с именем `io.flutter.app.android.SplashScreenUntilFirstFrame`, а затем добавить два новых `meta-data`: `io.flutter.embedding.android.SplashScreenDrawable` и `io.flutter.embedding.android.NormalTheme`. Это используется для установки стилей placeholder при запуске приложения и темы после запуска.

**Здесь следует обратить внимание, что, как показано выше, требуется конфигурация `flutterEmbedding` внутри узла `application`, чтобы включить новую логику загрузки плагинов.**
```xml
<meta-data
android:name="flutterEmbedding"
android:value="2" />
```
4. После этого можно выполнить команду `flutter packages get`, чтобы сгенерировать новый файл `GeneratedPluginRegistrant`. Как показано ниже, новые `FlutterPlugin` будут загружаться непосредственно через `flutterEngine.getPlugins().add`, а старые реализации плагинов будут совместимо загружены через `ShimPluginRegistry` в реализацию версии 2.```java
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.view.FlutterView;
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (registry.hasPlugin("shared_preferences")) {
new SharedPreferencesPlugin().onRegister(registry.registrarFor("shared_preferences"));
}
// Добавление других плагинов аналогично
}
}
``` private static void addPlugins(FlutterView view, PluginRegistry registry) {
view.getPlugins().add(new FlutterEnginePlugin());
// Добавление других плагинов аналогично
}
}
@Keep
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
flutterEngine.getPlugins().add(new io.flutter.plugins.androidintent.AndroidIntentPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.connectivity.ConnectivityPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.deviceinfo.DeviceInfoPlugin());
io.github.ponnamkarnik.toast.fluttertoast.FluttertoastPlugin.registerWith(shimPluginRegistry.registrarFor("io.github.ponnamkarnik.toast.fluttertoast.FluttertoastPlugin"));
flutterEngine.getPlugins().add(new io.flutter.plugins.packageinfo.PackageInfoPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin());
com.baseflow.permissionhandler.PermissionHandlerPlugin.registerWith(shimPluginRegistry.registrarFor("com.baseflow.permissionhandler.PermissionHandlerPlugin"));
flutterEngine.getPlugins().add(new io.flutter.plugins.share.SharePlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin());
com.tekartik.sqflite.SqflitePlugin.registerWith(shimPluginRegistry.registrarFor("com.tekartik.sqflite.SqflitePlugin"));
flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin());
}
}
5. Последним шагом является необязательное обновление в файле `gradle-wrapper.properties`, который находится в директории `android/gradle/wrapper`. Вы можете изменить значение `distributionUrl` на `https\://services.gradle.org/distributions/gradle-5.6.2-all.zip`.
Вместе с этим вам потребуется также обновить версию Gradle в файле build.gradle
, расположенном в директории android/
до значения com.android.tools.build:gradle:3.5.0
. Также можно обновить версию плагина Kotlin до '1.3.50'
.### Другие обновления
Если ваш проект ещё не использует Androidx
, вы можете добавить следующий код в файл gradle.properties
, расположенный в директории android/
, чтобы активировать его:
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
Нужно добавить .flutter-plugins-dependencies
в список игнорируемых файлов.
После обновления, если у вас возникли вопросы относительно увеличения размера пакета iOS, вы можете обратиться к сообществу GitHub, где подробно объясняется причина этого поведения. Кроме того, если вы столкнулись с проблемой отсутствия логов при работе с реальным устройством iOS 13, рекомендуется просмотреть этот issue.
Podfile
для iOS. При использовании старого файла могут появиться соответствующие предупреждения. Ниже приведены примеры таких конфигураций.# Отмените этот символ, чтобы определить глобальную платформу для вашего проекта
# platform :ios, '9.0'
# Аналитика CocoaPods отправляет сетевые данные синхронно, что может влиять на время сборки Flutter.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
``````ruby
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exist?(file_abs_path)
return []
end
generated_key_values = {}
skip_line_start_symbols = ['#', '/']
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Неверное определение плагина: #{line}"
end
end
generated_key_values
end
target 'Runner' do
use_frameworks!
use_modular_headers!
# Flutter Pod
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Копируем Flutter.framework и Flutter.podspec в Flutter/, чтобы было что связывать, если скрипт бэкэнда Xcode ещё не запущен.
# Этот скрипт скопирует правильную версию фреймворка для отладки/профиля/релиза в зависимости от текущей выбранной конфигурации Xcode.
# CocoaPods не будет внедрять фреймворк при установке пакета (до того как любые этапы сборки могут сгенерировать его), если dylib не существует.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Файл Generated.xcconfig должен существовать. Если вы запускаете pod install вручную, убедитесь, что flutter pub get выполнено перед этим"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']
end
end
``````ruby
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )