IT之家12月5日消息: 今天谷歌官方宣布Flutter 的 1.0 版本正式发布!Flutter是Google打造的UI工具包,帮助你通过一套代码同时在iOS 和Android上构建媲美原生体验的精美应用 —— 2018”
Google 刚公布Kotlin 成为 Android 官方开发语言没多久,Flutter就发布了 1.0 版本。虽然说搞不明白 google 的战略意图,但作为一个IT人员,既然有了新东西,尤其跟自己的工作息息相关,就得去了解一下了。
kotlin 自不必说,其大概功能以及优缺点已经在之前博客中进行了简单的说明:
从Java的角度看kotlin特性(一)
从Java的角度看kotlin特性(二)
其中已经对比了java
与 kotlin
部分差异以及功能。
那么Flutter到底是什么?能做些什么?要想弄明白这个,我们可以看一下官方说明:
Flutter is Google’s mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.
ok,一个跨平台的移动端框架,适用于Android 和 IOS 平台,开源,不同平台使用相同代码。
其实这种框架或者说想法从来不曾缺少,事实上,在很久之前就开始了跨平台的研究,各种跨平台理念层出不穷:
- H5+原生
- JavaScript开发+原生渲染 (React Native、Weex等)
像上面两种方式是使用最多也是最通用的,那么Flutter 一个“初来乍到”的概念,对比之前的方式又有什么新颖之处?
从各种资料来看Flutter 宣扬的 “高明” 之处主要在于:
- 热重载
- 60FPS
热重载得益于Dart 语言的 AOT 和 JIT 两种运行模式。
60FPS则是由于自身实现了完整的2D图形引擎。
既然说优点这么多,那总得试一下效果才好;
首先我们尝试一下在Android(IOS)端开发过程。
一、 准备工作
按照官方文档,我们将 Flutter 环境安装完成。由于国外网站都被墙了,因此我们可以查看 Flutter 中文网 来完成预备步骤。
如果是Android 开发人员,这里我们直接 查看这里 入门: 在Windows上搭建Flutter开发环境
需要注意的是,Flutter SDK 是存放于 GITHUB 的一个开源项目,因此我们可以直接从GITHUB 上克隆下来。
然后中间教程会有说到需要执行:
flutter doctor
这相当于一个Flutter 的全局检查功能,所有错误或者缺少的东西,都会被检查到。
还有,Flutter 是依赖Dart SDK 的,所幸Flutter 会自动安装 Dart 到自身目录下,一般而言是这样的:
因此我们不需要单独再去下载其他功能包。
目前开发Android 使用的应该都是 AndroidStudio ,我们打开 AS 软件,然后搜索两个 plugin :
- flutter
- dart
安装后重启AS,就会在启动后看到这样的功能按钮:
当然,如果AS提示有错误,则可能是Flutter SDK路径未自动配置,想这样配置一下 Flutter 和 Dart
如果AS启动后,首页界面没有 Start a new Flutter project
这个选项,则可能是有个 plugin 没打开,这是只要把这个插件置于打开状态就好:
二、 创建 Flutter Project
然后我们就可以创建一个flutter 项目了:
点击Start a new Flutter project
,可能会有些慢,稍等几十秒,然后系统会弹出需要创建的 flutter 项目类型类型。
1、 创建 flutter application
flutter application
是指创建一个正常的Flutter ,即创建一个适用于Android 及 IOS移动设备的项目,创建过程由 idea 一键完成,并不需要有多复杂的步骤。
上面一次含义为:
- Project 项目名称,和一般项目相同
- Flutter SDK 目录,这个一般会自动配置
- 项目所在目录
- 项目介绍
这些和普通创建一个项目是没有区别的,点击 next
,配置项目其他信息:
先需要配置一下包名,包名对于android 和ios 来说,不同应用应该不同。
然后需要根据需求,配置 ios 是否需要加入swift
支持,android 是否需要加入 kotlin
支持。
然后点击 finish
,项目创建成功:
android
,ios
目录下分别对应两个平台各自的代码。lib
目录包含了我们将要编写的业务逻辑代码,这里会自动生成一个入口界面:main.darttest
目录用于单元测试或者组件测试pubspec.yaml
是所有配置所在的地方,其中指明了:项目名,项目描述,项目当前版本号,sdk版本,运行或开发时依赖等内容。
观察AS界面,可以看到如下部分:
在真机已连接,入口类已设定情况下,点击三角箭头,应用就会安装到真机上。然后就可以使用热更新进行快速高效的开发了。
这里我们不仔细讨论Flutter 如何编码,只用一个现有的例子来查看显示效果,主要模仿华容道进行一个关卡设置,可以针对卡片进行上下左右滑动:
代码可以在 github 上下载查看:checkpoint_one.dart
PlusPlugins.dart
代码在另一个类中,这里直接写出来:
///
/// 额外添加的class系列
///
///定义系列的返回特定类型的函数,只有一个参数
typedef RBoolOneP<T> = bool Function(T obj);
typedef RVoidOneP<T> = void Function(T obj);
typedef RIntOneP<T> = int Function(T obj);
typedef BBB<T> = int Function(List<T> obj);
///定义系列的返回特定类型的函数,无参数
typedef RBoolZeroP = bool Function();
typedef RVoidZeroP = void Function();
typedef RIntZeroP = int Function();
这里只用 debug 包进行演示,只要知道 release 比 debug 模式要顺畅就可以了:
可以看到,流畅效果不比 原生的差了。
2 、创建 Flutter Plugin
Flutter Plugin 从名字就可以看出,主要是一个插件,主要作为一个中间件,来连接原生 与 flutter 。
flutter 如果需要调用原生功能,比如照相机等硬件设备时,自身肯定无法完成这种功能,这时就需要使用 plugin 插件,通过相互通信,让原生来调用设备能力。
Flutter Plugin 创建流程和 Application 差不多,创建完成后,代码结构如下所示:
lib
下 的文件成为了 插件类,我们可以在其中约定原生与flutter 相互调用的方法,如,我们在 flutter_plugin.dart
中添加如下代码:
import 'dart:async';
import 'package:flutter/services.dart';
///编写插件的部分
class FlutterPlugin {
///方法通道标识,用于在 平台间 调用接口,该字符串为多平台统一的
static const MethodChannel _channel = const MethodChannel('flutter_plugin');
///通道可调用的方法/参数: 需要设置为异步(确定不会阻塞主线程)
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
///获取随机的double值
static Future<double> get randomCode async {
return await _channel.invokeMethod('getRandomCode');
}
}
然后在项目的 android 目录下,编写插件对应的android 实现(kotlin 语言)
然后在 example/lib/main.dart
中,可以测试插件是否编写的正确:
注意,example
是idea 自动帮我们生成的一个内置 project,我们可以看一下example/pubspec.yaml
文件:
即,example 项目引用了我们创建的 plugin ,所以,才可以使用该 project 测试插件的功能。
这里还有一个问题,flutter_plugin
插件如何知道 flutter 与 原生哪个类进行对应的?事实上,在 flutter_plugin/pubspec.yaml
中也进行了指定:
红线标识部分说明了该插件对应了 com.knowledge.mnlin.flutter_plugin
包下的 FlutterPlugin
类,于是项目编译时,在 flutter_plugin/example/android/
项目里,GeneratedPluginRegistrant
会对指定的插件进行 注册。
说了那么多,来看一下,插件是否可以正常运行;我们指定这个类为入口,然后真机测试:
在这个类中,进行原生调用,然后查看效果(注意,这里放了一个三秒间隔的循环定时获取随机数):
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
double _random = 0xFFFFFFFF;
@override
void initState() {
super.initState();
initPlatformState();
//再次加载random
Timer.periodic(Duration(seconds: 3), (it){
initRandom();
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await FlutterPlugin.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
Future<void> initRandom() async {
_random = await FlutterPlugin.randomCode;
setState(() {
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\nRandom:$_random'),
),
),
);
}
}
效果如图:
3、Flutter Package
Flutter Package
只是单纯的 flutter 包,我们在其中可以创建一个通用的 widget ,或者当做一个工具包等等,相当于一个弱化版的 Flutter Plugin。
创建过程不必多说,代码结构如图所示:
只需要在 lib目录
中编写公共的widget或者通用的逻辑方法即可。
4、Flutter Module
在继续往下介绍之前,可以先了解一下大致的flutter module 应用场景。
目前现有的应用,基本都已经成型,并且由于flutter 还并未完全成熟,因此如果使用的话,最多也只是替换现有项目部分内容当做一个Flutter模块。
Flutter 模块的创建需要遵循一定的规则,我们需要在项目的同级目录建立一个Flutter Module,然后有两种方式在原生代码中引入 Flutter (以Android 项目为例)
- 将Flutter当成一个 View 或者Fragment,并入到现有的Activity类中。
- 创建FlutterActivity 的实现类。
两者我更推崇后一个,因为后一个可以避免去处理多余的一些事件,比如返回键逻辑等等。
如果想以View 或者 Fragment 的方式引入 Flutter,可以参考这个博客完成:Android原生项目集成Flutter Module
当然这个博客都需要先看一下,里面包含了针对Android 的 build.gradle 以及 settings.gradle
的修改部分。这个是都需要配置的
如果以FlutterActivity 的形式引入Flutter,首先创建Flutter Module的过程都是一样的(都需要在原生项目同级目录下):
和一般的Flutter项目仅有微小的不同,就是在 pubspec.yaml
中,会指明本项目为一个 附属module。
然后依照教程,使用AS在另一个窗口打开原生Android项目,在安卓项目的 settings.gradle
中添加如下代码:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'gft_flutter_module/.android/include_flutter.groovy'
))
在 安卓项目主module
的build.gradle
文件中添加如下依赖:
dependencies {
// ...
//flutter - module
implementation project(':flutter')
// ...
}
然后创建一个 FlutterActivity 的子类:
class FlutterContainerActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
}
companion object {
/**
* 进入flutter时,可提供默认的路由
*/
fun makeIntent(context: Context, routePage: String?): Intent {
var _routePage = routePage
if (_routePage == null || _routePage == "") {
_routePage = "/"
}
val intent = Intent(context, FlutterContainerActivity::class.java)
intent.action = Intent.ACTION_RUN
intent.putExtra("route", _routePage)
return intent
}
}
}
别忘了,该Acitivity 时需要 在 AndroidManifest
注册的 。
然后我们需要进入Flutter 模块功能界面时时,直接调用如下代码即可:
startActivity(FlutterContainerActivity.makeIntent(baseActivity, "/"))
这里展示一下使用Flutter模块效果图,开始是原生界面,然后点击进入的就是FlutterContainerActivity界面,两次进入传了不同的路由,因此显示了不同的Flutter界面:
当然,很多情况下,我们在引入module时,flutter中代码逻辑可能需要调用 原生代码,此时,可以直接修改一下FlutterContainerActivity
代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
registerWith(flutterView.pluginRegistry.registrarFor("***.***.***"))
}
注:***代替的字符串部分只要使用唯一标识就行,一般可以用项目包名
在 onCreate 方法中,最后添加了一行插件注入的代码;registerWith
方法是静态导入的,源码在插件类中:
/**
* Created on 2019/4/26 18:32
* function : 定义的 flutter 和 android 交互监听
*
* @author mnlin
*/
class UsdpBridgePlugin : MethodChannel.MethodCallHandler {
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
println("flutter在调用android端的方法: ${methodCall.arguments} +++ ${methodCall.arguments::class.java}")
when (methodCall.method) {
"getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
"changeLocaleLanguage" means "修改语言环境" ->
//...
"changeThemeMode" means "模式切换" ->
//...
else -> result.notImplemented()
}
}
}
/**
* 注册插件回调
*/
fun registerWith(registrar: PluginRegistry.Registrar) {
MethodChannel(registrar.messenger(), "***").setMethodCallHandler(UsdpBridgePlugin())
}
注:***代替的字符串须要和Flutter中指定Channel的字符串相同,这样才可以互相调用。如果忘了是哪个字符串,可以参考博客前面创建 Flutter Plugin的部分,在Flutter插件类中代码处static const MethodChannel _channel = const MethodChannel('***');
传入的字符串。
当然,若还是不清楚原生项目和flutter-module如何交互,可以查看此博客:Flutter知识点: Flutter与原生(Android)的交互
最后一定别忘了,在原生项目的 Application
类中 onCreate
时,先把Flutter模块初始化了,保证Flutter 与 原生无缝切换界面:
//初始化flutter框架
FlutterMain.startInitialization(this);
以上简单讲述了 Flutter 的使用方式,其实主要关注两个就行:第一,创建flutter跨平台项目;第二,让Flutter可以与原生项目进行合并。
三、 flutter_web——向WEB端前进
既然Flutter已经做到了横跨Android 和 IOS 两大平台,那么能否把Web端包含在内呢?这样不就实现了前端的 统一么?
然而,可惜的是,Flutter 并没有这个功能。
不过解决问题的方式总比困难多一些,官方已经给出了解决方案,或者说是一个 超功能的库:flutter_web
实现Flutter 项目 向 web端的迁移 ,不得不说,这个想法很不错,dart语言的初衷就是为了替代 js ,虽然失败了,但至少有过丰富的失败经验,那么依据dart语言的flutter做这些工作时,至少不会再开头就碰到太多的难题,也不用担心 此种方案的可行性。
flutter_web的README很详细的说明了迁移需要做的工作,然而,实在是 不想看那么多的逻辑。
好在我们都使用的是AS,那我们直接看 AS如何一键操作就好了,何必那么麻烦。
然而,使用AS 发现还是有些问题,根本没办法创建web项目,因此我们改用 Intellij 开发工具。步骤在 readme中已经列出来,像这样:
- install the Flutter SDK
- set up your copy of IntelliJ or Android Studio
- configure IntelliJ or Android Studio to point to your local Flutter SDK
- create a new Dart project; note, for a Flutter for web app, you want to start from the Dart project wizard, not the Flutter project wizard
from the Dart project wizard, - select the ‘Flutter for web’ option for the application template
- create the project; pub get will be run automatically
- once the project is created, hit the run button on the main toolbar
- IntelliJ will use the webdev command-line tool to build and run your app; a new Chrome window should open, showing your running app
SDK在前面步骤已经下载过了,那么接下来我们就完成剩下步骤:
首先,打开intellij 的settings 配置,添加 Dart 和 Flutter 插件,没有插件,我们是没办法创建项目的。
然后按照如下图创建一个 flutter - web 项目:
接下来选择创建项目的类型一定不能出错:
指定一下项目名:
点击 finish
创建完成。
等待idea把需要的依赖下载完成后,点击配置一下浏览器(URL地址不要主动去更改了):
配置后点击三角启动图标,如果启动不起来,不要着急,把idea重启一下就可以了。
启动后网页会自动打开,然后显示出默认的界面:
现在回头我们再看一下flutter - web 的目录结构:
lib目录中存放的是移动端flutter代码。
web目录下包含两个文件,index.html是网页入口,web/main.dart
可以理解为从 flutter 到 web 的一个过渡桥梁,代码很简单,就是在网页初始化完成后,启动真正显示的布局信息。
main() async {
await ui.webOnlyInitializePlatform();
app.main();
}
当然,从 flutter 的代码是不能直接迁移到 flutter-web 的,需要更改一小部分内容,具体逻辑在github的readme文档中已经进行了说明:
If you’d like to migrate existing Flutter code to run on the web preview, read the migration guide.
flutter-web的更新也类似于hot-reload,在项目中修改代码后,直接刷新一次网页就可以显示出最新效果。
调试开发完成后,我们还需要进行部署,这个时候,就需要主动调用命令了,我们在启动服务时,可以很清楚的看到这样的内容:
这里指明了 webdev
包所在的位置,在打正事环境包时,先关闭调试的服务器,执行以下代码即可:
E:\flutter_sdk\flutter\bin\cache\dart-sdk\bin\pub.bat global run webdev build
切记,webdev
包的路径每个设备可能不同,因此一定要查看清楚再调用命令。
执行完成后,项目目录下多出 了一个 :
这个 build 目录中内容就是我们需要的正事包。切记,这个包直接双击浏览器打开index.html 文件是不行的,必须要放到 http 服务器中,这个官方在 readme中也有说明:
This will create a build directory with index.html, main.dart.js and the rest of the files needed to run the application using a static HTTP server.
我们可以进行简单的测试,看这个正事包能否正常使用,为了简单起见,我们使用 node 搭建一个小型的服务器:
然后,我们把通过命令生成的 build 目录下所有的文件,都拷贝过来,放到 node 项目的 public 目录下:
点击上面的三角形运行标志启动http服务;然后在浏览器中输入地址(node服务器默认启动端口号为3000):
http://localhost:3000/
然后浏览器中就会显示 flutter 指定的界面:
目前 flutter_web
大概只有2000多star,毕竟前端人员几乎都在使用vue,也没有 迁移转换使用dart 的必要性。
不过,将来的趋势肯定倾向于前端的大一统,相信随着google的大力推行,会有更多的人接触使用flutter。
参考资料主要来源:Flutter中文网