• 给 Web 开发者的 Flutter 指南
    • 执行基础布局操作
      • 文本样式与对齐
      • 设置背景颜色
      • 居中元素
      • 设置容器宽度
    • 操控位置与大小
      • 设置绝对位置
      • 旋转元素
      • 缩放元素
      • 应用线性变换
        • 垂直变换
        • 水平变换
    • 操控图形
      • 圆角
      • 添加盒阴影 (box shadows)
      • 生成圆与椭圆
    • 操控文本
      • 文字间距调整
      • 内联样式更改
      • 生成文本摘要

    给 Web 开发者的 Flutter 指南

    本文是为那些熟悉用 HTML 与 CSS 语法来管理应用页面中元素的开发者准备的。本文会将 HTML/CSS 代码片段替换为等价的 Flutter/Dart 代码。

    这些示例包含如下假设:

    • HTML 文件以 <!DOCTYPE html> 开头,且为了与 Flutter 模型保持一致,所有 HTML 元素的 CSS 盒模型被设置为 border-box
    1. {
    2. box-sizing: border-box;
    3. }
    • 在 Flutter 中,为了保持语法简洁,”Lorem ipsum” 文本的默认样式由如下 bold24Roboto 变量定义:
    1. TextStyle bold24Roboto = TextStyle(
    2. color: Colors.white,
    3. fontSize: 24,
    4. fontWeight: FontWeight.w900,
    5. );

    React-style 或 声明式 编程与传统的命令式风格有何不同?为了对比,请查阅 声明式 UI 介绍。

    执行基础布局操作

    以下示例将向你展示如何执行最常见的 UI 布局操作。

    文本样式与对齐

    CSS 所处理的字体样式、大小以及其他文本属性,都是一个 Text widget 子元素 TextStyle 中单独的属性。

    在 HTML 和 Flutter 中,子元素或者 widget 都默认锚定在左上方。

    1. <div class="greybox">
    2. Lorem ipsum
    3. </div>
    4.  
    5. .greybox {
    6. background-color: #e0e0e0; /* grey 300 */
    7. width: 320px;
    8. height: 240px;
    9. font: 900 24px Georgia;
    10. }
    1. var container = Container( // grey box
    2. child: Text(
    3. "Lorem ipsum",
    4. style: TextStyle(
    5. fontSize: 24,
    6. fontWeight: FontWeight.w900,
    7. fontFamily: "Georgia",
    8. ),
    9. ),
    10. width: 320,
    11. height: 240,
    12. color: Colors.grey[300],
    13. );

    设置背景颜色

    在 Flutter 中,你可以通过 Container的 decoration 属性来设置背景颜色。

    CSS 示例使用十六进制颜色,这等价于材质调色板。

    1. <div class="greybox">
    2. Lorem ipsum
    3. </div>
    4.  
    5. .greybox {
    6. background-color: #e0e0e0; /* grey 300 */
    7. width: 320px;
    8. height: 240px;
    9. font: 900 24px Roboto;
    10. }
    1. var container = Container( // grey box
    2. child: Text(
    3. "Lorem ipsum",
    4. style: bold24Roboto,
    5. ),
    6. width: 320,
    7. height: 240,
    8. decoration: BoxDecoration(
    9. color: Colors.grey[300],
    10. ),
    11. );

    居中元素

    一个 Center widget 可以将它的子元素水平和垂直居中。

    要用 CSS 实现相似的效果,父元素需要使用一个 flex 或者 table-cell 显示布局。本节示例使用的是 flex 布局。

    1. <div class="greybox">
    2. Lorem ipsum
    3. </div>
    4.  
    5. .greybox {
    6. background-color: #e0e0e0; /* grey 300 */
    7. width: 320px;
    8. height: 240px;
    9. font: 900 24px Roboto;
    10. display: flex;
    11. align-items: center;
    12. justify-content: center;
    13. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Text(
    4. "Lorem ipsum",
    5. style: bold24Roboto,
    6. ),
    7. ),
    8. width: 320,
    9. height: 240,
    10. color: Colors.grey[300],
    11. );

    设置容器宽度

    要指定一个 Containerwidget 的宽度,请使用它的 width 属性。和 CSS 中的 max-width 属性用于指定容器可调整的宽度最大值不同的是,这里指定的是一个固定宽度。要在 Flutter 中模拟该效果,可以使用 Container 的 constraints 属性。新建一个带有 minWidthmaxWidth 属性的BoxConstraints widget。

    对嵌套的 Container 来说,如果其父元素宽度小于子元素宽度,则子元素会调整尺寸以匹配父元素大小。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. width: 100%;
    21. max-width: 240px;
    22. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. ),
    8. decoration: BoxDecoration(
    9. color: Colors.red[400],
    10. ),
    11. padding: EdgeInsets.all(16),
    12. width: 240, //max-width is 240
    13. ),
    14. ),
    15. width: 320,
    16. height: 240,
    17. color: Colors.grey[300],
    18. );

    操控位置与大小

    以下示例将展示如何对 widget 的位置、大小以及背景进行更复杂的操作。

    设置绝对位置

    默认情况下, widget 相对于其父元素定位。

    要通过 x-y 坐标指定一个 widget 的绝对位置,把它嵌套在一个 Positionedwidget 中,而该 widget 则需被嵌套在一个Stack widget 中。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. position: relative;
    13. }
    14. .redbox {
    15. background-color: #ef5350; /* red 400 */
    16. padding: 16px;
    17. color: #ffffff;
    18. position: absolute;
    19. top: 24px;
    20. left: 24px;
    21. }
    1. var container = Container( // grey box
    2. child: Stack(
    3. children: [
    4. Positioned( // red box
    5. child: Container(
    6. child: Text(
    7. "Lorem ipsum",
    8. style: bold24Roboto,
    9. ),
    10. decoration: BoxDecoration(
    11. color: Colors.red[400],
    12. ),
    13. padding: EdgeInsets.all(16),
    14. ),
    15. left: 24,
    16. top: 24,
    17. ),
    18. ],
    19. ),
    20. width: 320,
    21. height: 240,
    22. color: Colors.grey[300],
    23. );

    旋转元素

    要旋转一个 widget,请将它嵌套在Transformwidget 中。使用 Transform widget 的 alignmentorigin属性分别来指定转换原点(支点)的相对和绝对位置信息。

    对于简单的 2D 旋转,widget 是依据弧度在 Z 轴上旋转的。(角度 × π / 180)

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. transform: rotate(15deg);
    21. }
    1. var container = Container( // gray box
    2. child: Center(
    3. child: Transform(
    4. child: Container( // red box
    5. child: Text(
    6. "Lorem ipsum",
    7. style: bold24Roboto,
    8. textAlign: TextAlign.center,
    9. ),
    10. decoration: BoxDecoration(
    11. color: Colors.red[400],
    12. ),
    13. padding: EdgeInsets.all(16),
    14. ),
    15. alignment: Alignment.center,
    16. transform: Matrix4.identity()
    17. ..rotateZ(15 * 3.1415927 / 180),
    18. ),
    19. ),
    20. width: 320,
    21. height: 240,
    22. color: Colors.grey[300],
    23. );

    缩放元素

    要缩放或放大一个 widget,请将它嵌套在一个Transformwidget 中。使用 Transform widget 的 alignmentorigin 属性分别来指定缩放原点(支点)的相对和绝对信息。

    对于沿 x 轴的简单缩放操作,新建一个 Matrix4 标识对象并用它的 scale() 方法来指定缩放因系数。

    当你缩放一个父 widget 时,它的子 widget 也会相应被缩放。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. transform: scale(1.5);
    21. }
    1. var container = Container( // gray box
    2. child: Center(
    3. child: Transform(
    4. child: Container( // red box
    5. child: Text(
    6. "Lorem ipsum",
    7. style: bold24Roboto,
    8. textAlign: TextAlign.center,
    9. ),
    10. decoration: BoxDecoration(
    11. color: Colors.red[400],
    12. ),
    13. padding: EdgeInsets.all(16),
    14. ),
    15. alignment: Alignment.center,
    16. transform: Matrix4.identity()
    17. ..scale(1.5),
    18. ),
    19. width: 320,
    20. height: 240,
    21. color: Colors.grey[300],
    22. );

    应用线性变换

    要将线性变换应用在 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°)。

    垂直变换

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. padding: 16px;
    18. color: #ffffff;
    19. background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%);
    20. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. ),
    8. decoration: BoxDecoration(
    9. gradient: LinearGradient(
    10. begin: const Alignment(0.0, -1.0),
    11. end: const Alignment(0.0, 0.6),
    12. colors: <Color>[
    13. const Color(0xffef5350),
    14. const Color(0x00ef5350)
    15. ],
    16. ),
    17. ),
    18. padding: EdgeInsets.all(16),
    19. ),
    20. ),
    21. width: 320,
    22. height: 240,
    23. color: Colors.grey[300],
    24. );

    水平变换

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. padding: 16px;
    18. color: #ffffff;
    19. background: linear-gradient(90deg, #ef5350, rgba(0, 0, 0, 0) 80%);
    20. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. ),
    8. decoration: BoxDecoration(
    9. gradient: LinearGradient(
    10. begin: const Alignment(-1.0, 0.0),
    11. end: const Alignment(0.6, 0.0),
    12. colors: <Color>[
    13. const Color(0xffef5350),
    14. const Color(0x00ef5350)
    15. ],
    16. ),
    17. ),
    18. padding: EdgeInsets.all(16),
    19. ),
    20. ),
    21. width: 320,
    22. height: 240,
    23. color: Colors.grey[300],
    24. );

    操控图形

    以下示例将展示如何新建和自定义图形。

    圆角

    在矩形上实现圆角,请用BoxDecoration对象的 borderRadius 属性。新建一个BorderRadius对象来指定每个圆角的半径大小。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* gray 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. border-radius: 8px;
    21. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red circle
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. ),
    8. decoration: BoxDecoration(
    9. color: Colors.red[400],
    10. borderRadius: BorderRadius.all(
    11. const Radius.circular(8),
    12. ),
    13. ),
    14. padding: EdgeInsets.all(16),
    15. ),
    16. ),
    17. width: 320,
    18. height: 240,
    19. color: Colors.grey[300],
    20. );

    添加盒阴影 (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 共同用于设置阴影深度、颜色等等。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8),
    21. 0 6px 20px rgba(0, 0, 0, 0.5);
    22. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. ),
    8. decoration: BoxDecoration(
    9. color: Colors.red[400],
    10. boxShadow: [
    11. BoxShadow (
    12. color: const Color(0xcc000000),
    13. offset: Offset(0, 2),
    14. blurRadius: 4,
    15. ),
    16. BoxShadow (
    17. color: const Color(0x80000000),
    18. offset: Offset(0, 6),
    19. blurRadius: 20,
    20. ),
    21. ],
    22. ),
    23. padding: EdgeInsets.all(16),
    24. ),
    25. ),
    26. width: 320,
    27. height: 240,
    28. decoration: BoxDecoration(
    29. color: Colors.grey[300],
    30. ),
    31. margin: EdgeInsets.only(bottom: 16),
    32. );

    生成圆与椭圆

    尽管 CSS 中有 基础图形,用 CSS 生成圆可以用一个变通方案,即将矩形的四边 border-radius 均设成50%。

    虽然 BoxDecoration的 borderRadius 属性支持这样设置,Flutter 为BoxShape enum提供一个 shape 属性用于实现同样的目的。

    1. <div class="greybox">
    2. <div class="redcircle">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* gray 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redcircle {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. text-align: center;
    21. width: 160px;
    22. height: 160px;
    23. border-radius: 50%;
    24. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red circle
    4. child: Text(
    5. "Lorem ipsum",
    6. style: bold24Roboto,
    7. textAlign: TextAlign.center,
    8. ),
    9. decoration: BoxDecoration(
    10. color: Colors.red[400],
    11. shape: BoxShape.circle,
    12. ),
    13. padding: EdgeInsets.all(16),
    14. width: 160,
    15. height: 160,
    16. ),
    17. ),
    18. width: 320,
    19. height: 240,
    20. color: Colors.grey[300],
    21. );

    操控文本

    以下示例展示了如何设置字体和其他文本属性。他们同时还展示了如何变换文本字符、自定义间距以及生成摘要。

    文字间距调整

    在 CSS 中你可以通过分别给 letter-spacing 和 word-spacing属性的长度赋值来指定每个字母以及每个单词间的空白距离。距离的单位可以是 px, pt, cm, em 等等。

    在 Flutter 中,你可以在 Text widget 子元素TextStyle的 letterSpacingwordSpacing 属性中将间距设置为逻辑像素(允许负值)。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. letter-spacing: 4px;
    21. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum",
    6. style: TextStyle(
    7. color: Colors.white,
    8. fontSize: 24,
    9. fontWeight: FontWeight.w900,
    10. letterSpacing: 4,
    11. ),
    12. ),
    13. decoration: BoxDecoration(
    14. color: Colors.red[400],
    15. ),
    16. padding: EdgeInsets.all(16),
    17. ),
    18. ),
    19. width: 320,
    20. height: 240,
    21. color: Colors.grey[300],
    22. );

    内联样式更改

    一个 Text widget允许你展示同一类样式的文本。为了展现具有多种样式(本例中,是一个带重音的单词)的文本,需要改用 RichText widget。它的 text 属性可以指定一个或多个可以单独设置样式的TextSpan widget。

    在接下来的示例中,”Lorem” 位于 TextSpan widget 中,具有默认(继承)文本样式,”ipsum” 位于具有自定义样式、单独的一个 TextSpan 中。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem <em>ipsum</em>
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. }
    21. .redbox em {
    22. font: 300 48px Roboto;
    23. font-style: italic;
    24. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: RichText(
    5. text: TextSpan(
    6. style: bold24Roboto,
    7. children: <TextSpan>[
    8. TextSpan(text: "Lorem "),
    9. TextSpan(
    10. text: "ipsum",
    11. style: TextStyle(
    12. fontWeight: FontWeight.w300,
    13. fontStyle: FontStyle.italic,
    14. fontSize: 48,
    15. ),
    16. ),
    17. ],
    18. ),
    19. ),
    20. decoration: BoxDecoration(
    21. color: Colors.red[400],
    22. ),
    23. padding: EdgeInsets.all(16),
    24. ),
    25. ),
    26. width: 320,
    27. height: 240,
    28. color: Colors.grey[300],
    29. );

    生成文本摘要

    一个摘要会展示一个段落中文本的初始行内容,并常用省略号处理溢出的文本内容。在 HTML/CSS 中,摘录不能超过一行。在多行之后进行截断需要运行一些 JavaScript 代码。

    在 Flutter 中,使用 Textwidget 的 maxLines 属性来指定包含在摘要中的行数,以及 overflow 属性来处理溢出文本。

    1. <div class="greybox">
    2. <div class="redbox">
    3. Lorem ipsum dolor sit amet, consec etur
    4. </div>
    5. </div>
    6.  
    7. .greybox {
    8. background-color: #e0e0e0; /* grey 300 */
    9. width: 320px;
    10. height: 240px;
    11. font: 900 24px Roboto;
    12. display: flex;
    13. align-items: center;
    14. justify-content: center;
    15. }
    16. .redbox {
    17. background-color: #ef5350; /* red 400 */
    18. padding: 16px;
    19. color: #ffffff;
    20. overflow: hidden;
    21. text-overflow: ellipsis;
    22. white-space: nowrap;
    23. }
    1. var container = Container( // grey box
    2. child: Center(
    3. child: Container( // red box
    4. child: Text(
    5. "Lorem ipsum dolor sit amet, consec etur",
    6. style: bold24Roboto,
    7. overflow: TextOverflow.ellipsis,
    8. maxLines: 1,
    9. ),
    10. decoration: BoxDecoration(
    11. color: Colors.red[400],
    12. ),
    13. padding: EdgeInsets.all(16),
    14. ),
    15. ),
    16. width: 320,
    17. height: 240,
    18. color: Colors.grey[300],
    19. );