码个蛋(codeegg)第 743 次推文
Flutter 说到底只是一个 UI 框架,很多功能都需要通过原生的 Api 来实现,那么就会涉及到 Flutter 和 Native 的交互,因为本人不懂 iOS 开发,所以只能讲下 Flutter 同 Android 的交互。
Android项目配置Flutter依赖
既然是互相交互,那么需要准备一个 Android 项目。接着就需要创建 flutter module,让 Android 项目依赖,创建的方法可以参考官网 Flutter Wiki,虽然是官网提供的方法,但是完全按照这个步骤来,还是会有坑的,这边就慢慢一步步解决坑。
如果你用的是 Android Studio 进行开发的话,直接打开底部的 Terminal,直接创建 flutter module 依赖
flutter create -t module flutter_native_contact
至于 module 名可以随意填写,module 创建完后结构大概是这样的
flutter module.png
接着切换到 module 下的 .android 文件夹,接着有坑来了,官网提供的方法是 ./gradlew flutter:assembleDebug
可能会提示命令不存在,那么直接通过 gradlew flutter:assembleDebug
来运行,等它自动跑完后,打开根目录下的 settings.gradle
文件,加入官网提供的 gradle 代码
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'flutter_native_contact/.android/include_flutter.groovy' // new
)) // new
你以为这里没坑,真是图样图森破,没坑是不可能的,编译器大爷可能会给你甩这么个错误
很明显可以看出是找不到我们的文件,所以把文件名路径给补全
evaluate(new File( // new
settingsDir.parentFile, // new
'FlutterNativeContactDemo/flutter_native_contact/
.android/include_flutter.groovy' // 这里补全路径
))
接着打开原有项目下,原有项目下,原有项目下的 app 中的 build.gradle
文件,在 android 下加上如下代码
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
这个必须要加,不要问为什么,我也不知道为什么,最后在项目下添加 flutter module 的依赖就完成了。这个过程告诉我们一个什么道理呢?*不要以为官网的都对,官网讲的也不是完全可信的,时不时给你来个坑就能卡你老半天。
原生界面加载Flutter页面
那么如何在原生界面显示 Flutter 界面呢,这个就需要通过 FlutterView 来实现了,Flutter 这个类提供了 createView
和 createFragment
两个方法,分别用于返回 FlutterView 和 FlutterFragment 实例,FlutterFragment 的实现原理也是通过 FlutterView 来实现的,可以简单看下 FlutterFragment 的源码
/**
* A {@link Fragment} managing a {@link FlutterView}.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
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 FlutterView onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// 最后还是挺过 createView 方法来生成页面,只不过直接放在 fragment,
// 放在 fragment 会比直接 使用 FlutterView 更方便管理,例如实现 ViewPager 等
return Flutter.createView(getActivity(), getLifecycle(), mRoute);
}
}
CreateFragment方式加载
在原生页面显示 Flutter 界面的第一种方式就是加载 FlutterFragment,看个比较简单的例子吧
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!-- 这个布局用于加载 fragment -->
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/flutter_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginBottom="50dp"
android:src="@drawable/ic_add_white_36dp"
app:fabSize="auto"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>
在 Activity 可以直接通过返回 FlutterFragment 加载到 FrameLayout 即可
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container,
Flutter.createFragment("route_flutter"))
.commit()
}
}
这样就把 Flutter 页面加载到原生界面了,会通过传递的路由值在 dart 层进行查找,所以接着就需要编写 Flutter 界面
/// runApp 内部值也可以直接传入 _buildWidgetForNativeRoute 方法
/// 这边在外层嵌套一层 MaterialApp 主要是防止一些不必要的麻烦,
/// 例如 MediaQuery 这方面的使用等
void main() => runApp(FlutterApp());
class FlutterApp extends StatelessWidget {
@override
Widget