Source: Android Team – Natural Yu
This paper mainly solves three problems:
- Integrate Flutter into the Android project to open the default page of Flutter
- You can jump to the specified page of Flutter
- You can embed the specified components of a Flutter into the native page and pass parameters
1. Integrate Flutter into Android
Here, we to Flutter the Module to create a Flutter engineering (Flutter), and then run up, can in. Android/Flutter/build/outouts/aar folder below to get the aar
The reason why the Flutter Module is developed here, not the Flutter Application, is to get this AAR. This folder is automatically generated by the Flutter Module. The Android folder contains the Flutter folder, but not the Flutter Application folder. In this way, we can borrow the Gradle script that Flutter already has to generate aArs. Otherwise, we have to write our own Gradle packaging script. It is easy to step into a pit and not get up.
Then open another window, create a new Android project (Flutter_container) and copy the AAR
One problem that needs to be noted here is that because of Flutter itself, the icudtl.dat file copied from AAR is missing. We need to manually copy the icudtl.dat file to assets/ Flutter_shared directory.
How to obtain the icudtl.dat file by unpacking the default APK generated by the Flutter project
We then need to set up the Activity to receive the Flutter in the host Android project. There are two core ideas for the Flutter project’s android/app directory:
- Application: initializes the Flutter
public class App extends Application {
@Override
public void onCreate() { super.onCreate(); FlutterMain.startInitialization(this); }}Copy the code
- Activity: Inherits FlutterActivity
/** * A white screen appears when you jump to the Flutter screen in native mode. Public class MainFlutterActivity extends FlutterActivity {@override protected void onCreate(Bundle) savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); }}Copy the code
After that, we can jump to the MainFlutterActivity and access the default page of the Flutter project in the Android project.
2. Go to the specified page
However, we know that when we jump from the Android project to the Flutter, we must selectively jump to the specified page. We cannot simply jump to the default page, so we need to use the static route of the Flutter.
Dart of the Flutter project is modified to specify two routes for a page: homePage and channelPage
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', theme: ThemeData(primarySwatch: color.blue,), home: TestPage(), // <String, WidgetBuilder> {'homePage': (BuildContext context) => new HomePage(),
'channelPage': (BuildContext context) => new ChannelPage(), }, ); }}Copy the code
Then, in the host Android project, add the Activity container for the specified page and get the View of the specified page with Flutter. CreateView
Note that HomeFlutterActivity only needs to inherit AppCompatActivity, not FlutterActivity.
public class HomeFlutterActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FlutterView homePage = Flutter.createView(
this,
getLifecycle(),
"homePage"
);
setContentView(homePage); }}Copy the code
After that, we can jump to the HomeFlutterActivity and enter the specified page of the Flutter project in the Android project.
3. Embed View and pass parameters
It was possible to jump to the specified page, but apparently there was a big problem: you couldn’t pass parameters.
This is a major disadvantage of the static route to Flutter. Although dynamic routes can pass parameters and receive return values, dynamic routes cannot be used for native calls.
Navigator.of(context)
.push<String>(new MaterialPageRoute(builder: (context) {
return new NextPage(params);
})).then((String value) {
setState(() {
params = value;
});
});
Copy the code
There is a route library for Flutter: Fluro, which can implement static route parameter transfer, for example:
The ginseng
var bodyJson = '{"user":1281,"pass":3041}';
router.navigateTo(context, '/home/$bodyJson');
Copy the code
receive
Router router = new Router();
void main() {
router.define('/home/:data', handler: new Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) {
return new FluroHomePage(params['data'] [0]); })); runApp(MyApp()); }Copy the code
However, this method works within a Flutter, but cannot be called to a native Flutter. Fluro is not used in a native Flutter when passing a Flutter. CreateView, which is the default route.
We investigated a number of options, but finally, we ran out of options and resorted to the dumbest method of all: passing parameters through a MethodChannel.
Flutterview.post (new Runnable())) is a MethodChannel call that will not be passed to Flutter.
Native transfer to Flutter
Native calls
MethodChannel channel = new MethodChannel(flutterView, CHANNEL);
channel.invokeMethod("invokeFlutterMethod"."hello,flutter", new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
Log.i("flutter"."1. Primitive invokefluttermethod-success :"+o.toString());
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
Log.i("flutter"."1. Primitive invokefluttermethod-error");
}
@Override
public void notImplemented() {
Log.i("flutter"."1. Invokefluttermethod-notimplemented"); }});Copy the code
Flutter to perform
platform.setMethodCallHandler((handler) {
Future<String> future=Future((){
switch (handler.method) {
case "invokeFlutterMethod":
String args = handler.arguments;
print("2. Flutter perform invokeFlutterMethod:${args}");
return "this is flutter result"; }});return future;
});
Copy the code
Flutter passes references to the native
Flutter call
print("3. InvokeNativeMethod Flutter call");
int result =
await platform.invokeMethod("invokeNativeMethod"."hello,native");
print("5. Original implementation results received:${result}");
Copy the code
Native to perform
channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "invokeNativeMethod":
String args = (String) call.arguments;
Log.i("flutter"."4. Native execution invokeNativeMethod:"+args);
result.success(200);
break;
default:
}
}
});
Copy the code
Finally, post the complete code of the reference page, which basically runs:
- InvokeFlutterMethod is called natively
- Flutter execution invokeFlutterMethod
- Flutter call invokeNativeMethod
- Native execution invokeNativeMethod
Android:
public class ChannelFlutterActivity extends AppCompatActivity {
private static final String CHANNEL = "com.ezbuy.flutter";
FlutterView flutterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_channel);
FrameLayout frFlutter = findViewById(R.id.fr_flutter);
flutterView = getFlutterView("channelPage");
frFlutter.addView(flutterView);
flutterView.post(new Runnable() {
@Override
public void run() { initMethodChannel(flutterView); }}); } public FlutterView initMethodChannel(FlutterView flutterView) { MethodChannel channel = new MethodChannel(flutterView, CHANNEL); //1. Call the Flutter method channel.invokemethod ("invokeFlutterMethod"."hello,flutter", new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
Log.i("flutter"."1. Primitive invokefluttermethod-success :"+o.toString());
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
Log.i("flutter"."1. Primitive invokefluttermethod-error");
}
@Override
public void notImplemented() {
Log.i("flutter"."1. Invokefluttermethod-notimplemented"); }}); Log.i("flutter"."1. Invoke invokeFlutterMethod natively"); / / 4. Flutter call native methods to monitor the channel. SetMethodCallHandler (new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "invokeNativeMethod":
String args = (String) call.arguments;
Log.i("flutter"."4. Native execution invokeNativeMethod:"+args);
result.success(200);
break;
default:
}
}
});
return flutterView;
}
public FlutterView getFlutterView(String initialRoute) {
returnFlutter.createView( this, getLifecycle(), initialRoute ); }}Copy the code
Flutter
class ChannelPage extends StatefulWidget {
ChannelPage();
@override
_ChannelPageState createState() => _ChannelPageState();
}
class _ChannelPageState extends State<ChannelPage> {
static const platform = const MethodChannel('com.ezbuy.flutter');
String data;
@override
void initState() {
super.initState();
data ="The default data"; initChannel(); } @override Widget build(BuildContext context) {// Scaffolding must be wrappedreturn Scaffold(body: new Center(child: new Text(data)));
}
void initChannel() {
platform.setMethodCallHandler((handler) {
Future<String> future=Future((){
switch (handler.method) {
case "invokeFlutterMethod":
String args = handler.arguments;
print("2. Flutter perform invokeFlutterMethod:${args}");
setState(() {
data = "2. Flutter perform invokeFlutterMethod:${args}";
});
invokeNativeMethod();
return "this is flutter result"; }});return future;
});
}
void invokeNativeMethod() async {
print("3. InvokeNativeMethod Flutter call");
int result =
await platform.invokeMethod("invokeNativeMethod"."hello,native");
print("5. Original implementation results received:${result}"); }}Copy the code
This section describes how to nest a Flutter into an Android Activity at the View level. The View created by Flutter. CreateView is not very different from a normal View. Just addView, nothing special. For example, the ChannelFlutterActivity layout file I used looks like this:
The final result is:
Other pit
1. When the Flutter project relies on a plugin, the host Android project will report an error that the plugin’s native code cannot be found
My Flutter project relies on the shared_Preferences plugin, causing an error:
The reason: when the Flutter project was exported as an AAR, it did not include the native code in the plugin.
There are two solutions. Instead of the default way of generating AAR, fataar-gradle-plugin is used to make the generated FLUTTER. Aar contains the aar of the nested plugin project directly. It is need to modify the Flutter engineering. Android/Flutter/build. Gradle file. I tried, the result reported the error of cyclic dependence, so I gave up, if this scheme works, please tell me the specific steps.
My solution: HERE I took a simple and straightforward approach, went to the aar of the plugin and copied it into the host Android project. The AAR for this plugin is here:
Copy here:
The downside of this solution, however, is that you need to copy each plug-in later, and the maintenance cost is a bit high. Unlike Fataar, there is only flutter. Aar. Especially in the later stage, aar will definitely be made to be remotely dependent, rather than directly copied, which will be more expensive to maintain.
conclusion
As we can see above, Flutter is actually quite convenient to integrate into the Android project (except for FlutterView input). As for how to integrate Flutter into the ios project, I have not practiced it yet, and I still need to explore with my ios colleagues. If you have filled in any holes and summarized your experience during the integration of Flutter into the ios project, please feel free to share with us.