The effect
Usually Flutter interacts with Android pages by occupying the entire phone screen, but there are some situations where Flutter does not meet this requirement. For example: The Android map is embedded in the Flutter page, the camera preview is embedded in the Flutter page, etc. These need to have both the Flutter page and the Android page in the mobile phone screen. Sometimes the Flutter page does not provide the relevant plug-ins or the plug-ins do not meet the requirements. Developers can customize it by referring to the methods in this article. The specific demo effect of this paper is as follows:
The development of
- First create a Flutter project in which you define the layout of a flutter:
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Android button hit $_counter times',
style: const TextStyle(fontSize: 17.0)),),),Container(
padding: const EdgeInsets.only(bottom: 15.0, left: 5.0),
child: Row(
children: <Widget>[
Image.asset('assets/flutter-mark-square-64.png', scale: 1.5).const Text('Flutter', style: TextStyle(fontSize: 30.0)),
],
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _sendFlutterIncrement,
child: const Icon(Icons.add),),);
}
Copy the code
The layout shown above is the top part of the Flutter image. The FloatingActionButton is set to display the interaction between the Flutter and Android. “Android button clicked $_counter times “displays the interaction result. Image.asset() displays the logo of a Flutter (marking the layout of the Flutter). This is done because Flutter and Android pages are nested with each other and interact with data.
- Creating a layout in Android:
<io.flutter.embedding.android.FlutterView
android:id="@+id/flutter_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/grey"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:id="@+id/button_tap"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/button_tap"
.
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/android"
.
/>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
.
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Copy the code
IO. Flutter. Embedding. Android. FlutterView is the need to show the flutter of the layout of the layout is the first step in writing, the rest of the part and the first step of logic is the same, There is a TextView(@+ ID/Button_tap) for displaying the results of the interaction, a TextView(@String/Android) for marking the Android page, and a button (FloatingActionButton) for interacting.
- Defining communication channels
- Flutter:
static const String _channel = 'increment';
static const String _pong = 'pong';
static const String _emptyMessage = ' ';
static const BasicMessageChannel<String> platform =
BasicMessageChannel<String>(_channel, StringCodec());
int _counter = 0;
@override
void initState(a) {
super.initState();
platform.setMessageHandler(_handlePlatformIncrement);
}
Future<String> _handlePlatformIncrement(String message) async {
setState(() {
_counter++;
});
return _emptyMessage;
}
void _sendFlutterIncrement(a) {
platform.send(_pong);
}
Copy the code
The communication channels in this code are BasicMessageChannel. We don’t use MethodChannel and EventChannel because BasicMessageChannel can communicate anywhere, anytime. The other two have their own limitations, which I won’t explain here. A later article will be devoted to these three communication channels. _channel is the name of the communication channel, which is unique and fixed. Once defined, the Android terminal must use the same name; otherwise, the communication fails. _pong is the message content that Flutter transmits to Android. Every time Android receives “pong”, the corresponding Flutter button clicks +1. Users can customize the message content and type. Just define the generics and message encoding mechanism for BasicMessageChannel (StringCodec is used in this article). If the content of the message is large, the developer can use Json for messaging. _counter is the number of Android button clicks that flutter receives. Each time the Android button is clicked, _counter +1. After the relevant variable or constant is defined, the developer needs to receive the message in initState() because BasicMessageChannel is two-way communication, Platform. SetMessageHandler (_handlePlatformIncrement) to process the message is received, _handlePlatformIncrement method shows the message is of type String, the message is asynchronous, After _counter+1, call setState() to refresh the layout and display the corresponding Android button clicks +1. Platform. send(_pong) is called when the Flutter button is clicked and BasicMessageChannel sends the message to Android.
- Android:
private static FlutterEngine flutterEngine;
private FlutterView flutterView;
private int counter;
private static final String CHANNEL = "increment";
private static final String EMPTY_MESSAGE = "";
private static final String PING = "ping";
privateBasicMessageChannel<String> messageChannel; .@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (flutterEngine == null) {
flutterEngine = new FlutterEngine(this, args); flutterEngine.getDartExecutor().executeDartEntrypoint( DartEntrypoint.createDefault() ); }... flutterView = findViewById(R.id.flutter_view); flutterView.attachToFlutterEngine(flutterEngine); messageChannel =new BasicMessageChannel<>(flutterEngine.getDartExecutor(), CHANNEL, StringCodec.INSTANCE);
messageChannel.
setMessageHandler(new MessageHandler<String>() {
@Override
public void onMessage(String s, Reply<String> reply) { onFlutterIncrement(); reply.reply(EMPTY_MESSAGE); }}); FloatingActionButton fab = findViewById(R.id.button); fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { sendAndroidIncrement(); }}); .Copy the code
A CHANNEL is the name and identifier of a communication CHANNEL. A CHANNEL must be consistent with a FLUTTER, otherwise it cannot communicate with a FLUTTER. The BasicMessageChannel is a communication channel that cannot communicate if a different one is used than the FLUTTER end. EMPTY_MESSAGE is the reply Android sends to a Flutter when it receives a Flutter message. This indicates that a Flutter knows that Android has received the message. The Android terminal will process the message in setMessageHandler when the flutter button is clicked +1(onFlutterIncrement()) and reply to acknowledge the received message (reply.reply(EMPTY_MESSAGE)). SendAndroidIncrement sends a message to the Flutter that the Flutter button has been clicked:
private void sendAndroidIncrement(a) {
messageChannel.send(PING);
}
Copy the code
PING is the message that Android sends to Flutter. When a Flutter receives the message, it clicks the button on Android +1 times. If a large number of messages are delivered, it is necessary to judge the specific message to confirm what processing needs to be done. In this paper, only one kind of message is delivered, so the parameters and methods of the message are not judged. In the code, flutterView is the Flutter layout to be displayed, while flutterEngine is the Flutter engine. FlutterView. AttachToFlutterEngine (flutterEngine) is to infuse flutterView life and energy, otherwise flutterView is not empty life and content control. The life cycle of flutterEngine and AppCompatActivity is tied together:
@Override
protected void onResume(a) {
super.onResume();
flutterEngine.getLifecycleChannel().appIsResumed();
}
@Override
protected void onPause(a) {
super.onPause();
flutterEngine.getLifecycleChannel().appIsInactive();
}
@Override
protected void onStop(a) {
super.onStop();
flutterEngine.getLifecycleChannel().appIsPaused();
}
@Override
protected void onDestroy(a) {
flutterView.detachFromFlutterEngine();
super.onDestroy();
}
Copy the code
Once there is a lifecycle binding in Android, it means that as long as you do what you’re told, you don’t mess up, and if there is a problem, it’s not its problem.
conclusion
- Flutter’s interaction with Android has been improved over time, and the latest version of Flutter is much simpler than earlier versions of Flutter.
- If it is possible to avoid the nesting of Flutter and Android, it should be avoided as the nesting of Flutter and Android is very energy consuming, which may cause problems such as lag, crash and high power consumption.
instructions
- Some code is not presented completely, please see demo for details.
- For infringement or error, please send an email to [email protected].