Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。Widget 描述了在当前的配置和状态下,视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
提示:在flutter中所有的UI元素都是Widget,Widget类似html中的div标签,android的view, ios中的UIView。
1. Hello world
创建一个最小的 Flutter 应用简单到仅需调用 runApp() 方法并传入一个 widget 即可:
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
runApp() 函数会持有传入的 Widget,并且使它成为 widget 树中的根节点。在这个例子中,Widget 树有两个 widgets,Center widget 及其子 widget ——Text 。框架会强制让根 widget 铺满整个屏幕,也就是说“Hello World”会在屏幕上居中显示。在这个例子我们需要指定文字的方向,当使用 MaterialApp widget 时,你需要考虑这一点,之后我们会进一步的描述。
2. 基础widget
flutter自带了很多基础widget, 例如:
- Text: Text widget 可以用来在应用内创建带样式的文本。
- Row, Column:这两个 flex widgets 可以让你在水平(Row)和垂直(Column)方向创建灵活的布局。它是基于 web 的 flexbox 布局模型设计的。
- Stack:Stack widget 不是线性(水平或垂直)定位的,而是按照绘制顺序将 widget 堆叠在一起。你可以用 Positioned widget 作为 Stack 的子 widget,以相对于 Stack 的上,右,下,左来定位它们。Stack 是基于 Web 中的绝对位置布局模型设计的。
- Container: Container widget 可以用来创建一个可见的矩形元素。Container 可以使用 BoxDecoration 来进行装饰,如背景,边框,或阴影等。Container 还可以设置外边距、内边距和尺寸的约束条件等。另外,Container 可以使用矩阵在三维空间进行转换。
除了基础的widget, Material 组件提供了更多的Material风格的UI组件,这后续的教程会有详细介绍。
做过app开发的人都知道,我们一般一个复杂的UI界面,都是由各种基础组件(widget)组合起来构成一个复杂UI界面。我们也可以将一个复杂页面自定义为一个新的widget组件,提供给其他地方使用。
在写应用的过程中,取决于是否需要管理状态,通常会创建一个新的组件继承 StatelessWidget 或 StatefulWidget。Widget 的主要工作是实现 build 方法,该方法根据其它较低级别的 widget 来描述这个 widget。框架会逐一构建这些 widget,直到最底层的描述 widget 几何形状的 RenderObject。
2. 声明式渲染
学习过Vue,react,微信小程序之类的web前端框架,对声明式渲染都不陌生,简单来说就是我们先定义一个ui组件的状态,然后框架根据这些定义好的状态,渲染UI界面。
Flutter 应用是 声明式 的,这也就意味着 Flutter 构建的用户界面就是应用的当前状态, 这点跟android和ios原生开发不大一样。
2.1. 命令式渲染
android/ios就是命令式渲染,这种风格就是,渲染UI页面,都是通过函数调用的方式设置各种组件的属性实现。
例如
// 设置ui控件的文本, 这个时候界面上对应组件的文本就改变了
t.setText("Hello world");
2.2. 声明式渲染
这种风格,我们先定义好组件有什么属性,然后根据这些属性渲染界面,如果改变属性,然后重新渲染UI界面就可以。
例子:
import 'package:flutter/material.dart';
void main() => runApp(TextDemo());
// 继承StatefulWidget,定义一个有状态组件
class TextDemo extends StatefulWidget {
// 重写createState函数,返回我们定义的widget组件
_TextDemo createState() => _TextDemo();
}
// 自定义widget组件
class _TextDemo extends State<TextDemo> {
// 定义属性
String title = "Hello world";
// build决定我们如何渲染ui
@override
Widget build(BuildContext context) {
// 返回定义的widget布局,这里是在一行中放两个widget, 一个展示文本,一个展示按钮
return Row(children: <Widget>[
Text(title), // Text组件根据title属性,展示文本
// 按钮widget
FlatButton(child: const Text("点击改变Text"), onPressed: () { // 点击事件
// setState告诉框架属性变化了,重新调用build函数渲染界面
setState(() {
// 改变属性
this.title = "标题已经改变了!";
});
},)
],);
}
}
3. 无状态Widget
无状态widget指的是,组件的状态是静态的,就是组件的属性初始化之后,就不会改变属性的值。
定义无状态Widget,通过继承StatelessWidget,重载build函数,实现我们自定义的UI布局 (通常是通过各种基础widget组件,组合出复杂的UI布局)。
例子:
import 'package:flutter/material.dart';
void main() => runApp(TextDemo());
// 定义一个简单无状态组件
class TextDemo extends StatelessWidget {
// 定义静态属性, 其实就是定义常量,这些属性不会改变
final String title = "Hello world";
@override
Widget build(BuildContext context) {
// 根据title属性,展示一个简单的Text组件,当然也可以组合复杂的组件。
return Text(title);
}
}
4. 有状态Widget
有状态widget,通常指的是一些拥有状态数据,而且状态数据会改变的widget组件。
有状态widget主要涉及两个类:
- StatefulWidget - 负责创建State
- State - 负责自定义UI视图逻辑部分,也可以包含组件私有状态数据
定义有状态widget,需要分两步;
- 继承StatefulWidget,自定义有状态组件。
- 通过自定义有状态组件的createState函数,创建State。
有状态widget的例子,请参考 2.2. 声明式渲染 中的例子。