1.1 Flutter的特点与核心概念
Flutter的特点如下所示:
❑跨平台:现在Flutter至少可以跨5种平台,甚至支持嵌入式开发。我们常用的如MacOS、Windows、Linux、Android、iOS,甚至可以在谷歌最新的操作系统Fuchsia上运行。到目前为止,Flutter算是支持平台最多的框架了,良好的跨平台性直接带来的好处就是减少开发成本。
❑丝滑般的体验:使用Flutter内置的Material Design和Cupertino风格组件、丰富的motion API、平滑而自然的滑动效果和平台感知,为用户带来全新体验。
❑响应式框架:使用Flutter的响应式框架和一系列基础组件,可以轻松构建用户界面。使用功能强大且灵活的API(针对2D、动画、手势、效果等)能解决艰难的UI挑战。
❑支持插件:通过Flutter的插件可以访问平台本地API,如相机、蓝牙、WiFi等。可借助现有的Java、Swift、Objective-C、C++代码实现对原生系统的调用。
❑60fps超高性能:Flutter采用GPU渲染技术,所以性能极高。用Flutter编写的应用可以达到60fps(每秒传输帧数),这意味着它完全可以胜任游戏的制作。官方宣称用Flutter开发的应用的性能甚至会超过原生应用。
Flutter包括一个现代的响应式框架、一个2D渲染引擎、各种组件和开发工具。这些组件有助于快速地设计、构建、测试和调试应用程序。Flutter的核心概念有:组件、构建、状态、框架等,下面分别介绍。
1.1.1 一切皆为组件
组件(Widget)是Flutter应用程序用户界面的基本构建块。不仅按钮、输入框、卡片、列表这些内容可作为Widget,甚至布局方式、动画处理都可视为Widget,所以Flutter具有一致的统一对象模型:Widget。
Widget可以定义为:
❑一个界面组件(如按钮或输入框)。
❑一个文本样式(如字体或颜色)。
❑一种布局(如填充或滚动)。
❑一种动画处理(如缓动)。
❑一种手势处理(GestureDetector)。
Widget具有丰富的属性及方法,通过属性可改变组件的状态(颜色、大小等)及回调方法的处理(单击事件回调、手势事件回调等),方法主要是提供一些组件的功能扩展。比如TextBox是一个矩形的文本组件,其属性及方法如下:
❑bottom:底部间距属性。
❑direction:文本排列方向属性。
❑left:左侧间距属性。
❑right:右侧间距属性。
❑top:上部间距属性。
❑toRect:导出矩形方法。
❑toString:转换成字符串方法。
1.1.2 组件嵌套
复杂的功能界面通常都是由一个一个功能简单的组件组装完成的。有的组件负责布局,有的负责定位,有的负责调整大小,有的负责渐变处理,等等。这种嵌套组合的方式带来的最大好处就是解耦。
例如,界面中添加了一个居中组件Center,居中组件里嵌套了一个容器组件Container,容器组件里嵌套了一个文本组件Text和一个装饰器BoxDecoration。代码如下所示:
return Center( // 添加容器 child: Container( // 添加装饰器 decoration: BoxDecoration( ), child: Text( // 添加文本组件 ), ), ),
大家如果是首次看到这段代码,会觉得嵌套层次太多、太复杂。其实不然,随着对组件的深入了解及熟练使用,写起来还是非常得心应手的。
最基础的组件类是Widget,其他所有的组件都是继承Widget的,如图1-1所示。紧接着下面有两大类组件:有状态组件及无状态组件。有状态组件是界面会发生变化的组件,如Scrollable、Animatable等,无状态的组件即界面不发生变化的组件,如Text、AssetImage等。
图1-1 类层次结构
1.1.3 构建
可以通过重写Widget的build方法来构建一个组件,如下代码所示:
@protected Widget build(BuildContext context);
构建即为创建一个Widget,返回值也是一个Widget对象,不管返回的是单个组件还是返回通过嵌套的方式组合的组件,都是Widget的实例。
在build方法里我们发现传入了一个参数context。BuildContext即构建Widget上下文,每一个BuildContext对应一个Widget。在任意一个组件的build方法中都需要传入一个BuildContext对象。
BuildContext的作用主要是通过上下文获取指定的数据,如下所示:
❑Theme.of(context):获取主题。
❑Navigator.push(context, route):压栈。
1.1.4 处理用户交互
如果Widget需要根据用户交互或其他因素进行更改,则该Widget是有状态的。例如,如果一个Widget的计数器在用户点击一个按钮时递增,那么该计数器的值就是该Widget的状态。当该值发生变化时,需要重新构建Widget以更新UI。
这些Widget将继承StatefulWidget(而不是State),并将它们的可变状态存储在State的子类中,如图1-2所示。
图1-2 有状态Widget继承示意图
每当改变一个State对象时(例如增加计数器),必须调用setState()来通知框架,框架会再次调用State的构建方法来更新用户界面。
有了独立的状态和Widget对象,其他Widget可以以同样的方式处理无状态和有状态的Widget,而不必担心丢失状态。父Widget可以自由地创造子Widget的新实例且不会失去子Widget的状态,而不是通过持有子Widget来维持其状态。框架在适当的时候完成查找和重用现有状态对象的所有工作。
1.1.5 状态
Flutter中的状态(State)和React中的状态概念一致。State是一个组件的UI数据模型,是组件渲染时的数据依据。Flutter程序的运行可以认为是一个巨大的状态机,用户的操作、请求API和系统事件的触发都是触发点,触发点通过调用setState方法推动状态机进行响应,实现不同的状态,然后渲染UI,让UI与数据保持一致。状态的生命周期如图1-3所示。
图1-3 状态的生命周期
状态的生命周期每步的作用如下所示:
❑StatefulWidget.createState :Framework会通过调用StatefulWidget.createState来创建一个State。
❑initState :新创建的State会和一个BuildContext产生关联,此时认为State已经被安装好了,initState函数将会被调用。通常,我们可以重写这个函数,进行初始化操作。
❑didChangeDependencies :在initState调用结束后,这个函数会被调用。事实上,当State对象的依赖关系发生变化时,这个函数总会被Framework调用。
❑build :经过以上步骤,系统认为一个State已经准备好了,就会调用build来构建视图。我们需要在这个函数中返回一个Widget。
❑deactivate :当State被暂时从视图树中移除时,会调用这个函数。页面切换时,也会调用它,因为此时State在视图树中的位置发生了变化,需要先暂时移除后添加。
❑dispose :当State被永久地从视图树中移除时,Framework会调用该函数。在销毁前触发,我们可以在这里进行最终的资源释放。在调用这个函数之前,总会先调用deactivate函数。
❑didUpdateWidget :当Widget的配置发生变化时,会调用这个函数。比如,热重载的时候就会调用这个函数。调用这个函数后,会调用build函数。
❑setState:当需要更新State的视图时,需要手动调用这个函数,它会触发build函数。
1.1.6 分层的框架
Flutter框架是一个分层的结构,每一层都建立在前一层之上。图1-4显示了Flutter框架,上层比下层的使用频率更高。
图1-4 Flutter框架
提示 有关构成Flutter分层框架的完整库,请参阅官方的API文档,网址为https://docs.flutter.io/。
分层设计的目标是帮助开发者用更少的代码做更多的事情。例如,Material层通常组合来自Widget层的基本Widget,而Widget层通过对象渲染层来构建。
分层结构为构建应用程序提供了许多选项。开发者可以用一种自定义的方法来释放框架的全部表现力,或使用构件层中的构建块,或混合搭配。可以使用Flutter提供的所有Widget,也可以使用Flutter团队定制的Widget。也就是说,你可以从高层次、统一的Widget概念中获得开发效率的优势,也可以深入下层施展你的才能。