In addition to bringing new development tools to Flutter Interact, one of the biggest highlights of Flutter Interact is the release of the stable version 1.12.
Different from previous versions, version 1.12.x contains a number of incompatible updates to the Flutter Framework, such as in the Dart layer: ImageProvider load added the DecoderCallback parameter, TextField’s minimum height changed from 40 to 48, and PageView started using the SliverLayoutBuilder Deprecate incompatible upgrades such as RenderSliverFillViewport.
None of these issues are fatal, however, because the Dart code can be used to fix the problem. The most significant changes are to the Android plugins APIs. This adjustment requires users to readjust the Code of the Android modules and plug-ins in the Flutter project.
A, Android Plugins
1, the introduction
With The start of Flutter 1.12, the Flutter team changed the Implementation code of the Android plugin. After Flutter 1.12, Android will use the new plugin API, based on the old pluginregistry.registrar. However, the official suggestion is to migrate to the new API based FlutterPlugin. In addition, the official suggestion of the new version is to directly use Androidx support, and the official plug-ins have been fully upgraded to Androidx.
The advantages of the new API over the old one are: For the plug-in depends on the life cycle of provides a more decoupling method of use, such as PluginRegistry. Before the Registrar. The activity () when use, if Flutter haven’t added to the activity may return null, Also, plug-ins don’t know when they will be loaded and used by the engine, and these issues have been optimized in the new API.
1, upgrade,
The Android plugin is implemented on the new API using FlutterPlugin and MethodCallHandler, as well as providing ActivityAware for Activity lifecycle management and retrieval. Provide ServiceAware for Service lifecycle management and acquisition. The migration steps are as follows:
1. Update the main plug-in class (* plugin.java) to implement FlutterPlugin. Normally, Android plug-ins need to inherit FlutterPlugin and MethodCallHandler interfaces. If you need to use the Activity, you need to inherit the ActivityAware interface.
Previous Flutter plug-ins inherited MethodCallHandler directly and provided registerWith static methods; The registerWith static method is retained, as shown in the code below, because compatibility support is still needed for older versions, and the new API’s MethodCallHandler will be initialized and built in the onAttachedToEngine method. Released in the onDetachedFromEngine method; At the same time, the four implementation methods of the Activity also provide the corresponding operation logic.
public class FlutterPluginTestNewPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware { private static MethodChannel channel; Registrar registerWith public static void registerWith(Registrar registerWith) {log.e ("registerWith"."registerWith");
channel = new MethodChannel(registerWith.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"."message");
channel.invokeMethod("onMessageTest", map);
} else{ result.notImplemented(); }} //// FlutterPluginBinding @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) {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"); }}Copy the code
Simply put, you need multiple inheritanceFlutterPlugin
Interface, and then inonAttachedToEngine
Build in methodMethodCallHandler
andsetMethodCallHandler
After the synchronization in the reservedregisterWith
Method implementationonAttachedToEngine
Similar initialization in.
The input that the plug-in normally calls after running looks like this:
The 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 18:05:48. 2019-12-19, 051, 24809-24809 / com. Shuyu. Flutter_plugin_test_new_example E/onDetachedFromActivity: OnDetachedFromActivity 18:05:48. 2019-12-19, 052, 24809-24809 / com. Shuyu. Flutter_plugin_test_new_example E/onDetachedFromEngine: onDetachedFromEngineCopy the code
In addition, if you want the Plugin to be compatible with older versions of the Flutter Plugin, the registerWith static method should be adjusted to look like this:
public static void registerWith(Registrar registrar) {
channel = new MethodChannel(registrar.messenger(), "flutter_plugin_test_new");
channel.startListening(registrar.messenger());
}
Copy the code
Of course, if it’s a Kotlin plug-in, you might make changes similar to those shown below.
2. Modify the MainActivity object of the main project if conditions permit. Will inherit FlutterActivity from IO. Flutter. App. FlutterActivity replaced with IO. Flutter. Embedding. Android. FlutterActivity, after the plugin will automatically register; If the conditions are not allowed to not inherit FlutterActivity need to manually call GeneratedPluginRegistrant. RegisterWith method, of course, here may be prompted registerWith method invocation is not correct, Don’t be so quick to ignore it and go down.
// If flutterEmbedding v2 is not configured on androidmanifest.xml in the following 3, @override public void configureFlutterEngine(@nonnull FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); }Copy the code
If, in accordance with 3 opened the same v2, FlutterEngine generated GeneratedPluginRegistrant is used, no configuration is PluginRegistry v2 use.
3, after also need to adjust the AndroidManifest. XML file, as shown in the figure below, needs to be original IO. Flutter. App. Android. SplashScreenUntilFirstFrame the meta – data removed, Then add to the IO. Flutter. Embedding. Android. SplashScreenDrawable and IO. Flutter. Embedding. The android. NormalTheme both meta – data, This is mainly used for placeholder map style when the application is opened and theme style after entering the application.
Also note here, as shown in the figure aboveapplication
Intra-node ConfigurationflutterEmbedding
To take effect the new plug-in loading logic.
<meta-data
android:name="flutterEmbedding"
android:value="2" />
Copy the code
4, after can perform flutter packages get to generate new GeneratedPluginRegistrant file, as shown in the following code, The new FlutterPlugin will be loaded directly by flutterengine.getPlugins ().add, while the old plugin implementation will be loaded compatibly into the V2 implementation via ShimPluginRegistry.
@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.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin.registerWith(shimPluginRegistry.registrarFor("io.github.ponnamkarthik.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()); }}Copy the code
Properties file under Android /gradle/wrapper. You can change the distributionUrl to the gradle-5.6.2-all.zip version. At the same time need to android/directory of the build. Gradle file gradle is also modified to com. Android. View the build: gradle: 3.5.0; The kotlin plugin version can also be upgraded to ext.kotlin_version = ‘1.3.50’.
Other upgrades
1. If your previous project is not Androidx enabled, you can open Androidx by adding the following code to gradle.properties under Android /.
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
Copy the code
Add. Flutter -plugins-dependencies to the ignored file.
If you have any doubts about the iOS package after the update, check out #47101, which has a good description of the cause and effect relationship. In addition, if you can’t enter logs on iOS13, check #41133.
4. As you can see below, the iOS Podfile has also been modified in the 1.12.x update. If you use an older file, you may get a warning.
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if! File.exists? file_abs_pathreturn [];
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(pattern=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 "Invalid plugin specification: #{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)# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') unless File.exist? (generated_xcode_build_settings_path) raise"Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; unless File.exist? (copied_framework_path) FileUtils.cp_r(File.join(cached_framework_dir,'Flutter.framework'), copied_flutter_dir) end unless File.exist? (copied_podspec_path) FileUtils.cp(File.join(cached_framework_dir,'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('.. /.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks'.'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
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
Copy the code
All right, that’s it for now.
Flutter article summary address:
A complete series of articles on Flutter
A series of articles on the world outside Flutter
Resources to recommend
- Making: github.com/CarGuo
- Open Source Flutter complete project:Github.com/CarGuo/GSYG…
- Open Source Flutter Multi-case learning project:Github.com/CarGuo/GSYF…
- Open Source Fluttre Combat Ebook Project:Github.com/CarGuo/GSYF…
- Open Source React Native project: github.com/CarGuo/GSYG…