64fluent communicates with Native

Flutter plug-in development (II)

1. Introduction

In the last article, we talked about Flutter, and there are corresponding cases, but we found that we did not obtain the context, which is obviously unreasonable in the actual development. Without the context, we cannot apply for permission, jump to the gallery and other functions. This article mainly analyzes the problems from the perspective of actual development. Take Android as an example.

The old way of registerWith has changed. It is recommended to write plug-ins in a new way

2. Think about a problem

Where does the context and Activity obtained by the plug-in come from?

In the previous article, we talked about the case of example. In the case, application and MainActivity are registered in AndroidManifest. Therefore, just as you think, context comes from here. Our fluent interface relies on fluteractivity, so this is why we can obtain context and Activity

3. Use

1. Get context

        flutterPluginBinding.applicationContext

2. Get activity

Need to implement this ActivityAware Interface.

package com.uih.flutternative

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler

/**
 * Creation date: May 28, 2021 on 16:57
 * Description:
 * Author: Jiang li. v
 */
class MyPlugin2 : FlutterPlugin, ActivityAware, MethodCallHandler {
    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    }

    override fun onDetachedFromActivity() {
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    }

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        binding.activity
        binding.addActivityResultListener { requestCode, resultCode, data ->
            return@addActivityResultListener false
        }
    }

    override fun onDetachedFromActivityForConfigChanges() {
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    }
}

3. Background service

Optional) if you want your plug-in to remain in the background service at any time, please implement this ServiceAware Interface. Similar to activity, no more explanation

be careful:

Your plug-in may or may not be associated with a given Flutter experience at any given time. You should pay attention to the behavior of initializing the plug-in onAttachedToEngine() in and then cleaning up the reference onDetachedFromEngine() of the plug-in in.
FlutterPluginBinding provides some important references for your plug-ins:

  • binding.getFlutterEngine()
    The returned FlutterEngine is where your plug-in is installed to provide access to components, such as DartExecutor, FlutterRenderer, etc.
  • binding.getApplicationContext()
    Returns the Android Application of the running application in Context.

4. Case, write a permission request plug-in

Take Android as an example. Since the permission requires Activity, we need to inherit ActivityAware

1.Android:

package com.example.flutter_permission

import androidx.annotation.NonNull

import android.app.Activity
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar

/** FlutterPermissionPlugin */
class FlutterPermissionPlugin: FlutterPlugin, ActivityAware, MethodCallHandler {
  private lateinit var channel: MethodChannel
  private lateinit var permissionUtils2: PermissionUtils2
  private lateinit var activity: Activity
  override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(binding.binaryMessenger, "flutter_permission")
    channel.setMethodCallHandler(this)
    binding.applicationContext
  }

  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  override fun onDetachedFromActivity() {
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    activity = binding.activity
    permissionUtils2 = PermissionUtils2()
  }

  override fun onDetachedFromActivityForConfigChanges() {
  }

  override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    when (call.method) {
      "requestPermission" -> {
        permissionUtils2.setCallBack {
          result.success(it)
        }
        permissionUtils2.requestPermission(activity)
      }
      "getPlatformVersion" -> {
        result.success("Android ${android.os.Build.VERSION.RELEASE}")
      }
      else -> {
        result.notImplemented()
      }
    }
  }
}

PermissionUtils2: / / apply for location permission

package com.example.flutter_permission;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import androidx.annotation.NonNull;

import com.hjq.permissions.OnPermission;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;

import java.util.List;

/**
 * Created on: May 28, 2021 on 17:26
 * Description:
 * Author: Jiang li
 */
public class PermissionUtils2 {
    private static final int SUCCESS = 0;
    private static final int DENIED = -1;
    private static final int FOREVER_DENIED = -2;
    private PermissionCallBack callBack;

    public void setCallBack(PermissionCallBack callBack) {
        this.callBack = callBack;
    }

    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == SUCCESS) {
                callBack.callBack(SUCCESS);
            } else if (msg.what == FOREVER_DENIED) {
                callBack.callBack(FOREVER_DENIED);
            } else {
                callBack.callBack(DENIED);
            }
        }
    };


    public interface PermissionCallBack {
        void callBack(int value);
    }

    void requestPermission(Activity activity) {
        XXPermissions.with(activity)
                .permission(Permission.Group.LOCATION) //Apply for location permission
                .request(new OnPermission() {
                    @Override
                    public void hasPermission(List<String> granted, boolean all) {
                        if (all) {
                            handler.obtainMessage(SUCCESS).sendToTarget();
                        } else {
                            handler.obtainMessage(DENIED).sendToTarget();
                        }
                    }

                    @Override
                    public void noPermission(List<String> denied, boolean never) {
                        if (never) {
                            handler.obtainMessage(FOREVER_DENIED).sendToTarget();
                        } else {
                            handler.obtainMessage(DENIED).sendToTarget();
                        }
                    }
                });
    }
}

Note: xxpermissions is the permission application framework written by brother wheel Daniel. Thank brother wheel for his selfless dedication. GitHub:https://github.com/getActivity/XXPermissions

build.gradle guide bag

    implementation 'com.hjq:xxpermissions:9.0'

Request permission in xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.flutter_permission">
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>

2. In Lib

import 'dart:async';

import 'package:flutter/services.dart';

class FlutterPermission {
  static const MethodChannel _channel =
      const MethodChannel('flutter_permission');

  static Future<String> get platformVersion async {
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
	
  ///Apply for permission
  static Future<int> get requestPermission async {
    final int permission = await _channel.invokeMethod("requestPermission");
    return permission;
  }
}

3. Test in Example

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_permission/flutter_permission.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(title: Text("flutter demo")), body: HomeContent()));
  }
}

class HomeContent extends StatefulWidget {
  HomeContent({Key key}) : super(key: key);

  @override
  _HomeContentState createState() {
    return _HomeContentState();
  }
}

class _HomeContentState extends State<HomeContent> {
  String _platformVersion = 'Unknown';
  String _permissionValue = "";

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  Future<void> requestPermission() async {
    int result;
    try {
      result = await FlutterPermission.requestPermission;
    } on PlatformException {
      result = -3;
    }
    setState(() {
      _permissionValue = result.toString();
    });
  }

  ///Get platform information
  Future<void> initPlatformState() async {
    String platformVersion;

    try {
      platformVersion = await FlutterPermission.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Column(
        children: [
          OutlinedButton(
              onPressed: requestPermission,
              child: Text('Permission test $_permissionValue')),
          OutlinedButton(
              onPressed: initPlatformState, child: Text('Version number $_platformVersion'))
        ],
      ),
    );
  }
}

4. Add plug-ins locally

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  flutter_permission:
    # When depending on this package from a real application you should use:
    #   flutter_permission: ^x.y.z
    # See https://dart.dev/tools/pub/dependencies#version-constraints
    # The example app is bundled with the plugin so we use a path dependency on
    # the parent directory to use the current plugin's version.
    path: ../

Other methods, github and pub, are not described in this chapter.

5. Summary

1. Through the above cases, we find that the writing of the Flutter plug-in is not as difficult as we thought. It's just the communication between Native and Flutter

2. The flutter plug-in performs two-way communication between native < ------- > flutters through MethodChannel (there is a pigeon after 2.0, which has not been used)

3. The flutter runs on the FlutterActivity (note that this is very important. It is not unreal rendering, but also has containers), which is why the Flutter widget has a life cycle and so on

Refer to official documents:

https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#basic-plugin

Keywords: Flutter

Added by r270ba on Tue, 08 Feb 2022 01:01:49 +0200