Start
In flutter development, there are always two unavoidable problems:
- The migration of native projects to flutter requires access to flutter in native projects
- In order to use some mature applications in flutter project, it is unavoidable to use various mature libraries, such as audio and video.
In this article, we will introduce the above two situations separately.
Accessing flutter interface in Android
flutter needs to be accessed as module in android project
Create a flutter module
Enter the current android project and run the following commands in the root directory:
flutter create -t module my_flutter
The above indicates creating a flutter module named my_flutter
Then run
cd my_flutter cd .android/ ./gradlew flutter:assembleDebug
At the same time, make sure that you add the following code to app/build.gradle in your android project directory:
android { compileSdkVersion 28 defaultConfig { ... } buildTypes { ... } //flutter Statement compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } }
Next, add the following code to settings.gradle in the root directory of the android project
include ':app' setBinding(new Binding([gradle: this])) evaluate(new File( rootDir.path + '/my_flutter/.android/include_flutter.groovy' ))
Finally, my_flutter needs to be introduced into app/build.gradle under the android project
dependencies { ... //Import flutter implementation project(':flutter') }
At this point, you can basically start accessing flutter content.
However, there is another problem to be noted at this time. If your android project has been migrated to androidx, you may encounter the following problems
The obvious reason for this problem is that flutter did not convert androidx when creating moudles, because the command to create moudles does not support androidx yet.
You can see the following issue
Generated Flutter Module Files Do Not Use AndroidX
Let's begin to solve this problem.
Solving the problems brought about by androidx
First, if your original android project has migrated to androidx, then grale.properties in the root directory must have the following contents
# Represents using androidx android.useAndroidX=true # Represents migrating third-party libraries to androidx android.enableJetifier=true
Now go to my_flutter directory and modify the dependency part of the library in your android project / my_flutter/.android/Flutter/build.gradle
If the default is as follows:
dependencies { testImplementation 'junit:junit:4.12' implementation 'com.android.support:support-v13:27.1.1' implementation 'com.android.support:support-annotations:27.1.1' }
Modify all dependencies to androidx versions:
dependencies { testImplementation 'junit:junit:4.12' implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'androidx.annotation:annotation:1.0.0' }
After clicking Sync Now Synchronization on android studio
Go to the following directory and modify Flutter.java and Flutter Fragment. Java respectively under your android project / my_flutter/.android/Flutter/src/main/java/io/flutter/facade / directory
Modify FlutterFragment.java
The original dependence is as follows
Replace the error section with the androidx version
import androidx.annotation.NonNull; import androidx.fragment.app.Fragment;
Modify Flutter.java
The original dependence is as follows
Replace the error section with the androidx version
import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent;
Now that androidx's problems are solved, we're ready to officially access Flutter.
Edit Entry in flutter
When you enter the lib directory in my_flutter directory, you can see the main.dart file that comes with the system. This is the default counter page. We modify part of it:
void main() => runApp(getRouter(window.defaultRouteName)); Widget getRouter(String name) { switch (name) { case 'route1': return MyApp(); default: return Center( child: Text('Unknown route: $name', textDirection: TextDirection.ltr), ); } }
Change the entry to enter by "route1" name
Next up is android.
Accessing flutter in android
Into the android project, in MainActivity, we do the following:
bt_flutter.setOnClickListener { val flutterView = Flutter.createView( this@MainActivity, lifecycle, "route1" ) val layout = ConstraintLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) layout.leftMargin = 0 layout.bottomMargin = 26 addContentView(flutterView, layout) }
As you can see from the above code, we show the counter page of flutter by clicking on a button. The actual results are as follows:
So android access to flutter is over. Here's android access to flutter
Accessing android Interface in Flutter
We can create a new flutter project to test this example
Because kotin is used, the following commands are used
flutter create -a kotlin counter_native
After the project is created, we can start. Before we start, we can first understand how to get the data in android from flutter.
Getting android data
The main way to get data is to use Method Channel.
Take a look at the MainActivity code in android
class MainActivity: FlutterActivity() { private val channelName = "samples.flutter.io/counter_native"; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) MethodChannel(flutterView, channelNameTwo).setMethodCallHandler { methodCall, result -> when(methodCall.method){ "getCounterData" -> { result.success(getCounterData()) } else -> { result.notImplemented(); } } } } private fun getCounterData():Int{ return 100; } }
In the MethodChannel result callback, we filtered and returned 100 directly if the method name was getCounterData.
Next, write the following code in flutter:
static const platform = const MethodChannel('samples.flutter.io/counter_native'); void getCounterData() async { int data; try { final int result = await platform.invokeMethod('getCounterData'); data = result; } on PlatformException catch (e) { data = -999; } setState(() { counterData = data; }); }
The results are as follows:
That's all for getting android data. Here's how to get android pages.
Get the layout of android
The layout of android is much more complex than the data.
Creating an android View
In the android project, we create a layout that we want to show in flutter. Here, we combine XML files to create the layout, but using xml, there will be the case that R files can not be found. At this time, the compiler will make an error and leave it alone for the time being:
class CounterView(context: Context, messenger: BinaryMessenger, id: Int) : PlatformView, MethodChannel.MethodCallHandler { private var methodChannel: MethodChannel = MethodChannel(messenger, "samples.flutter.io/counter_view_$id") private var counterData: CounterData = CounterData() private var view: View = LayoutInflater.from(context).inflate(R.layout.test_layout, null); private var myText: TextView init { methodChannel.setMethodCallHandler(this) myText = view.findViewById(R.id.tv_counter) } override fun getView(): View { return view } override fun dispose() { } override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) { when (methodCall.method) { "increaseNumber" -> { counterData.counterData++ myText.text = "current Android Of Text The numerical value is:${counterData.counterData}" result.success(counterData.counterData) } "decreaseNumber" -> { counterData.counterData-- myText.text = "current Android Of Text The numerical value is:${counterData.counterData}" result.success(counterData.counterData) } "decreaseSize" -> { if(myText.textSize > 0){ val size = myText.textSize myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size-1) result.success(myText.textSize) } else{ result.error("error", "size Can't be smaller!", null) } } "increaseSize" -> { if(myText.textSize < 100){ val size = myText.textSize myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size+1) result.success(myText.textSize) } else{ result.error("error", "size It can't be any bigger!", null) } } "setText" -> { myText.text = (methodCall.arguments as String) result.success(myText.text) } else -> { result.notImplemented(); } } } }
The CountData class above is a class for storing data creation:
class CounterData(var counterData: Int = 0) { }
Next, we create a CounterViewFactory class to get the layout:
class CounterViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { override fun create(context: Context, id: Int, o: Any?): PlatformView { return CounterView(context, messenger, id) } }
Finally, create a CounterViewPlugin.kt file for registering views, which is equivalent to initializing entries
class CounterViewPlugin{ fun registerWith(registrar: Registrar) { registrar.platformViewRegistry().registerViewFactory("samples.flutter.io/counter_view", CounterViewFactory(registrar.messenger())) } }
After the creation is completed, the view is registered in MainActivity:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) CounterViewPlugin().registerWith(flutterView.pluginRegistry.registrarFor("CounterViewPlugin")) ... }
Next, there are some things that need to be done in flutter
Getting android View in flutter
In flutter, to get an android view, you need to get it through android View.
Widget build(BuildContext context) { if (Platform.isAndroid) { return AndroidView( viewType: 'samples.flutter.io/counter_view', onPlatformViewCreated: _onPlatformViewCreated, ); } return Text( '$defaultTargetPlatform This layout is not yet supported'); }
In the onPlatform View Created method, we need to create a Method Channel to call the methods written in android. We can encapsulate a Controller to handle these logic:
final CounterController counterController; void _onPlatformViewCreated(int id) { if (widget.counterController == null) { return; } widget.counterController.setId(id); }
Here's CounterController
typedef void CounterViewCreatedCallBack(CounterController controller); class CounterController { MethodChannel _channel; void setId(int id){ _channel = new MethodChannel('samples.flutter.io/counter_view_$id'); print("id"); } Future increaseNumber() async { final int result = await _channel.invokeMethod( 'increaseNumber', ); print("result:${result}"); } Future decreaseNumber() async { final int result = await _channel.invokeMethod( 'decreaseNumber', ); } Future increaseSize() async { final result = await _channel.invokeMethod( 'increaseSize', ); } Future decreaseSize() async { final result = await _channel.invokeMethod( 'decreaseSize', ); } Future setText(String text) async { final result = await _channel.invokeMethod( 'setText',text, ); } }
The results are as follows:
appendix
A Todo-List project that is super-suited for Flutter introduction:
Last
If you see it here and think it's a good article, give it a compliment? If you think it's worth improving, please leave me a message. We will inquire carefully and correct the shortcomings. Thank you.
I hope you can forward, share and pay attention to me, and update the technology dry goods in the future. Thank you for your support! ___________
Android architects have a long way to go. Let's work together.