Let’s see what it looks like:
access
As for the access method, I refer to the official introduction document. What I try here is the Android access method, which is relatively smooth.
1. Execute the flutter create -t module my_flutter command in your Android project directory. When the flutter is complete, it should look something like this.
2, Open your Android project setting.gradle file, line it up, and add:
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'tip_flutter/.android/include_flutter.groovy' // new
))
Copy the code
These lines of code mean to introduce the module you just created into the Android project as an Android module. 3. Finally, open build.gradle in your app directory and add it to your dependencies
//flutter
implementation project(':flutter')
Copy the code
Ok, by synchronizing, you can introduce Flutter into your existing Android project. The steps for ios will not be described. Refer to the documentation.
Have a problem
Of course, the process I described above was pretty smooth, but my access process wasn’t so smooth, and I tried everything. 1, For example, what if the flutter create -t module my_flutter is not in the same directory as the Android project
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'Your project directory name /tip_flutter/.android/include_flutter. Groovy' // new
))
Copy the code
That’s ok, but it doesn’t look good. Ios introduces the Flutter module to your Android project, so it’s more elegant to be independent of the Android project and keep things decoupled. Some of you may have to configure buildTypes that will fail to synchronize. The solution is to modify your build.gradle folder in the.android folder. Keep it consistent with your app’s buildTypes.
3. Debug runs well, release is down, this must be a confusion problem, you can refer to it
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
Copy the code
Libflutter. So is a library that has not been loaded into the corresponding CPU architecture
ndk {
abiFilters "armeabi"."armeabi-v7a". Add everything. Here, your bag is too big for you. }Copy the code
In summary, in fact, the access process is not so smooth, ha ha, or quite pits, but generally can find a solution.
Use the pose
Create a flutterView directly createView and add it to your layout, route1 in this case.
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"route1"
);
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600.800);
layout.leftMargin = 100;
layout.topMargin = 200;
addContentView(flutterView, layout);
Copy the code
This is actually a way for flutter to manage pages. Routing is used to configure a route for each page. Route1 is the name of the page, so you can jump directly to each page named Route1. Dart void main() => runApp(new MyApp()); dart void main() => runApp(new MyApp()); It is understandable that he is the root. 2. Use fragments
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
tx.commit();
Copy the code
There are two ways to apply the Flutter module. In fact, if you are smart enough, there is only one of them. Both add a flutterView to the native, and if you look at the source code, you will find that the flutterView is actually a SurfaceView. It just implements a special interface, BinaryMessenger.
The flutter calls the native module
Opening a page for a FLUTTER implementation is very simple, but where does the data presented inside come from? Nativie sends data to the flutter end in one of two ways. B. The flutter end requests data from the native end. Here, we first look at the second method, the flutter side asks the native side for data, because the second method is mentioned more officially. Usually, the native method of flutter invocation is done through the MethodChannel. Let’s learn how to do this first.
MethodChannel(getFlutterView(), "app.channel.shared.data")
.setMethodCallHandler(MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.contentEquals("getSharedText")) {
result.success("some thing want to send to flutter");
sharedText = null; }}});Copy the code
Let’s take a look at the source code definition. Here’s the excerpt
public final class MethodChannel {
private static final String TAG = "MethodChannel#";
private final BinaryMessenger messenger;
private final String name;
private final MethodCodec codec;
public MethodChannel(BinaryMessenger messenger, String name) {
this(messenger, name, StandardMethodCodec.INSTANCE);
}
Copy the code
As mentioned earlier, the first argument is BinaryMessenger, and since FlutterView implements this interface, the official demo passes getFlutterView().
So obviously, there are two ways that we talked about using postures. The first way is that the FlutterView is obviously out there, you can easily grab it and start a MethodChannel. What about the second way? Wait, let’s see what a FlutterFragment is.
public class FlutterFragment extends Fragment {
public static final String ARG_ROUTE = "route";
private String mRoute = "/";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments() ! = null) { mRoute = getArguments().getString(ARG_ROUTE); } } @Override public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) { super.onInflate(context, attrs, savedInstanceState); } @Override public FlutterView onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {returnFlutter.createView(getActivity(), getLifecycle(), mRoute); }}Copy the code
OnCreateView returns a FlutterView, as mentioned earlier. In fact, the native reference to the Flutter module simply adds the FlutterView to the existing layout.
Ok, so now that I’ve done both, I’m going to show you how to do it in the second way.
public class FlutterBaseFragment extends FlutterFragment {
private static final String METHOD_CHANNEL = "tip.flutter.io/method";
public static FlutterBaseFragment newInstance(String route) {
Bundle args = new Bundle();
args.putString(ARG_ROUTE, route);
FlutterBaseFragment fragment = new FlutterBaseFragment();
fragment.setArguments(args);
return fragment;
}
@SuppressWarnings("unchecked")
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
new MethodChannel((FlutterView) getView(), METHOD_CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if(batteryLevel ! = -1) { result.success(batteryLevel); }else {
result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); }}}); } private intgetBatteryLevel() {...}
}
Copy the code
That’s the native implementation part. Then, how does the flutter side call?
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('tip.flutter.io/method');
String _batteryLevel = 'Unknown battery level.';
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Copy the code
Familiar? This is the official demo.
Isn’t there a requirement for data sent to flutter via MethodChannel? Can any type of data be sent? Sorry, no, for example, your custom class is obviously not allowed. He supports only the following types:
So, what if we want to send custom type data?
Obviously, we need to convert to the type supported by dart. You might think of Object->Json, and then, on the flutter side, change to a Json Object. There are other ways, for example, if you happen to be using protobuf, it would be nice to just pass byte[], or you can implement custom protocols if you have enough time. In general, the data passed should be of a type that is recognizable across platforms.
Sends data to the flutter native
It feels strange to send data to a flutter natively. So, what is the scenario, for example, a change in the charging status of a cell phone? How does this change inform the flutter?
In this case, you need to use EventChannel, which is actually not that different from how MethodChannel sends data over, except that we understand that the flutter side requests the data and gets it, EventChannel might be understood as being actively pushed from native.
new EventChannel((FlutterView) getView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, final EventChannel.EventSink events) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run(a) {
events.success("Current time in milliseconds" + System.currentTimeMillis() / 1000); }},1000.1000);
}
@Override
public void onCancel(Object arguments) {... }});Copy the code
How does the code on the flutter side receive the data pushed by this EventChannel?
Future<Null> _lisEvent() async {
String eventStr;
try {
_streamSubscription =
eventChannel.receiveBroadcastStream().listen((data) {
eventStr = 'event get data is $data ';
setState(() {
_eventStr = eventStr;
});
});
} on PlatformException catch (e) {
eventStr = "event get data err: '${e.message}'."; setState(() { _eventStr = eventStr; }); }}Copy the code
conclusion
The pages made by the native pull up of the flutter, the native module that the flutter calls, and the native module that pushes data to the flutter are all verified to be OK. Therefore, the way that the flutter connects to the existing APP is feasible. After the flutter is connected, The package size will increase by 5.5m +, mainly because this SO library is needed. It would be nice if it could be obtained from the network, but unfortunately it can only be packaged into APK at present. Together with the files generated by the business, the overall package size increases by about 8M for a simple business.