给 Web 开发者的 Flutter 指南
本文是为那些熟悉用 HTML 与 CSS 语法来管理应用页面中元素的开发者准备的。本文会将 HTML/CSS 代码片段替换为等价的 Flutter/Dart 代码。
这些示例包含如下假设:
- HTML 文件以
<!DOCTYPE html>
开头,且为了与 Flutter 模型保持一致,所有 HTML 元素的 CSS 盒模型被设置为border-box
。
{
box-sizing: border-box;
}
- 在 Flutter 中,为了保持语法简洁,”Lorem ipsum” 文本的默认样式由如下
bold24Roboto
变量定义:
TextStyle bold24Roboto = TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w900,
);
React-style 或 声明式 编程与传统的命令式风格有何不同?为了对比,请查阅 声明式 UI 介绍。
执行基础布局操作
以下示例将向你展示如何执行最常见的 UI 布局操作。
文本样式与对齐
CSS 所处理的字体样式、大小以及其他文本属性,都是一个 Text widget 子元素 TextStyle 中单独的属性。
在 HTML 和 Flutter 中,子元素或者 widget 都默认锚定在左上方。
- <div class="greybox">
- Lorem ipsum
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Georgia;
- }
- var container = Container( // grey box
- child: Text(
- "Lorem ipsum",
- style: TextStyle(
- fontSize: 24,
- fontWeight: FontWeight.w900,
- fontFamily: "Georgia",
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
设置背景颜色
在 Flutter 中,你可以通过 Container的 decoration
属性来设置背景颜色。
CSS 示例使用十六进制颜色,这等价于材质调色板。
- <div class="greybox">
- Lorem ipsum
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- }
- var container = Container( // grey box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- width: 320,
- height: 240,
- decoration: BoxDecoration(
- color: Colors.grey[300],
- ),
- );
居中元素
一个 Center widget 可以将它的子元素水平和垂直居中。
要用 CSS 实现相似的效果,父元素需要使用一个 flex 或者 table-cell 显示布局。本节示例使用的是 flex 布局。
- <div class="greybox">
- Lorem ipsum
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- var container = Container( // grey box
- child: Center(
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
设置容器宽度
要指定一个 Containerwidget 的宽度,请使用它的 width
属性。和 CSS 中的 max-width 属性用于指定容器可调整的宽度最大值不同的是,这里指定的是一个固定宽度。要在 Flutter 中模拟该效果,可以使用 Container 的 constraints
属性。新建一个带有 minWidth
和 maxWidth
属性的BoxConstraints widget。
对嵌套的 Container 来说,如果其父元素宽度小于子元素宽度,则子元素会调整尺寸以匹配父元素大小。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- width: 100%;
- max-width: 240px;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- width: 240, //max-width is 240
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
操控位置与大小
以下示例将展示如何对 widget 的位置、大小以及背景进行更复杂的操作。
设置绝对位置
默认情况下, widget 相对于其父元素定位。
要通过 x-y 坐标指定一个 widget 的绝对位置,把它嵌套在一个 Positionedwidget 中,而该 widget 则需被嵌套在一个Stack widget 中。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- position: relative;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- position: absolute;
- top: 24px;
- left: 24px;
- }
- var container = Container( // grey box
- child: Stack(
- children: [
- Positioned( // red box
- child: Container(
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- left: 24,
- top: 24,
- ),
- ],
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
旋转元素
要旋转一个 widget,请将它嵌套在Transformwidget 中。使用 Transform widget 的 alignment
和 origin
属性分别来指定转换原点(支点)的相对和绝对位置信息。
对于简单的 2D 旋转,widget 是依据弧度在 Z 轴上旋转的。(角度 × π / 180)
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- transform: rotate(15deg);
- }
- var container = Container( // gray box
- child: Center(
- child: Transform(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- textAlign: TextAlign.center,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- alignment: Alignment.center,
- transform: Matrix4.identity()
- ..rotateZ(15 * 3.1415927 / 180),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
缩放元素
要缩放或放大一个 widget,请将它嵌套在一个Transformwidget 中。使用 Transform widget 的 alignment
和 origin
属性分别来指定缩放原点(支点)的相对和绝对信息。
对于沿 x 轴的简单缩放操作,新建一个 Matrix4 标识对象并用它的 scale() 方法来指定缩放因系数。
当你缩放一个父 widget 时,它的子 widget 也会相应被缩放。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- transform: scale(1.5);
- }
- var container = Container( // gray box
- child: Center(
- child: Transform(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- textAlign: TextAlign.center,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- alignment: Alignment.center,
- transform: Matrix4.identity()
- ..scale(1.5),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
应用线性变换
要将线性变换应用在 widget 的背景上,请将它嵌套在一个Container widget 中。然后用 Container widget 的 decoration
属性生成一个BoxDecoration 对象,然后使用 BoxDecoration 的 gradient
属性来变换背景填充内容。
变换“角度”基于 Alignment (x, y) 取值来定:
- If the beginning and ending x values are equal, the gradient is vertical(0° | 180°).
如果开始和结束的 x 值相同,变换将是垂直的(0°180°)。
- If the beginning and ending y values are equal, the gradient is horizontal(90° | 270°).
如果开始和结束的 y 值相同,变换将是水平的(90°270°)。
垂直变换
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- padding: 16px;
- color: #ffffff;
- background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%);
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: const Alignment(0.0, -1.0),
- end: const Alignment(0.0, 0.6),
- colors: <Color>[
- const Color(0xffef5350),
- const Color(0x00ef5350)
- ],
- ),
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
水平变换
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- padding: 16px;
- color: #ffffff;
- background: linear-gradient(90deg, #ef5350, rgba(0, 0, 0, 0) 80%);
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: const Alignment(-1.0, 0.0),
- end: const Alignment(0.6, 0.0),
- colors: <Color>[
- const Color(0xffef5350),
- const Color(0x00ef5350)
- ],
- ),
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
操控图形
以下示例将展示如何新建和自定义图形。
圆角
在矩形上实现圆角,请用BoxDecoration对象的 borderRadius
属性。新建一个BorderRadius对象来指定每个圆角的半径大小。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* gray 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- border-radius: 8px;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red circle
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- borderRadius: BorderRadius.all(
- const Radius.circular(8),
- ),
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
添加盒阴影 (box shadows)
在 CSS 中你可以通过 box-shadow 属性快速指定阴影偏移与模糊范围。本例展示了两个盒阴影的属性设置:
xOffset: 0px, yOffset: 2px, blur: 4px, color: black @80% alpha
xOffset: 0px, yOffset: 06x, blur: 20px, color: black @50% alpha
在 Flutter 中,每个属性与其取值都是单独指定的。请使用 BoxDecoration 的 boxShadow
属性来生成一系列BoxShadowwidget。你可以定义一个或多个BoxShadow widget,这些 widget 共同用于设置阴影深度、颜色等等。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8),
- 0 6px 20px rgba(0, 0, 0, 0.5);
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- boxShadow: [
- BoxShadow (
- color: const Color(0xcc000000),
- offset: Offset(0, 2),
- blurRadius: 4,
- ),
- BoxShadow (
- color: const Color(0x80000000),
- offset: Offset(0, 6),
- blurRadius: 20,
- ),
- ],
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- decoration: BoxDecoration(
- color: Colors.grey[300],
- ),
- margin: EdgeInsets.only(bottom: 16),
- );
生成圆与椭圆
尽管 CSS 中有 基础图形,用 CSS 生成圆可以用一个变通方案,即将矩形的四边 border-radius 均设成50%。
虽然 BoxDecoration的 borderRadius
属性支持这样设置,Flutter 为BoxShape enum提供一个 shape
属性用于实现同样的目的。
- <div class="greybox">
- <div class="redcircle">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* gray 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redcircle {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- text-align: center;
- width: 160px;
- height: 160px;
- border-radius: 50%;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red circle
- child: Text(
- "Lorem ipsum",
- style: bold24Roboto,
- textAlign: TextAlign.center,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- shape: BoxShape.circle,
- ),
- padding: EdgeInsets.all(16),
- width: 160,
- height: 160,
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
操控文本
以下示例展示了如何设置字体和其他文本属性。他们同时还展示了如何变换文本字符、自定义间距以及生成摘要。
文字间距调整
在 CSS 中你可以通过分别给 letter-spacing 和 word-spacing属性的长度赋值来指定每个字母以及每个单词间的空白距离。距离的单位可以是 px, pt, cm, em 等等。
在 Flutter 中,你可以在 Text widget 子元素TextStyle的 letterSpacing
与 wordSpacing
属性中将间距设置为逻辑像素(允许负值)。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- letter-spacing: 4px;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum",
- style: TextStyle(
- color: Colors.white,
- fontSize: 24,
- fontWeight: FontWeight.w900,
- letterSpacing: 4,
- ),
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
内联样式更改
一个 Text widget允许你展示同一类样式的文本。为了展现具有多种样式(本例中,是一个带重音的单词)的文本,需要改用 RichText widget。它的 text
属性可以指定一个或多个可以单独设置样式的TextSpan widget。
在接下来的示例中,”Lorem” 位于 TextSpan widget 中,具有默认(继承)文本样式,”ipsum” 位于具有自定义样式、单独的一个 TextSpan 中。
- <div class="greybox">
- <div class="redbox">
- Lorem <em>ipsum</em>
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- }
- .redbox em {
- font: 300 48px Roboto;
- font-style: italic;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: RichText(
- text: TextSpan(
- style: bold24Roboto,
- children: <TextSpan>[
- TextSpan(text: "Lorem "),
- TextSpan(
- text: "ipsum",
- style: TextStyle(
- fontWeight: FontWeight.w300,
- fontStyle: FontStyle.italic,
- fontSize: 48,
- ),
- ),
- ],
- ),
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );
生成文本摘要
一个摘要会展示一个段落中文本的初始行内容,并常用省略号处理溢出的文本内容。在 HTML/CSS 中,摘录不能超过一行。在多行之后进行截断需要运行一些 JavaScript 代码。
在 Flutter 中,使用 Textwidget 的 maxLines
属性来指定包含在摘要中的行数,以及 overflow
属性来处理溢出文本。
- <div class="greybox">
- <div class="redbox">
- Lorem ipsum dolor sit amet, consec etur
- </div>
- </div>
- .greybox {
- background-color: #e0e0e0; /* grey 300 */
- width: 320px;
- height: 240px;
- font: 900 24px Roboto;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .redbox {
- background-color: #ef5350; /* red 400 */
- padding: 16px;
- color: #ffffff;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- var container = Container( // grey box
- child: Center(
- child: Container( // red box
- child: Text(
- "Lorem ipsum dolor sit amet, consec etur",
- style: bold24Roboto,
- overflow: TextOverflow.ellipsis,
- maxLines: 1,
- ),
- decoration: BoxDecoration(
- color: Colors.red[400],
- ),
- padding: EdgeInsets.all(16),
- ),
- ),
- width: 320,
- height: 240,
- color: Colors.grey[300],
- );