目前《闲鱼》客户端已经在商品详情页使用纯Flutter编写了,单页面纯Flutter写是没有问题的,在这里顺便提一下怎么简单辨认一个页面是Flutter还是原生(不严谨,在你知道它是有Flutter的情况下,因为RN也会这样的):
- 打开手机的“开发者模式”
- 打开“显示布局边界”
- 切回APP
以《闲鱼》商品列表页和商品详情页为例:
接下来我们打开布局边界显示,可以看到闲鱼的商品列表页(左边)是原生写的,所以可以看到布局层,颜色越深表示布局层数越多,而商品详情页(右边)是Flutter写的,只有一层布局:
好,题外话扯完,下面开始在Android中来混编Flutter,就使用之前的《日报》的例子。
计划是这样的:
- 将外层UI框架,包括toolbar,转移到原生中来处理
- 将ListView,日报数据请求逻辑等由Flutter来处理
这基本可以是一个次简单的混编例子。
最简单的。。。还要数Flutter官方的那个例子(生肉警告):https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps
新建Flutter Module
想在Native代码中混编Flutter,需要在Flutter中建立一个Module,官方通过命令行创建,但Android Studio的Flutter插件同样提供了这个功能,而且更加直观,所以这里以Android Studio为例:
接下来项目路径,名称等:
之后设置包名,再点击finish就好了。
新建Android工程
在Flutter Module同一父目录下新建Android工程,如果已有的工程也可以,同样道理。
之后一路next下去。
Android工程中引入Flutter
在建立的Android工程的settings.gradle中加入以下代码:
1 2 3 4 5 |
setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_daily_module/.android/include_flutter.groovy' )) |
其中settingsDir.parentFile表示当前目录的父级目录,flutter_daily_module是前面所建立的Flutter Module目录。
Sync一下就可以看到多了一个Flutter的library module
之后在Application Module的Build.gradle中依赖刚刚引入的library:
1 |
implementation project(':flutter')
|
Android原生代码调用Flutter
先来一个简单的Demo。
我们把Flutter中的代码改成如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return Center(
child: Text('route: $route', textDirection: TextDirection.ltr),
);
case 'route2':
return Center(
child: Text('route: $route', textDirection: TextDirection.ltr),
);
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}
|
上面代码中会返回一个Widget,如果传入参数为route1就会在widget居中位置显示route: router1。
在Android项目默认生成的MainActivity中,我们来展示一个route1。
1 2 3 4 5 6 7 8 9 |
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"route1"
);
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
layout.leftMargin = 100;
layout.topMargin = 400;
addContentView(flutterView, layout);
|
运行原生Android工程,可以看到如下效果:
FlutterView不是唯一的使用方式,还有一种通过FlutterFragment来调用Flutter的代码方式,如下代码所示:
1 2 3 |
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fl_flutter_view, Flutter.createFragment("route1"));
fragmentTransaction.commit();
|
其中fl_flutter_view对应Android中在xml中定义的一个FrameLayout:
1 2 3 4 |
<FrameLayout
android:id="@+id/fl_flutter_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
|
运行效果图如下所示:
Android原生外层框架中嵌入Flutter日报列表
完成上面步骤以后,我们已经了解在Android中是怎么调用Flutter的,下面把实战系列文章的《日报》列表页嵌入这个“外框”中。
《日报》纯Flutter项目源码:
https://github.com/KevinWu1993/DailyFlutter
我们先把Flutter中列表页的主要代码同步到flutter_daily_module中。
原本《日报》的main.dart代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import 'package:flutter/material.dart';
import 'package:zhihudaily/daily/daily_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '日报',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DailyPage(title: '日报'),
);
}
}
|
Flutter Module中main.dart我们改成如下:
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 |
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_daily_module/daily/daily_page.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'dailyInNative':
return MaterialApp(
title: '日报',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DailyPage(title: '日报', isShowToolbar: false,),
);
default:
return MaterialApp(
title: '日报',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DailyPage(title: '日报'),
);
}
}
|
要注意的是default选项,因为Flutter Module也是可以独立运行的,所以留着这个case。
同样的在DailyPage这一个Widget的构造方法里面,增加一个可选“是否显示Toolbar”的参数,目的是在独立运行flutter项目的时候显示状态栏,而在作为library module混编进Android的时候隐藏Toolbar,使用Android原生的Toolbar。
1 |
DailyPage({Key key, this.title, this.isShowToolbar = true}) : super(key: key);
|
对于Flutter Toolbar,在DailyPage中构建的方法如下:
1 2 3 4 5 6 7 8 |
Widget _buildAppBar(BuildContext buildContext) {
if (widget.isShowToolbar)
return new AppBar(
title: Text(widget.title),
);
else
return null;
}
|
完成上述步骤后,在Android原生代码中调用如下:
1 2 3 |
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fl_flutter_view, Flutter.createFragment("dailyInNative"));
fragmentTransaction.commit();
|
到此就完成了日报列表的嵌入,那么我们来运行一下:
而当使用Flutter独立模式运行的时候,它是这样的:
无图无真相,以一个布局边界对比图来结束这篇文章吧,注意看Toolbar,一个是原生的,一个是Flutter的:
文章作者: Kevin Wu
文章链接: https://kevinwu.cn/p/964c6c3/