重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Flutter是一个移动应用程序的软件开发工具包(SDK),具有以下特征:
超过十多年行业经验,技术领先,服务至上的经营模式,全靠网络和口碑获得客户,为自己降低成本,也就是为客户降低成本。到目前业务范围包括了:网站建设、成都网站制作,成都网站推广,成都网站优化,整体网络托管,重庆小程序开发,微信开发,app开发定制,同时也可以让客户的网站和网络营销和我们一样获得订单和生意!
跨平台应用的框架,没有使用WebView或者系统平台自带的控件,使用自身的高性能渲染引擎自绘
简化版的浏览器,最大限度在android和ios上统一UI,包括业务逻辑和用户体验
开发语言使用dart,结合C, C++, 和Skia(2D渲染引擎)构建
支持hot reload,包含着完整的控件和工具链
一切皆控件,控件是每个Flutter应用程序的基本构建块,与分离视图、控制器、布局和其他属性的框架不同,Flutter具有一致的统一对象模型:控件。一个控件可以定义:结构元素(比如按钮或菜单)、风格元素(比如字体或颜色方案)、布局的方面(比如填充)、一些业务逻辑等
组合大于继承,控件本身通常由许多小型、单用途的控件组成,结合起来产生强大的效果,类的层次结构是扁平的,以最大化可能的组合数量
强化版的WebView,框架仅提供一个View层,大部分功能要依赖原生
目前只能够运行大部分Dart代码(不能引入dart:mirrors或dart:html库)
Dart作为高级语言,支持面向对象的很多特性,并且支持基于mixin的继承方式,基于mixin的继承方式是指:一个类可以继承自多个父类,相当于其他语言里的多继承。所有的类都有同一个基类Object,这和特性类似于Java语言,Java所有的类也都是继承自Object,也就是说一切皆对象。
Dart 是一门面向对象的语言, 全部的类都是继承自 Object , 除了支持传统的 继承、封装、多态 , 还有基于组合(Mixin-based)的继承特性
类型推导(var/final/const)
var
final和const的区别
3.非零即真( )
4.字符串
5.集合
Dart中变量初始值为null,即使是int类型也可以是null(java中int默认是0, boolean默认是false); Dart支持自识别,可以是用var定义变量,也可以直接指定具体类型; final或者const都可修饰不可变的变量,final变量只能赋值一次,const是编译时常量。
int和double是num子类,没有float类型; 支持字符串模板,用${expression}的方式来实现字符串效果,类似如字符串拼接; String可以使用单引号或者双引号; Dart没有数组,只有列表; 其中List,Set,Map不是抽象接口,是具体实现类,可直接使用; Map的key没有指定类型,key类型不一致不会报错;key不能相同,但是value可以相同,value可以为null。 var name = 'Tom';
方法也是对象,方法可赋值给一个变量; 如果方法的参数是解构出来的可以通过 @required 注解标注为必填 const Scrollbar({Key key, @required Widget child}); 支持可选参数,可选命名参数用{}包围,可选位置参数写在最后并且使用[]包围 String say(String from, String msg, [String device]); 支持默认参数 void enableFlags({bool bold = false, bool hidden = false}) {…}; 以_开头的方法都是私有的。 void main() {
支持闭包,闭包能够访问外部方法内部的局部变量
1.空替换?? expr1 ?? expr2,如果expr1是non-null,返回其值。否则执行expr2并返回其结果; 2.条件成员访问?.P?.y = 4; 如果p是non-null,则设置y的值等于4; 3.类型判定操作符(as,is,is!); 4.级联操作,可以在同一个对象上连续调用多个函数以及访问成员变量;
和java不同的是,Dart可以抛出任意类型的对象; 程序不会强制要求开发中处理异常,但若发生异常,程序会中断; 其中异常主要分为Error和Exception两种类型。
创建对象可以不使用new关键字; Dart中没有public,private,protected这些关键字; 没有interfaces关键字,每一个类都是一个接口。我们可以用抽象类来类比java中的接口; Dart把多重继承的类叫做Mixins。
支持语法糖 Point(this.x, this.y); 每个实例变量都会自动生成一个getter方法,Non-final变量还会自动生成一个setter; 命名构造函数,使用命名构造函数可以为一个类实现多个构造函数,也能更加清晰的表明你的意图;
断言是如果条件表达式不满足则停止代码执行; 断言只在检查模式下运行有效,如果在生产模式下运行则不会执行。
Dart 以两种模式运行: Dart 1.x 有生产模式和检查模式两种模式, Dart 2.x 中移除了检查模式。
注:建议在开发/测试模式中使用 检查模式 运行 Dart VM ,因为它会添加警告和错误以帮助开发和调试过程;选中的模式会强制执行各种检查,例如类型检查等。
dart标识符可以包括字符和数字,但不能以 数字开头 。
Dart 是一种面向对象的编程语言。
代码说明:定义了一个类 TestClass ,这个类拥有一个方法 disp() ,方法可以实现在终端打印字符串 Hello Dart! ,使用 new 关键字创建类的对象,该对象调用方法 disp() 。
关于dart的学习还有很多;我列出如下: Flutter高级工程师进阶学习资料;需要可以私信我。发送“核心笔记”或“手册”,即可领取资料!
MaterialApp 是我们app开发中常用的符合MaterialApp Design设计理念的入口Widget。MaterialApp这个组件里面的参数比较多,而且一般在应用入口会用到,所以这里把它内部的所有参数都列出来了
基本用法:
可以看到我们在 App 的最外层直接使用了 MaterialApp ,可以指定App的名称( title ),App的主题样式( theme ),首页的组件( home ),路由跳转配置)( routes ),关于路由跳转我们在后面的章节中会介绍
Scaffold 实现了基本的 Material Design 布局结构, Scaffold 在英文中的解释为角手架,我们可以理解为楼体中的钢架结构,通过它可以构建一个页面
在Flutter应用开发中,我们可以将 Scaffold 理解为一个布局的容器。可以在这个容器中绘制我们的用户界面
下面是 MaterialApp + Scaffold 的组合的基本用法
AppBar 就是顶部的导航栏组件,支持自定义标题,左右两侧的工具栏按钮等
BottomNavigationBar 是底部的菜单栏组件
使用方法:
一般我们会定义一个全局变量如 _currentIndex 用于记录当前选中的下标。然后在 onTap 属性的回调方法中调用
setState(() { _currentIndex = index;}); 更新 _currentIndex 就可以实现底部菜单的切换。 BottomNavigationBar 一般会配合 BottomNavigationBarItem 一起使用(如下所示)
RefreshIndicator 是Flutter中的下拉刷新组件,一般配合 ListView 组件一起使用
Image 就类似于android中的 ImageView ,可以自定义图片显示的宽高
从网络中加载图片
从本地(File文件)加载图片
从本地资源加载图片
可以将byte数组加载成图片
TextField 就类似于android的 EditText
PageView 就类似于android中的 ViewPager
做 Android 的朋友都知道,在 Android 中,限定子元素大小的方法有三种,分别是 match_parent 、 wrap_content 、 fixed size 。如果使用 ConstraintLayout,还会有 match_constraint 。
这些值被设置到子元素的 layout_width 和 layout_height,由父元素解析生成 LayoutParams 设置给子元素,并在父元素的 measure 过程中使用它们以及父元素得到的 measureSpec 来确定子元素的大小。虽然子元素的大小最终由父元素决定,但父元素几乎不会违背子元素对自身大小的期望。因此你可以认为在 Android 中子元素的大小可以由子元素自己决定 。子元素想要什么大小,给自身的 layout_width 和 layout_height 指定不同的值即可。
而在 Flutter 中,就完全不一样了。Flutter 积极组合的设计,希望一切布局都尽量通过组合的方式来实现。组合优于继承这是真理,但 Flutter 却走向了极端。连指定子元素的大小、padding、margin、translate、background 等等都需要通过嵌套来实现,这就是导致嵌套地狱的根源。
以指定子元素的大小为例,你需要给子元素套一层 SizedBox,通过它来限定子元素的大小。基于此,你可以理解为在一般情况下, Flutter 中子元素的大小无法由子元素自己决定,始终由其父元素决定 ,这和 Android 有本质的区别。
Flutter 也没有提供 match_parent 来让子元素撑满父元素, wrap_content 来让子元素内容有多大就撑多大。但是它提供了 double.infinity。我们一般使用它来让子元素撑满父元素。但其实 double.infinity 也用来表示 wrap_content 。这得从 Flutter 的布局过程说起。
Flutter 的布局过程总结起来就三句话:
这里有一个很重要的点是, 子元素的大小永远不能违背父元素的约束 ,否则就会抛异常。子元素为自身的 size 赋值时,会触发以下检测:
为了避免这个异常,子元素在设置自身 size 之前,会先尝试满足父元素的约束:
这样就能保证设置的 size 永远满足父元素的约束,不引发异常。
当你使用 double.infinity 尝试撑满父元素时,这里的 contentWidth 值为 double.infinity,但 constraints.constrainWidth 会返回父元素的宽度,这就起到了 match_parent 的作用。这里的隐含意思是子元素的大小不可能真的为无限大,因为没有任何一个 UI 框架能渲染无限大的东西,因为那意味着无限大的资源消耗。
wrap_content 又是咋回事呢?其实也是同样的道理。
当父元素对子元素没有大小要求时,会向子元素传递宽松约束,即只限制最大的大小,一般为父元素的大小。意思是子元素想要多大就给多大,但不能超过自身大小。但有些 Widget 就没有这个限制,它们允许子元素的大小超过自己的大小,因此它们给子元素传递的最大大小为 double.infinity。但子元素测量自己时,发现父元素给的最大大小为 double.infinity,不可能真的将自身大小设置为 double.infinity,因为 Flutter 没法渲染无限大的东西。如果你尝试着这么做,同样会抛异常。所以一般情况下,子元素会直接将 double.infinity 当作 wrap_content 处理,向父元素反馈自己的内容大小。
所以总结下来就是:
当然一切的原因都在于 Flutter 设计之初没有考虑像 Android 那样用 -1 来表示 match_parent ,-2 来表示 wrap_content 。如果这么做的话,就没有 double.infinity 什么事了。
多数时候你都可以使用 double.infinity 来达到 match_parent 的效果,但也不总是这样。你永远如法用它来自下而上的达到 wrap_content 的效果。因为它只能自上而下。真想让子元素自身大小为 wrap_content ,那就结合支持 wrap_content 的 Widget 比如 Center 使用吧。或者使用 Flutter ConstraintLayout 。它自带了 matchParent 、 wrapContent 、 fixedSize 、 matchConstraint 的支持。
所有动画都由同一个 AnimationController 驱动,无论动画需要持续多长时间,控制器的值必须在0.0到1.0之间,而每个动画的间隔(Interval)也必须介于0.0和1.0之间。对于在间隔中设置动画的每个属性,需要分别创建一个 Tween 用于指定该属性的开始值和结束值。也就是说0.0到1.0代表整个动画过程,我们可以给不同动画指定不同的起始点和终止点来决定它们的开始时间和终止时间。
下面我们看一个例子,实现一个柱状图增长的动画:
开始时高度从0增长到300像素,同时颜色由绿色渐变为红色;这个过程占据整个动画时间的60%。
高度增长到300后,开始沿X轴向右平移100像素;这个过程占用整个动画时间的40%。
如果我们目前的项目是Android的,但是接下来我们希望部分页面可以使用Flutter进行开发,甚至我们希望在Native页面中嵌入FlutterUI组件,那么我们该如何实现呢?
假设你现在Android项目的目录的结构是这样的
这时候如果你想创建一个Flutter模块,使得Android模块和Flutter模块之间可以进行交互,我们可以通过Android Studio新建一个Flutter Module,具体过程是:File — New — New Module ,之后选择Flutter Module,指定Project Location的路径为
也就是说,最终你的项目结构会是这样的
接下来在Android Module的 build.gradle 文件中添加flutter依赖
先创建一个Flutter页面
这里比较重要的是 window.defaultRouteName 这个字段,这个字段可以接收从Native传递过来的参数 (下文我们会介绍原生传递参数的方法),也就是说通过这个字段我们就可以进行Flutter页面的路由的分发
我们可以直接在Android的 MainActivity 中启动一个 FlutterActivity ,这里的 initialRoute 方法中传递的参数就对应Flutter层的 window.defaultRouteName
注意:需要在 AndroidManifest.xml 注册 FlutterActivity
自己创建一个 FlutterAppActivity 继承自 FlutterActivity
在 MainActivity 中启动 FlutterAppActivity (另外别忘了在 AndroidManifest.xml 中注册 FlutterAppActivity )
两种启动方式的区别
如果单纯只是想打开一个Flutter页面,两种方式实际上基本没有太大区别,第一种方式也许还会更简单一点。但是,在Flutter开发中,我们往往还需要开发一些Native插件供Flutter调用,如果使用复写 FlutterActivity 的方式更有利于我们在 FlutterActivity 中注册我们的Native插件,所以实际开发中一般推荐使用第二种方式
扩展思考
initialRoute 从名称上看起来是Flutter提供给我们进行Native与Flutter交互的路由跳转的,但是实际上他就是一个字符串,我们不仅仅可以传递一个路由名称,有时候我们也可以通过这个参数传递一串JSON数据,然后在Flutter端进行解析,这样我们就可以通过这个参数做更多的事情
activity_main.xml
FrameLayout 用于承载Flutter组件
MainActivity.java
使用 FragmentManager 将 FlutterFragment 添加到 FrameLayout 容器中
运行结果
上半部分是原生的TextView,下半部分是Flutter的Text组件
本节主要介绍了Native和Flutter之间的页面跳转,以及同一个页面中Native与Flutter组件的组合。接下来会介绍如何编写Android插件与Flutter进行数据交互