[譯] Flutter 佈局備忘錄

你須要瞭解 Flutter 的簡單佈局模版嗎? 如今我將展現給你我總結的一系列 Flutter 佈局代碼片斷。我會盡可能保證代碼簡短易懂,而且會給出效果圖。 可是咱們仍舊須要按部就班 —— 模版目錄將會隨之逐步深刻。我將會將更多的篇幅集中於 Flutter 部件的應用,而不是單純陳列組件(Flutter Gallery 在這一點作的很好!) 若是你對於 Flutter 佈局還有其餘疑問,或者想要分享你的代碼,請留言給我!css


目錄

  • Row 和 Column
  • IntrinsicWidth 和 IntrinsicHeight
  • Stack
  • Expanded
  • ConstrainedBox
  • Container
    • 裝飾(decoration):BoxDecoration
    • 圖片(image):DecorationImage
    • 邊框(border):Border
    • 邊框半徑(borderRadius):BorderRadius
    • 形狀(shape):BoxShape
    • 陰影(boxShadow):List<BoxShadow>
    • 漸變(gradient):RadialGradient
    • 背景混合模式(backgroundBlendMode):BlendMode
  • SizedBox
  • SafeArea

Row 和 Column

MainAxisAlignment

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

若是你想要不一樣字符的基線對齊,你應該使用 CrossAxisAlignment.baseline前端

Row(
  crossAxisAlignment: CrossAxisAlignment.baseline,
  textBaseline: TextBaseline.alphabetic,
  children: <Widget>[
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.display3,
    ),
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.body1,
    ),
  ],
),
複製代碼

CrossAxisAlignment

Row /*或 Column*/( 
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  crossAxisAlignment: CrossAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

MainAxisSize

Row /*或 Column*/( 
  mainAxisSize: MainAxisSize.max,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

Row /*或 Column*/( 
  mainAxisSize: MainAxisSize.min,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

IntrinsicWidth 和 IntrinsicHeight

想要某行或列中全部部件和最高/最寬的部件同樣高/寬?不要亂找了,答案在這裏!android

當你有這種樣式的佈局:ios

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {},
            child: Text('Short'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('A bit Longer'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('The Longest text button'),
          ),
        ],
      ),
    ),
  );
}
複製代碼

可是你但願全部的按鈕都和最寬的按鈕等,只須要使用 IntrinsicWidthgit

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: IntrinsicWidth(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            RaisedButton(
              onPressed: () {},
              child: Text('Short'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('A bit Longer'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('The Longest text button'),
            ),
          ],
        ),
      ),
    ),
  );
}
複製代碼

若是你須要的是讓全部部件和最高的部件,能夠結合使用 IntrinsicHeightRow 部件。github


Stack

很是適用於將部件疊加在一塊兒算法

@override
Widget build(BuildContext context) {
  Widget main = Scaffold(
    appBar: AppBar(title: Text('Stack')),
  );

  return Stack(
    fit: StackFit.expand,
    children: <Widget>[
      main,
      Banner(
        message: "Top Start",
        location: BannerLocation.topStart,
      ),
      Banner(
        message: "Top End",
        location: BannerLocation.topEnd,
      ),
      Banner(
        message: "Bottom Start",
        location: BannerLocation.bottomStart,
      ),
      Banner(
        message: "Bottom End",
        location: BannerLocation.bottomEnd,
      ),
    ],
  );
}
複製代碼

若是想使用本身的部件,須要將它們放置在 Positioned 裏面後端

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Stack')),
    body: Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Material(color: Colors.yellowAccent),
        Positioned(
          top: 0,
          left: 0,
          child: Icon(Icons.star, size: 50),
        ),
        Positioned(
          top: 340,
          left: 250,
          child: Icon(Icons.call, size: 50),
        ),
      ],
    ),
  );
}
複製代碼

若是你不想去猜想 top 或 bottom 的值,你可使用 LayoutBuilder 來檢索它們數組

Widget build(BuildContext context) {
  const iconSize = 50;
  return Scaffold(
    appBar: AppBar(title: Text('Stack with LayoutBuilder')),
    body: LayoutBuilder(
      builder: (context, constraints) =>
        Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Material(color: Colors.yellowAccent),
            Positioned(
              top: 0,
              child: Icon(Icons.star, size: iconSize),
            ),
            Positioned(
              top: constraints.maxHeight - iconSize,
              left: constraints.maxWidth - iconSize,
              child: Icon(Icons.call, size: iconSize),
            ),
          ],
        ),
    ),
  );
}
複製代碼

Expanded

Expanded 能夠和 Flex\Flexbox 佈局一塊兒應用,而且很是適用於分配多元素的空間。app

Row(
  children: <Widget>[
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.red),
      ),
      flex: 3,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.green),
      ),
      flex: 2,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.blue),
      ),
      flex: 1,
    ),
  ],
),
複製代碼

ConstrainedBox

默認狀況下,大多數組件都會使用盡量小的空間:

Card(child: const Text('Hello World!'), color: Colors.yellow)
複製代碼

ConstrainedBox 讓部件可使用指望的剩餘空間。

ConstrainedBox( 
  constraints: BoxConstraints.expand(),
  child: const Card(
    child: const Text('Hello World!'), 
    color: Colors.yellow,
  ), 
),
複製代碼

你可使用 BoxConstraints 指定部件可使用多大的空間 —— 經過指定 height/widthmin/max 屬性。

BoxConstraints.expand 將會讓組件使用無限制(全部可用)的空間,除非另有指定:

ConstrainedBox(
  constraints: BoxConstraints.expand(height: 300),
  child: const Card(
    child: const Text('Hello World!'), 
    color: Colors.yellow,
  ),
),
複製代碼

上面代碼和以下代碼等效:

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity,
    maxWidth: double.infinity,
    minHeight: 300,
    maxHeight: 300,
  ),
  child: const Card(
    child: const Text('Hello World!'), 
    color: Colors.yellow,
  ),
),
複製代碼

Container

最經常使用的部件之一 —— 而且它之因此這麼經常使用是有緣由的:

用於佈局工具的 Container

若是你沒有指定 Containerheightwidth,它將和 child 的大小相同

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}
複製代碼

若是你想要 Container 擴大到和它的父級元素相等,對 heightwidth 屬性使用 double.infinity

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}
複製代碼

Container 的裝飾

你可使用 color 屬性來改變 Container 的背景色,可是 decorationforegroundDecoration 則能夠作更多。(使用這兩個屬性,你能夠完全改變 Container 的外觀,這部分我將在後續討論,由於這部份內容不少) decoration 總會放置在 child 後面,而 foregroundDecoration 則在 child 的上面。

decoration

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.decoration')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      decoration: BoxDecoration(color: Colors.yellowAccent),
      child: Text("Hi"),
    ),
  );
}
複製代碼

decoration and foregroundDecoration

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.foregroundDecoration')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      decoration: BoxDecoration(color: Colors.yellowAccent),
      foregroundDecoration: BoxDecoration(color: Colors.red.withOpacity(0.5)),
      child: Text("Hi"),
    ),
  );
}
複製代碼

Container 的變換

若是你不想使用 Transform 部件來改變你的佈局,你可使用 Containertransform 屬性

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.transform')),
    body: Container(
      height: 300,
      width: 300,
      transform: Matrix4.rotationZ(pi / 4),
      decoration: BoxDecoration(color: Colors.yellowAccent),
      child: Text(
        "Hi",
        textAlign: TextAlign.center,
      ),
    ),
  );
}
複製代碼

BoxDecoration

裝飾效果一般用於容器組件,來改變組件的外觀。

圖片(image):DecorationImage

將圖片做爲背景:

Scaffold(
  appBar: AppBar(title: Text('image: DecorationImage')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        image: DecorationImage(
          fit: BoxFit.fitWidth,
          image: NetworkImage(
            'https://flutter.io/images/catalog-widget-placeholder.png',
          ),
        ),
      ),
    ),
  ),
);
複製代碼

邊框(border):Border

指定容器的邊框樣式。

Scaffold(
  appBar: AppBar(title: Text('border: Border')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        border: Border.all(color: Colors.black, width: 3),
      ),
    ),
  ),
);
複製代碼

邊框半徑(borderRadius):BorderRadius

讓邊框能夠是圓角。

若是裝飾的 shapeBoxShape.circle,那麼 borderRadius 將無效

Scaffold(
  appBar: AppBar(title: Text('borderRadius: BorderRadius')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
          color: Colors.yellow,
          border: Border.all(color: Colors.black, width: 3),
          borderRadius: BorderRadius.all(Radius.circular(18))),
    ),
  ),
);
複製代碼

形狀(shape):BoxShape

盒子的形狀能夠是長方形、正方形、橢圓或者圓形。

對於其餘任意形狀,你應該使用 ShapeDecoration 而不是 BoxDecoration

Scaffold(
  appBar: AppBar(title: Text('shape: BoxShape')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        shape: BoxShape.circle,
      ),
    ),
  ),
);
複製代碼

陰影(boxShadow):List<BoxShadow>

能夠給容器添加陰影。

這個參數是一個列表,這樣你就能夠定義多種不一樣的陰影,而後將它們組合在一塊兒。

Scaffold(
  appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        boxShadow: const [
          BoxShadow(blurRadius: 10),
        ],
      ),
    ),
  ),
);
複製代碼

漸變(gradient)

有三種類型的漸變:LinearGradientRadialGradientSweepGradient

`LinearGradient`

Scaffold(
  appBar: AppBar(title: Text('gradient: LinearGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: const [
            Colors.red,
            Colors.blue,
          ],
        ),
      ),
    ),
  ),
);
複製代碼

RadialGradient

Scaffold(
  appBar: AppBar(title: Text('gradient: RadialGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: RadialGradient(
          colors: const [Colors.yellow, Colors.blue],
          stops: const [0.4, 1.0],
        ),
      ),
    ),
  ),
);
複製代碼

SweepGradient

Scaffold(
  appBar: AppBar(title: Text('gradient: SweepGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: SweepGradient(
          colors: const [
            Colors.blue,
            Colors.green,
            Colors.yellow,
            Colors.red,
            Colors.blue,
          ],
          stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
        ),
      ),
    ),
  ),
);
複製代碼

背景混合模式(backgroundBlendMode)

backgroundBlendModeBoxDecoration 中最複雜的屬性。 它能夠混合 BoxDecoration 的顏色和漸變,而且不管 BoxDecoration 在何種元素之上。

有了 backgroundBlendMode,你可使用 BlendMode 枚舉類型中的一長串算法。

首先,配置 BoxDecorationforegroundDecoration,它被渲染於 Container 子元素的上方(而 decoration 被渲染於子元素的後面)。

Scaffold(
  appBar: AppBar(title: Text('backgroundBlendMode')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      foregroundDecoration: BoxDecoration(
        backgroundBlendMode: BlendMode.exclusion,
        gradient: LinearGradient(
          colors: const [
            Colors.red,
            Colors.blue,
          ],
        ),
      ),
      child: Image.network(
        'https://flutter.io/images/catalog-widget-placeholder.png',
      ),
    ),
  ),
);
複製代碼

backgroundBlendMode 不只影響它所在的 Container

backgroundBlendMode 能改變從 Container 的部件樹中任意部件的顏色。 下面這段代碼中,有一個做爲父級元素的 Container,它渲染了一張圖片 image 和一個使用了 backgroundBlendMode 的子元素 Container。你仍舊會獲得和前一段代碼相同的效果。

Scaffold(
  appBar: AppBar(title: Text('backgroundBlendMode')),
  body: Center(
    child: Container(
      decoration: BoxDecoration(
        image: DecorationImage(
          image: NetworkImage(
            'https://flutter.io/images/catalog-widget-placeholder.png',
          ),
        ),
      ),
      child: Container(
        height: 200,
        width: 200,
        foregroundDecoration: BoxDecoration(
          backgroundBlendMode: BlendMode.exclusion,
          gradient: LinearGradient(
            colors: const [
              Colors.red,
              Colors.blue,
            ],
          ),
        ),
      ),
    ),
  ),
);
複製代碼

SizedBox

這是最簡單可是最有用的部件

用做 ConstrainedBox 的 SizedBox

SizedBox 能夠實現和 ConstrainedBox 類似的效果

SizedBox.expand(
  child: Card(
    child: Text('Hello World!'),
    color: Colors.yellowAccent,
  ),
),
複製代碼

用做內邊距的 SizedBox

若是你須要添加內邊距或者外邊距,你能夠選擇 Padding 或者 Container 部件。可是它們都不如添加 Sizedbox 簡單易讀

Column(
  children: <Widget>[
    Icon(Icons.star, size: 50),
    const SizedBox(height: 100),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
複製代碼

用做不可見對象的 SizedBox

不少時候你但願經過一個布爾值(bool)來控制組件的顯示和隱藏

Widget build(BuildContext context) {
  bool isVisible = ...
  return Scaffold(
    appBar: AppBar(
      title: Text('isVisible = $isVisible'),
    ),
    body: isVisible 
      ? Icon(Icons.star, size: 150) 
      : const SizedBox(),
  );
}
複製代碼

因爲 SizedBox 有一個 const 構造函數,使用 const SizedBox() 就變得很是簡單。

更簡單的解決方案是使用 Opacity 部件,而後將 opacity 的值改爲 0.0。這個方案的缺點是雖然組件不可見,可是它依舊佔據空間。


SafeArea

在不一樣的平臺上,有不少特殊的位置,好比 Android 系統的狀態欄,或者 iPhone X 的「齊劉海」,咱們應該避免在這些位置放置元素。

解決方案就是使用 SafeArea 部件(下面的例子分別是使用和沒使用 SafeArea 的效果)

Widget build(BuildContext context) {
  return Material(
    color: Colors.blue,
    child: SafeArea(
      child: SizedBox.expand(
        child: Card(color: Colors.yellowAccent),
      ),
    ),
  );
}
複製代碼

更多內容敬請期待

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索