共计 5836 个字符,预计需要花费 15 分钟才能阅读完成。
本文首发于微信公众号「Android 开发之旅」,欢迎关注,获取更多技术干货
混合开发应用场景
目前在国内开发纯 Flutter 的应用还是比较少的,绝大部分使用 Flutter 来开发的公司也都是使用混合开发。那么混合开发的主要使用场景有哪些呢?
- 作为独立的页面进行加入,可以从原生页面跳转到 Flutter 页面,也可以从 Flutter 页面跳转到原生页面。
- 作为页面的一部分嵌入。
创建 Flutter module
既然是做混合开发,那么我们肯定是由 Android 原生项目的。假如 native 项目的路径是这样的:flutter/flutter_hybrid/native, 那么我们需要在 native 上一层目录 flutter_hybrid 中创建 Flutter module。
cd flutter/flutter_hybrid/
// 创建支持 AndroidX 的 flutter_module
flutter create --androidx -t module flutter_module
// 创建不支持 AndroidX 的 flutter_module
flutter create -t module flutter_module
所以我们在创建模块的时候首先要确定 native 项目是不是已经支持 AndroidX,如果支持就需要加上 –androidx 参数。
输入后控制台打印如下:
$ flutter create -t module flutter_module
Creating project flutter_module...
flutter_module/test/widget_test.dart (created)
flutter_module/flutter_module.iml (created)
flutter_module/.gitignore (created)
flutter_module/.metadata (created)
flutter_module/pubspec.yaml (created)
flutter_module/README.md (created)
flutter_module/lib/main.dart (created)
flutter_module/flutter_module_android.iml (created)
flutter_module/.idea/libraries/Flutter_for_Android.xml (created)
flutter_module/.idea/libraries/Dart_SDK.xml (created)
flutter_module/.idea/modules.xml (created)
flutter_module/.idea/workspace.xml (created)
Running "flutter pub get" in flutter_module... 1.2s
Wrote 12 files.
All done!
Your module code is in flutter_module/lib/main.dart.
看到 All done 就表示我们项目创建好了。整个 module 目录和原生 Flutter 基本一样,主要就是 Android、iOS 的宿主工程和 lib 目录以及 pubspec.yaml 文件。
添加 Flutter module 依赖
module 项目创建好后就需要添加到 Android 项目中了。我们打开 Android 项目的 setting.gradle 文件,添加如下代码:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
//flutter_module 即为创建的模块名称
'flutter_module/.android/include_flutter.groovy'
))
setBinding 与 evaluate 允许 Flutter 模块包括它自己在内的任何 Flutter 插件,在 settings.gradle 中以类似 :flutter、package_info、:video_player 的方式存在。
然后打开 app/build.gradle 在 dependencies 标签中添加依赖:
implementation project(':flutter')
这样两步就完成了依赖的添加,这里为什么添加的叫“flutter”而不是“flutter_module”呢?因为项目编译完成后会在 Android 项目的目录下生成叫 Flutter 的目录,这就是需要我们依赖的。还有个需要注意是 gradle 中的 minSdkVersion 必须要大于等于 16,因为这个 flutter 支持的最低版本。同时添加使用 java8 来编译。在 app/build.gradle 中的 android 标签中添加:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
调用 Flutter module
依赖完成后我们就可以调用 flutter 模块来创建 UI 了。Flutter 为我们提供了两种方式调用,一种是 createView,以 view 的形式加载。另一种是 createFragment,以 Android 中的 fragment 的形式加载。
createView 方式:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
FlutterView flutterView = Flutter.createView(this, getLifecycle(), "initialRoute");
setContentView(flutterView);
}
}
createFragment 方式:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//container 为 activity_main 布局中的占位符 FrameLayout
transaction.replace(R.id.container, Flutter.createFragment("initialRoute"));
transaction.commit();}
}
这样就将 Flutter 默认的首页加载到应用上了。
从上面两部分代码中我们可以看到都有一个“initialRoute”参数,这个参数是用来告诉 Dart 代码在 Flutter 视图中显示哪个小部件。下面我们就来修改 module 中的 main.dart 代码来加载我们自己的页面。
我们设置两个 route,分别展示 route1Widget,和 route2Widget,当没有匹配的时候展示提醒文字。
import 'package:flutter/material.dart';
import 'dart:ui';
void main() => runApp(MyApp(
// 通过 window.defaultRouteName 获取从 native 传递过来的参数, 需要导入 dart:ui 包
initParams: window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
MyApp({Key key, this.initParams}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter_Android 混合开发',
theme: ThemeData(primarySwatch: Colors.blue,),
home: HomePage(initParams: initParams),
);
}
}
class HomePage extends StatefulWidget {
final String initParams;
const HomePage({Key key, this.initParams}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: _widgetRoute(widget.initParams),
),
);
}
}
/// 路由转发
Widget _widgetRoute(String route) {switch (route) {
case "route1":
return route1Widget();
case "route2":
return route2Widget();
default:
return notFoundWidget();}
}
Widget route1Widget() {
return Center(
child: Text(
"this is route1Widget",
style: TextStyle(color: Colors.red, fontSize: 20),
),
);
}
Widget route2Widget() {
return Center(
child: Text(
"this is route2Widget",
style: TextStyle(color: Colors.blue, fontSize: 20),
),
);
}
Widget notFoundWidget() {
return Center(
child: Text(
"未匹配到路由 111",
style: TextStyle(fontSize: 40),
),
);
}
我们现在将加载 Flutter 时的 initialRoute 参数替换为“route1”,那页面将加载 route1Widget,替换为“route2”,将加载 route2Widget。否则将展示 notFoundWidget。当然我们可以直接传路由参数,但是因为这个参数本身是一个字符串,所以我们可以来搞事情。比如传递一个 json 串,那么是不是可以做很多事呢?这里我就不贴 demo 了,因为和上面的逻辑基本一样,大家可以去试试看。
热重启 / 重新加载
大家在写纯 Flutter 应用的时候,知道是有热重启 / 重新加载功能的,但是在做混合开发的过程中,你会发现热重启 / 重新加载功能失效了。那么如何在混合开发中开启热重启 / 重新加载功能呢?
- 首先接入我们的设备或者模拟器
- 将我们的 App 关闭,退出后台,在 terminal 中运行 flutter attach 命令
$ flutter attach
Waiting for a connection from Flutter on Android SDK built for x86...
此时就在等待设备的连接。这里要注意的是,如果电脑连接了多台设备需要使用 -d 命令来指定一台设备,参数为设备的 id。
flutter attach -d '你的设备 id'
- 然后启动我们的应用会看到控制台输出:
Done.
Syncing files to device Android SDK built for x86... 1,393ms
???? To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on Android SDK built for x86 is available at: http://127.0.0.1:59354/zRsDBfpesrk=/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
这样就表示我们连接成功了。在输出的日志中也告诉了我们如何使用热重启 / 重新加载功能。
在 Terminal 中输入以下命令:
r : 热加载;R : 热重启;h : 获取帮助;d : 断开连接;q : 退出;
这里的的 d 和 q 的命令都有退出调试,区别在于 d 命令只是单纯的断开而 q 命令会将应用退到后台。
调试 Dart 代码
同样在混合开发过程中我们如何调试 dart 代码呢?
- 关闭我们的应用
- 点击 Android Studio 工具栏上的 Flutter Attach 按钮 (需要安装 Flutter 与 Dart 插件)
- 启动我们的应用
接下来就可以像调试普通 Flutter 项目一样来调试混合开发模式下的 Dart 代码了。
总结
以上就是如何在 Android 原生项目中接入 Flutter 模块的基础讲解,主要就是模块的创建、依赖、调用以及调试等等。其它的像 iOS 接入 Flutter 模块,Android 项目和 Flutter 项目之间的通信以及 iOS 项目和 Flutter 之间的通信都将在之后的文章中进行讲解。因为写在一篇中篇幅太长,朋友们读起来也累。所以后续还会有至少三篇相关的文章和大家见面。动动手关注公众号,即时获取相关文章的推送。
全部 Demo 源码已经上传到后台,关注公众号回复「Android 混合开发」即可获得下载链接。
如果你觉得文章还不错,请大家点赞分享下,你的肯定是对我最大的鼓励和支持。
推荐阅读
Flutter 开发必备 Dart 基础:Dart 快速入门
扫描下方二维码关注公众号,获取更多技术干货。