本文是给第一次接触flutter的原生开发iOS/android同窗快速入门的攻略,高手请绕路,轻拍哈。前端
对于原生开发的同窗,对于flutter会比较感兴趣,也许会从网上零星得到一些学习资源,可是比较零散,不构成学习路径,可能也会踩一些坑,为了不少走弯路,又能快速的入门flutter,现将我的的一些实践经历分享一下,供你们批评指正补充。android
1.1 先下载flutter编辑器 android studio,最新版本,解压,安装。。。(如下简称AS)ios
1.2 安装flutter 已iOS为例git
cd ~/development unzip ~/Downloads/flutter_macos_v1.9.1+hotfix.2-stable.zip 复制代码
先编辑bash_profile文件(默认状况下,macOS Mojave(及更早版本)使用Bash shell,所以编辑$HOME/.bashrc)github
$vim ~/.bash_profile 复制代码
添加如下路径shell
export PATH="$PATH:`pwd`/flutter/bin"
macos
完整的以下编程
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" export PATH="/Users/boob/Documents/flutter/bin:$PATH" export PATH="/usr/local/opt/openssl/bin:$PATH" export PATH="/Users/boob/Documents/depot_tools:$PATH" export PATH="/Users/boob/Library/Android/sdk/platform-tools:$PATH" export PATH 复制代码
编辑完成使用wq退出,便可执行flutter命令了swift
flutter --version
看看版本号, which flutter
能够查看flutter安装的路径ios的同窗默认安装了xcode,没有的话去安装一个吧,安卓同窗须要下载android studio,后续开发都是须要用as进行开发和调试的。 为了第一次能直接运行flutter,咱们先开一个模拟器,而且把其余真机设备移除,防止后面操做找不到运行目标,或不知道如何选择设备vim
前提操做 打开模拟器命令
open -a Simulator 复制代码
检测pod的版本号是否高于1.6.0
pod --version
复制代码
flutter默认最低支持的pod版本是1.6.0,若是使用到plugin时就会提示版本太低,致使pod失败了
flutter create testflutter
复制代码
注意工程名字都要小写,不然会提示你命名出错。
$ cd myapp $ flutter run 复制代码
此时flutter会编译dart代码,而且签名运行
咱们进入到flutter源码目录
ios目录存放ios工程,android则存放android工程 对于ios来讲,编译的产物在ios/Flutter文件夹中 包含了
App.framework 这是flutter工程编译出来的ios产物,对于debug编译来讲,flutter_assets包含了全部的可执行产物和资源 对于release编译来讲,可执行的部分在APP文件中,资源存放在flutter_assets中
flutter.framework 俗称flutter engine/flutter 引擎,支持上层flutter运行的底层库。
xxx.xcconfig 工程配置,flutter命令自动生成的,为了配置flutter路径,flutterframework的路径
flutter_export_environment.sh 1.9新增的脚本,配置flutter经常使用的环境变量
这是flutter的吹嘘的几大特性之一,跨平台一致性,热重载。。。 即写完代码能够当即执行。 编写flutter代码咱们使用android studio,官方推荐3.0以上的版本 developer.android.com/studio
咱们可使用最新的,由于已经使用了最新的flutter插件功能,包括断点调试,attach,性能查看分析等。
用AS打开testflutter工程
如今能够点运行按钮▶
️,直接启动flutter,这个跟在终端启动效果同样。
修改一下源码,把标题改为个人第一个flutter项目
,以下
而后按下 cmd+s 保存,便可在模拟器上看到运行结果
默认提供两套ui库,一套是android风格的Material Design
和ios风格的 cupertino (连接是传送门)
下面感觉一下差异
咱们使用一个button试一下 在main.dart的scaffold的中添加代码
CupertinoButton( child: Text('CLICK ME'), color: Theme.of(context).accentColor, onPressed: (){ print("点击了按钮"); }, disabledColor: Theme.of(context).disabledColor, ) 复制代码
到了源码级别,原生的编程思路就须要开始转变了,因为原生开发都是命令式编程,然而前端和flutter是声明式编程的。
对于命令式,是指若是咱们要对一个文本内容和文本颜色改变,咱们就去取到这个文本的text和textcolor 而后对text和textcolor进行赋值。
然而对于声明式,要改文本内容,须要将文本的内容text和文本的控件分别先声明
全部布局的控件都写到 Widget build(BuildContext context) { ... }
方法中,可是 可是控件须要用到是内容而且可能改变的,则使用一个变量记下来.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
复制代码
final String title;
就是声明了一个title的字符串。
或者
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
...
}
复制代码
AppBar(
title: Text(widget.title),
),
复制代码
Text( '$_counter', style: Theme.of(context).textTheme.display1, ), 复制代码
这是和原生最大的差异,须要改变文本的内容则须要使用setState中生效,告诉flutter这时候状态变化了,须要从新刷新。
例子中单击+
数字+1,能够看到界面上的数字当即更新了
void _incrementCounter() { setState(() { _counter++; }); } 复制代码
值得注意的是,state频繁刷新也会带来性能问题,不可滥用哦。 其余代码你们能够自行研究,都是声明式编程
的运用。
widget在ui层面至关于原生的uiview,可是不只仅局限于显示视图UIView,也有用于布局相关的。
Container、Button、Row和Column、Text、Scaffold、Icon、Image、Stack、TabBar+TabBarView、Widget-输入框TextField
Align、Center、Expended、LayoutBuilder、Padding、Wrap
CustomScrollView、GridView、ListView、PageView、SingleChildScrollView
BoxDecoration、Clip系列、Opacity、SafeArea、高斯模糊BackdropFilter
咱们知道横向布局使用Row 纵向布局使用Column Wiget 布局对其方式分为主轴和交叉轴,若是是Row布局主轴mainAxisAlignment就是横向,而其交叉轴就是纵轴, 主轴排列方式有6中,start,end,center,spaceAround,spaceBetween,spaceEvenly
Padding(
padding: const EdgeInsets.all(0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
)
复制代码
效果以下
Padding(
padding: const EdgeInsets.all(0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
复制代码
效果图以下
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
复制代码
效果图以下
start 表示将子级放置在尽量靠近主轴起点的位置。若是此值沿水平方向使用,则[TextDirection]必须为可用于肯定起点是左侧仍是右侧。 若是在垂直方向上使用此值,则[VerticalDirection]必须为可用于肯定起点是顶部仍是底部。
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
复制代码
效果图以下:
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
复制代码
效果以下
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,),
Container(color: Colors.red, width: 50, height: 50,),
Container(color: Colors.blue, width: 50, height: 50,)
],
),
复制代码
混合工程方案网上有很多,可是真正的核心只有一个,就是将flutter工程的构建产物加入到原生工程中来。
咱们已经知道了flutter的构建产物有App.framework 其余的就是Flutter.framework,还有就是dart依赖的第三方库,多是plugin也多是dart库,能够在.symbol文件夹中找到(.xxx开头的文件默认是隐藏的,使用shift+cmd+. 将其显示出来)
要想加入到主工程,没法就是将三个东西作成pod的形式,在pod install/update的时候将他pod进来。这样就能作原生开发人员无感知的使用flutter工程。
业界有一个主流的方向,安装产物的本地和远程来划分,本地依赖方式和远程依赖方式。没法仍是把产物放哪的问题。
提供另外一个视角看混合工程可能更好。咱们按照角色划分混合方案,最多三种角色
这三种角色的诉求分别是,
原生开发无需安装flutter,可是会用到flutter的产物。
其余人员都须要安装flutter,因此需求差很少。 a、安装了flutter的同窗,可能也不想编译flutter产物而是直接使用,b、flutter开发的同窗可能只想编译debug的产物,可是不想上传,他们想debug构建并attach到 c、对于构建的需求是须要使用最新的代码构建release的包。
使用官方的混合方式没法解决以上全部的需求。本身开发混合工程脚本,须要从以上角度考虑。
然而使用方法仅仅是简单的一句话
flutter_application_path = 'xxxflutter' load File.join(flutter_application_path, 'IOSFlutterConfig', 'start.rb') def GirFlutter puts "自动检测flutter是否存在,自动执行不一样的脚本" install_flutter_engine_pod end 复制代码
以后pod update
便可!
分为两个部分,flutter调用native的代码、native调用flutter的功能 官方提供了两种方式 methodchannel、eventchannel。
流程图以下,
另外plugin就是一种native和flutter通讯的最好的例子,咱们能够直接看他给的例子。
终端执行
flutter create --org com.example --template=plugin myplugin
复制代码
进入myplugin/ios/Classes目录 咱们看到 SwiftMypluginPlugin.swift
import Flutter import UIKit public class SwiftMypluginPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "myplugin", binaryMessenger: registrar.messenger()) let instance = SwiftMypluginPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { result("iOS " + UIDevice.current.systemVersion) } } 复制代码
能够看到这个插件建立了一个名为myplugin的FlutterMethodChannel,而且经过registar注册到了methodcalldelegate里面了。
在dart那边可使用调起该方法。
class Myplugin { static const MethodChannel _channel = const MethodChannel('myplugin'); static Future<String> get platformVersion async { final String version = await _channel.invokeMethod('getPlatformVersion'); return version; } } 复制代码
那么对于native如何调用flutter的代码呢 在methodchannel这个例子中,咱们看到已经给到了一个result过来了,咱们能够拿到这个result result: @escaping FlutterResult
,若是有新的事件想发出去,就不断回调这个result也能够。
self.result("第二次回调给flutter") 复制代码
使用流程相似,先注册-> 发送事件
FlutterEventChannel *evenChannal = [FlutterEventChannel eventChannelWithName:@"flutter_hummer_event" binaryMessenger:[registrar messenger]]; FlutterEventChannelHandel *handle = [FlutterEventChannelHandel new]; instance.eventHandel = handle; [evenChannal setStreamHandler:handle]; 。。。 self.eventHandel.eventsBlock(dic); 复制代码
dart层使用方式
先注册一个通知--> 监听回调
// 注册一个通知 static const EventChannel eventChannel = const EventChannel('flutter_hummer_event'); eventChannel .receiveBroadcastStream(12345) .listen(_onEvent, onError: _onError); // 回调事件 void _onEvent(Object event) { ... } // 错误返回 void _onError(Object error) {} 复制代码
!! 提示,plugin默认生成swift版的plugin,若是想要选择ios可使用-i objc选项,其余配置选项能够-h查询
flutter create --org com.example --template=plugin -i objc myplugin22
复制代码
欢迎你们批评指正补充,争取作最好最精的入门教程,持续更新中...
原创不易,如需转载请注明来源,共田君