Contents
  1. 1. 在已有Android项目当中开发Flutter
    1. 1.1. 1. 引入Flutter模块
      1. 1.1.1. 1.假设有一个 some/path/MyApp的android程序
      2. 1.1.2. 2.引入flutter_module
      3. 1.1.3. 3. 用 implementation 将flutter模块引入到app当中
    2. 1.2. 2. 创建Flutter容器 实现Flutter与Native交互
    3. 1.3. 3.Arndoird-Flutter 实践篇
      1. 1.3.1. 1.在一个已有的Activity当中起使用FlutterView
      2. 1.3.2. 2.重新写一个Activity 继承FlutterActivity实现
      3. 1.3.3. 3.继承Activity并实现FlutterActivity相关接口

在已有Android项目当中开发Flutter

1. 引入Flutter模块

参考 引入flutter

步骤

1.假设有一个 some/path/MyApp的android程序

输入以下命令

1
2
$ cd some/path/
$ flutter create -t module flutter_module

这样就在当前path文件夹里与 MyApp有同层级的模块 flutter_module这个文件夹

2.引入flutter_module

在当前MyApp的 settings.gradle加入:

1
2
3
4
5
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))

3. 用 implementation 将flutter模块引入到app当中

1
2
3
4
5
6
// MyApp/app/build.gradle
:
dependencies {
implementation project(':flutter')
:
}

2. 创建Flutter容器 实现Flutter与Native交互

首先看下官方提供的两种方法

Use the Flutter module’s Java API to add Flutter views to your host app. This can be done by directly using Flutter.createView
// 接下来使用Flutter模块提供的java api去给app增加flutterView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// MyApp/app/src/main/java/some/package/MainActivity.java
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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);
}
});

也可以创建一个 FlutterFragment,它可以有独立的生命周期

1
2
3
4
5
6
7
8
9
// MyApp/app/src/main/java/some/package/SomeActivity.java
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
tx.commit();
}
});

再看下Flutter部分的代码,上面我们使用字符串“route1”告诉Dart代码要在Flutter视图中显示哪个组件。 Flutter模块项目模板的 lib/main.dart文件应该打开(或以其他方式解释)提供的路由字符串,该字符串可用作window.defaultRouteName,以确定创建和传递哪个小部件给runApp。示意图,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 'dart:ui';
import 'package:flutter/material.dart';

void main() => runApp(_widgetForRoute(window.defaultRouteName));

Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return SomeWidget(...);
case 'route2':
return SomeOtherWidget(...);
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}

完全取决于您想要的路径字符串以及如何解释它们。就是传相应的route显示相应的值
通过以上的代码

通过以上代码 我们可以有多种flutter也Activity结合方案
图示

cmd-markdown1

针对上个图片 我们可以看出一个login模块可以创建多个FlutterView实例,来进行客户的定制化,也可以同一个FlutterView来对应多个Widget。相比创建多个FlutterView实例,创建单个FlutterView更省资源。

目前适合我们目前现有的开发方式 还是一个Login模块对应一个FlutterView容器对应一种定制化FlutterWidget。在接下来的篇章会讲解整个实践的过程

3.Arndoird-Flutter 实践篇

1.在一个已有的Activity当中起使用FlutterView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  final FlutterView flutterView2 = Flutter.createView(
GuideActivity.this,
getLifecycle(),
"/register"
);

btnGoAllFlutter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
L.e(TAG,"点击22222");
final FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
addContentView(flutterView2, layout);
flutterView2.addFirstFrameListener(new FlutterView.FirstFrameListener() {
@Override
public void onFirstFrame() {
L.e(TAG, "2222第一帧");
}
});


}
});

// main.dart文件
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
LoginPage.sName: (context) {
return LoginPage();
},
RegisterPage.sName: (context) {
return RegisterPage();
},
cus1Login.sName: (context) {
return cus1Login();
},
Test.sName: (context) {
return Test();
}
},
);
}
}

实践结果 能跳转,但是在使用TextField这个widget时 出现无法自动弹出输入框,route可以根据自己传入的参数,跳转到指定页面

2.重新写一个Activity 继承FlutterActivity实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class Customer1Activity extends FlutterActivity implements LifecycleOwner,MethodChannel.MethodCallHandler{
private LifecycleRegistry mLifecycleRegistry;

public static final String TAG = Customer1Activity.class.getSimpleName();
private static final String CHANNEL = "com.ty.test/android";


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
// FlutterMain.startInitialization(this);
L.e(TAG,"定制一Activity的第一个页面");
//注册通道
final FlutterView flutterView = Flutter.createView(
Customer1Activity.this,
mLifecycleRegistry,
"/"
);
GeneratedPluginRegistrant.registerWith(this);
ToastPlugin.register(this,flutterView); //插件必须要和相应的flutterView绑定
ApiPlugin.register(this,flutterView);
final FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
addContentView(flutterView, layout);
//解决黑屏问题 尝试一下
flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() {
@Override
public void onFirstFrame() {
L.e(TAG, "定制1第一帧");
}
});
// FlutterActivity


}

@Override
protected void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}


@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}

@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {

}
}

实践结果:输入框无法点击,看来这样写还是有问题,且继承FlutterActivity 就要放弃继承我们代码中封装过BaseActivity 所以将出现第三种写法

3.继承Activity并实现FlutterActivity相关接口

自己通过相关接口来写一个Flutter容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
public class TYFlutterActivity extends Activity implements PluginRegistry, FlutterActivityDelegate.ViewFactory, FlutterView.Provider, LifecycleOwner {
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate;
private final FlutterView.Provider viewProvider;
private final PluginRegistry pluginRegistry;

private FlutterView flutterView2;

public TYFlutterActivity() {
this.eventDelegate = this.delegate;
this.viewProvider = this.delegate;
this.pluginRegistry = this.delegate;
}

private LifecycleRegistry mLifecycleRegistry;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
// flutterView2 = Flutter.createView(
// TYFlutterActivity.this,
// getLifecycle(),
// "/register"
// );

this.eventDelegate.onCreate(savedInstanceState);
ToastPlugin.register(this,this.getFlutterView()); //插件必须要和相应的flutterView绑定
ApiPlugin.register(this,this.getFlutterView());
GeneratedPluginRegistrant.registerWith(this);

}

protected void onStart() {
super.onStart();
this.eventDelegate.onStart();
}

protected void onResume() {
super.onResume();
this.eventDelegate.onResume();
}

protected void onDestroy() {
this.eventDelegate.onDestroy();
super.onDestroy();
}

public void onBackPressed() {
if (!this.eventDelegate.onBackPressed()) {
super.onBackPressed();
}

}

protected void onStop() {
this.eventDelegate.onStop();
super.onStop();
}

protected void onPause() {
super.onPause();
this.eventDelegate.onPause();
}

protected void onNewIntent(Intent intent) {
this.eventDelegate.onNewIntent(intent);
}

public void onUserLeaveHint() {
this.eventDelegate.onUserLeaveHint();
}

public void onTrimMemory(int level) {
this.eventDelegate.onTrimMemory(level);
}

public void onLowMemory() {
this.eventDelegate.onLowMemory();
}

@Override
public FlutterView createFlutterView(Context context) {
// if(flutterView2!=null){
// return flutterView2;
// }
WindowManager.LayoutParams matchParent = new WindowManager.LayoutParams(-1, -1);
FlutterNativeView nativeView = this.createFlutterNativeView();
FlutterView flutterView = new FlutterView(TYFlutterActivity.this, (AttributeSet) null, nativeView);
flutterView.setInitialRoute("/");
flutterView.setLayoutParams(matchParent);
this.setContentView(flutterView);
return flutterView;

}

@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}

@Override
public boolean retainFlutterNativeView() {
return false;
}

@Override
public Registrar registrarFor(String pluginKey) {
return this.pluginRegistry.registrarFor(pluginKey);
}

@Override
public boolean hasPlugin(String key) {
return this.pluginRegistry.hasPlugin(key);
}

@Override
public <T> T valuePublishedByPlugin(String pluginKey) {
return this.pluginRegistry.valuePublishedByPlugin(pluginKey);
}

@Override
public FlutterView getFlutterView() {
return this.viewProvider.getFlutterView();
}

@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}

实践结果:任何功能正常,且在release环境下,页面切换与native相似,这样写扩展性更高