理解 Flutter

[此处应该有一张图]
本文均依据于flutter官方文档流程,只梳理其中的坑点。
使用方法:由于本文可能会较长,知识点零散,请读者善用Ctrl+F。
基于Flutter2.0
Windows+VS Code向

请注意:本文 不是教程内容,章节名与flutter官方文档对应章节一致,只用于备查和帮助学习中遇到问题的童鞋,欢迎指正。

开始使用Flutter

安装和环境配置

系统选择:Windows

获取 Flutter SDK

从教程中的链接下载最新的stable安装包,建议解压到其他盘或者用户路径下。然后win+s输入env添加flutter的bin路径到环境变量中。

这里就有一个问题,修改环境变量后,需要使用环境变量的进程(如:ternimal,cmd,vscode等)都需要重启,保证进程内环境变量被封信。

知识点:系统环境变量只会在启动进程时注入进程,由一个进程向启动的另外一个进程注入。

运行 flutter doctor

flutter doctor检查flutter相关环境是否可用,按照你的需要来判断,如果红叉部分和你要用的无关,那么忽略它就是。如你想要用VS Code做Web开发,你只需要关注Flutter、浏览器和VS Code是不是勾,其他的叉叉对你没有任何影响。

检查结果里如果Android toolchain是叉叉,你又不想装Android Studio,那么自己安装Android SDK和Java就可以了。如果你安装了VS,那么用VS Installer安装xmarin相关的安卓SDK、虚拟机、JDK等组件就可以搞定。

请注意,Flutter教程中提供的Google USB Driver下载链接只是一个公版驱动,很多国产手机不一定能够适配,请自行安装手机厂商自己提供的驱动。如果厂商没有提供驱动或者下载安装之后仍然检测不到,不用担心,在安装好Android SDK后,在Android SDK管理器里安装附加程序中Google USB Driver己可。

提示:如果你安装了Microfost Edge,检查结果中Chrome是叉叉也没关系,Flutter能够识别Edge。

编译工具设定

本教程选择:Visual Studio Code
注意点,一定要按照教程安装插件后重启VS Code。然后,按快捷键 Ctrul + ` 打开终端,键入Flutter命令是否能找到。

这里就有一个问题,VS Code终端可能会提示Flutter命名无效。原因有以下几点:

  1. 很多童鞋在前面教程中下载Flutter SDK后解压放到了高权限目录,如C:盘除用户目录之外的其他目录下。这里建议一般放在其他盘或者用户目录下。
  2. VS Code在设置Flutter的环境变量前就已经启动,或者启动VS Code的资源管理器/启动器就已经打开。所以,喜欢使用启动器的小伙伴注意了:

如果启动器没有重启过,那么它的环境变量中必然是没有FlutterSDK的Path的。请使用开始菜单或桌面图表重启VS Code,或者重启启动器再启动VS Code,不必像网上那些教程那样重启电脑。

开发体验初探

这个环节讲如何创建应用和编译运行应用。

注意,即使装了插件,在VS Code的Ctrl+Shift+P 中也没有全部命令,只有一些常规的。

在官方后续的教程中,可能是因为教程不是由单一人员写就,同样的操作可能在不同的地方介绍的方式会不统一。所以,作为程序员,应该学会全部使用终端输入命令的方式。本文都是采用终端命令来讲述操作

前提一:要创建Windows桌面应用吗?

官方教程链接:Flutter 桌面支持

在Flutter2.0版本中,桌面应用还是处于beta版本中,幸运的是stable版本包含了一份Beta版本的快照,如果你需要尝试Windows桌面应用,那么你需要在终端中执行如下命令:

// 启动windows桌面支持
PS >flutter config --enable-windows-desktop
// 检查Windows的状态
PS >flutter devices
// 检查Windows开发相关有没有安装
PS >flutter doctor
前提二:在国内开发吗?请设置镜像站的环境变量

官方教程:在中国网络环境下使用 Flutter

因为在国内访问速度很慢或者有的地区的童鞋根本就不能访问官方的包管理,而在创建应用的时候,会去包管理服务器下载一些依赖包,就会出现400系列错误或者500系列错误(因为网络原因,官方的服务器会随时挂掉)

注意:官方给出了好多个镜像地址,经过使用后,推荐CNNIC,或Flutter社区,发现清华的不是太稳定,上海交大的十分不稳定。具体镜像情况查看这个网址:Flutter 镜像状态检测 (uptimerobot.com)

官方教程中没有windows平台设置方法,开发windows的童鞋下按照以下步骤设置:

1. win+s 搜索env打开环境变量
2. 在系统变量中`新建`输入PUB的环境变量。如:变量名:```PUB_HOSTED_URL``` ,变量值:```http://mirrors.cnnic.cn/dart-pub```
3. 和FLUTTER的,如:变量名:```FLUTTER_STORAGE_BASE_URL```,变量值:```http://mirrors.cnnic.cn/flutter```
4. 保存后重启VS Code,注意前文的环境变量注入原理
创建应用
// 简单写法
PS >flutter create yourappname
// 复杂写法
PS >flutter create --template app --platforms linux,windows,android --project-name yourappname .\YourAppProjectFolderName\
// 为已有的应用添加桌面支持(多个平台名称用英文逗号隔开,如:windows,linux,maos)
// *** 最后的`点`很重要,是当前应用目录的意思,别忘了 ***
PS >flutter create --platforms=windows .

注意:项目名称一定!一定!一定!不能有大写,单词之间想隔开用_,dart不允许项目名称有大写。但是,文件夹名字可以随便写大小写??

运行应用
// 运行当前app的命令,会自动按照当前选定的设备进行编译。-v 参数是打印详细日志
PS >flutter run -v

如果是要开发安卓APP的小童鞋,要注意了。
####### 1. Gradle生成工具
咱们安卓的Gradle生成工具要访问国外的链接,不然编译就会出现各种报错问题,如卡在Gradle task assembleDebug。遇到这个问题,可能需要替换成国内的包源。

这里只介绍项目内的更换Gradle包源的办法,全局更换方法请google就是。

找到项目中的:\android\build.gradle 这个文件,对照如下代码修改

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        //google()
        //jcenter()
        // 下面两句是阿里包源
        maven { url 'https://maven.aliyun.com/repository/google/' }
        maven { url 'https://maven.aliyun.com/repository/jcenter/'}
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        //google()
        //jcenter()
        // 下面两句是阿里包源
        maven { url 'https://maven.aliyun.com/repository/google/' }
        maven { url 'https://maven.aliyun.com/repository/jcenter/'}
    }
}

####### 2. JDK版本之谜
咱们的Flutter兼容的JDK版本是个谜,编译会报各种错误,网上有人说是只兼容JDK8,更为新的版本有问题。JDK有几个下载渠道,在Oracle下载的应该没有问题。

强烈建议不要!不要!不要在OpenJDK这个Oracle的换皮网站下载,我下的所有的二进制压缩包发现都不全,最后上微软官网下了一个,配置环境变量(记得要重启vs code)后正常使用,就是版本号奇奇怪怪的。

20201/04/09热点更新:伴随着微软在Google和甲骨文的Java专利官司中胜诉,微软立马发布自己的OpenJDK——Microsoft Build of OpenJDK,以后可以到微软官网下载JDK。

####### 3. 编译出错怎么查看更多信息
安卓采用Gradle进行构建,所以当出错后我们自然使用Gradle查看更多信息,那么Gradle在哪里呢?

// 在你的项目路径下的这里
./android/gradlew

知道它在哪里,终端中错误的相关建议就知道怎么做了。下图里的* Try: 后面的内容是指的Gradle而言,所以我们就能通过这样的命令查看调用堆栈信息:

PS >./android/gradlew compileDebug --stacktrace -info
终端中的错误信息

####### 4. 常见问题:

1. 'Finished with error: Gradle task assembleDebug failed with exit code 1' 
出现这个报错要检查下JAVA的环境变量设置是否正确,VS Code有没有重启。
2. `javax.net.ssl.SSLException` 
出现这个异常一般是JDK版本不兼容,更换为Flutter兼容SDK即可。
3. `Gradle "Could not open buildscript class cache"`
出现这个报错一般关闭VS Code,删除C盘用户目录下.gradle下caches文件夹?;共荒芙饩鲇锌赡苁荍DK版本不兼容,更换为Flutter兼容SDK即可。
4. 出现运行错误,打印日志中提示需要 Android SDK Build tools 的某个版本
出现这个问题,打开Andorid SDK管理工具,勾选下载Android SDK Build tools下的对应版本即可

开发文档

用户界面

Widgets介绍

这个章节介绍,Flutter中一切UI元素都是Widget(组件),这是个基类。所以就肯定有继承于基类的通用的基础组件,和由基础组件组成应用于不同场景的高级组件,也肯定可以自己编写组件类。虽然逻辑抽象反而会增加代码复杂度,面向对象(在面向UI对象上)真的是的好工具。

其中,我觉得传达出一个很重要的概念:组件和状态分离。

如果熟悉MVVM模式(vue.js、WPF)的童鞋一定很熟悉UI与数据分离的模式,通过更新数据就可以达到更新UI的效果。而Flutter采用的是另外一套MVU模式,我个人感觉是脚本+闭包的嵌套写法。

初次接触这种写法刚开始肯定会被搞蒙:代码和数据混杂、作用域混乱、又臭又长,代码层级巨深,阅读体验极差等。但是,它也有它的好处,就是一致性强。数据和UI在一起,清晰的代码走向。所以,在一开始学习的时候,就要做好使用好面向对象这个工具,将可以抽离的??樾纬衫?、方法、对象等。而,代码的简洁和性能之道也是需要在后续的使用过程中持续提高的,可能在使用了flutter几年之后,你仍然能提升写法使你的代码更加优雅。

布局构建
Fluter中的布局

笔者认为的布局组件结构表如下:

级别 标准 widgets Material widgets
基础 Row、Column、Center、ContainerStack
高级 ListView、GridViewTable Card、ListTile、DataTable

####### 1. 布局要点
第一,常规布局使用基础基别的标准widgets即可,平面布局Row、Colume互相嵌套再加上Container,Container就是一个常见UI框架的容器大集合,和div很像。层次布局使用Stack嵌套。
第二,在一些局部的复杂数据呈现、批量同类数据和特定局部布局中使用高级的布局组件可以简化布局工作。
最后,因为MVU的代码编写方式是高度嵌套的结构,为了最大限度地减少嵌套深入产生的代码阅读体验差,可以在变量和函数中实现 UI 的各个部分。

布局约束[新增]

总结起来就是:

子组件的成长空间由父组件决定,而父组件和现实世界相同则有两种:放任自流和进行约束,放任自流又分为自私和儿控,约束又分为宽松约束和严格约束。

注:这里是胡乱分类取名,搏君一笑尔,会意即可,切勿认真。

  1. 放任自流,子组件可以充分发挥自己的长度,不受屏幕限制。容易产生越界行为。
  2. 自私指的是,自己受父控件约束,子控件不管。
  3. 儿控指的是,既受父控件约束,在这个前提下能跟子控件一边大就一边大,没儿子跟才父组件一样大。
  4. 宽松约束是指下限可以跟上限不同,子组件在中间随意,越界不行。
  5. 严格约束是指下限和上限一致,子组件必须和父组件的约束一样大。

So,常见组件的约束分类如下:

父组件类型 约束类型 组件
放任自流 自私 Row、Column、UnconstrainedBox、OverflowBox
儿控 Container、FittedBox
约束 宽松约束 Center、Align、ConstrainedBox、LimitedBox、Flexible、Scaffold
严格约束 App、Expanded、SizedBox.expand()

####### 容易忽略的点

  1. Row不会对子组件有任何约束,所以在Row中直接放置Text,Text接收不到任何约束。在文本过长的情况下,会直接超出屏幕。为了能实现文本自动换行的效果,需要给Text加上Expanded,因为Expanded的宽度为自动填充计算出来的,由它来传递约束给Text,Text就能根据约束来进行换行。
  2. UnconstrainedBox和OverflowBox虽然对子组件无限制,但是前者在子组件越界了会管一下报错,后者不会管。
  3. FittedBox它有的空间都会给儿子,但是儿子不能贪心要天上的月亮(无穷无?。?,不然什么都没有。
  4. LimitedBox能对子组件进行限制,但是却需要它的父组件对它没有限制。
  5. Flexible和Expanded都会忽略其子控件的宽度,而Flexible只是会让瘦弱的孩子得到刚适合它的大小。
添加互动

互动中最核心的还是状态的问题,Widget分为无状态和有状态的。无状态的组件意味着数据无关,创建后就UI一直不变到生命消亡。有状态组件有着状态(数据),在生命周期内数据会发生变化而影响到UI变化。

看着这种区别泾渭分明,实则不用过多区别。其实它是一个层级关系,最基础的组件都是无状态的,有状态的组件只是由无状态组件组合成了一个能单独支撑某一特定类型业务数据呈现的层面。实际上它的状态更新导致的UI更新,也是对子无状态组件的一次销毁到重新构建的过程。

而互动中的状态管理的意思是,对于有状态的组件应该怎么样管理状态。总结一句话就是:

业务状态业务控件管,界面状态自己管。和人相关父亲管,自身状态自己管。

其实,熟悉MVVM的童鞋这个就很好理解,状态对应ViewModel,我们会在Control和View层级对应相应的ViewModel。而控件自身的显示状态、动画、效果等均由控件自身(也就是样式)自己来维护。体现的就是一个作用域思想,也可以理解成层级关系。让合适的层级干它能干的事情就行,不去可以扩大状态管辖范围也不缩小状态范围还得进行状态同步。

这里介绍一个我认为的设计原则:

能做无状态的Widget就做成无状态,减少状态管理的位置,进而减少状态耦合和代码复杂度,增加状态的聚合度。通过父Widget状态更新让Flutter重新构建这个无状态Widget达到更新界面的目的。

Windows窗口美化

一般来说,任何一个桌面商业软件,都必然是要将默认系统窗口的窗体去掉,自己进行修饰的。修饰内容就主要包括:标题栏,三金刚键,标题拖动,窗体大小,窗体位置,边框和阴影,窗体背景。

一般对于win32程序员来说,这根本不是一个问题,因为他们会自己对flutter项目中的windows文件夹中的win32代码进行修改,实现他们想要的任何窗体效果。但是对于不熟悉win32的程序员来说,这就是一件特别难受的事情了。

很遗憾,直至2021/04/21,windows的CSD(Client-Side Decoration)支持还未得到官方支持,有兴趣的可以去这个issue了解信息Desktop Client Side Decoration support · Issue #31373。

其中就包含一个解决方案,是一个开发者也是遇到需要美化窗体时,看到这条issue就自己动手实现了一个库来解决这个问题。pub链接:bitsdojo_window,感谢他的付出。

同时,开发者还贴心的录制了视频放在某Y屏蔽网站上,B站的搬运在此:Flutter desktop - 自定义窗口标题栏包含Windows窗体的最大化最小化和关闭按钮

这里需要注意:因为Flutter对于各个平台的runner实现均放在对应平台的文件夹下,windows文件下就有对应源代码windows窗体相关的win32窗口和dart的封装,但是经过一番研究和修改后发现它的主要结构还是为主窗口+flutter子窗口,他们都不是layer层。主窗口为进程的启动窗口,子窗口用于放置flutter的app内容。可通过修改实现移除默认windows窗口,增加顶部拖动,增加按钮,增加阴影等一系列功能,但是不能增加背景透明度达到类似win7的毛玻璃效果。所以要实现背景透明类似的效果需要等待官方的实现。

未完待续...

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352