目前《闲鱼》客户端已经在商品详情页使用纯Flutter编写了,单页面纯Flutter写是没有问题的,在这里顺便提一下怎么简单辨认一个页面是Flutter还是原生(不严谨,在你知道它是有Flutter的情况下,因为RN也会这样的):
以《闲鱼》商品列表页和商品详情页为例:
商品列表页和商品详情页
接下来我们打开布局边界显示,可以看到闲鱼的商品列表页(左边)是原生写的,所以可以看到布局层,颜色越深表示布局层数越多,而商品详情页(右边)是Flutter写的,只有一层布局:
布局边界
好,题外话扯完,下面开始在Android中来混编Flutter,就使用之前的《日报》的例子。
计划是这样的:
这基本可以是一个次简单的混编例子。
最简单的。。。还要数Flutter官方的那个例子(生肉警告):https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps
想在Native代码中混编Flutter,需要在Flutter中建立一个Module,官方通过命令行创建,但Android Studio的Flutter插件同样提供了这个功能,而且更加直观,所以这里以Android Studio为例:
新建Flutter Module
接下来项目路径,名称等:
Flutter Module路径配置
之后设置包名,再点击finish就好了。
在Flutter Module同一父目录下新建Android工程,如果已有的工程也可以,同样道理。
新建Android工程
之后一路next下去。
在建立的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
Flutter Module
之后在Application Module的Build.gradle中依赖刚刚引入的library:
1 |
implementation project(':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运行效果
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 |
|
运行效果图如下所示:
FlutterFragment运行效果
完成上面步骤以后,我们已经了解在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独立模式运行的时候,它是这样的:
Flutter独立运行效果
无图无真相,以一个布局边界对比图来结束这篇文章吧,注意看Toolbar,一个是原生的,一个是Flutter的:
布局边界对比
文章作者: Kevin Wu
文章链接: https://kevinwu.cn/p/964c6c3/