Flutter布局方式(2)-Stack和Position

1.Stack

Stack 這個是Flutter中布局用到的組件,可以疊加的現(xiàn)實(shí)View.

Stack({
  Key key,
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.overflow = Overflow.clip,
  List<Widget> children = const <Widget>[],
})
  • alignment : 指的是子Widget的對其方式,默認(rèn)情況是以左上角為開始點(diǎn) ,這個屬性是最難理解的,它區(qū)分為使用了Positioned和未使用Positioned定義兩種情況,沒有使用Positioned情況還是比較好理解的.
  • fit :用來決定沒有Positioned方式時候子Widget的大小,StackFit.loose 指的是子Widget 多大就多大,StackFit.expand使子Widget的大小和父組件一樣大.
  • overflow :指子Widget 超出Stack時候如何顯示,默認(rèn)值是Overflow.clip,子Widget超出Stack會被截?cái)?,Overflow.visible超出部分還會顯示的.

stack組件的使用

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(title: Text('title'),),
        body: Stack(
          children: <Widget>[
            Container(width: 300,height: 300,color: Colors.cyan,),
            Container(width: 200,height: 200,color: Colors.red,),
            Container(width: 100,height: 100,color: Colors.yellow,),
          ],
        ),
      ),
    );
  }
}
運(yùn)行結(jié)果.png

fit 屬性使用

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(
          title: Text("stack title"),
          actions: <Widget>[
            RaisedButton(
              onPressed: () {

              },
              color: Colors.blue,
              child: Icon(Icons.add),
            ),
          ],
        ),
        body: Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Container(
              width: 100,
              height: 100,
              color: Colors.red,
            ),
            Container(
              width: 90,
              height: 90,
              color: Colors.blue,
            ),
            Container(
              width: 80,
              height: 80,
              color: Colors.green,
            ),
          ],
        ),
      ),
    );
  }
}

如果指定是StackFit.expand,所以的子組件會和Stack一樣大的.

StackFit.expand

如果指定是StackFit.loose,所以子Widget 多大就多大.

StackFit.loose

2. Position

這個使用控制Widget的位置,通過他可以隨意擺放一個組件,有點(diǎn)像絕對布局.

Positioned({
  Key key,
  this.left,
  this.top,
  this.right,
  this.bottom,
  this.width,
  this.height,
  @required Widget child,
})

left、top 、right、 bottom分別代表離Stack左、上、右、底四邊的距離

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(
          title: Text("Postion Title"),
        ),
        body: Stack(
          children: <Widget>[
            Positioned(
              top: 100.0,
              child: Container(
                color: Colors.blue,
                child: Text("第一個組件"),
              ),
            ),
            Positioned(
              top: 200,
              right: 100,
              child: Container(
                color: Colors.yellow,
                child: Text("第二個組件"),
              ),
            ),
            Positioned(
              left: 100.0,
              child: Container(
                color: Colors.red,
                child: Text("第三個組件"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Position

這個地方有注意地方,例如說第一個組件我指定距離左邊0個距離,距離右邊0個距離,那么這個組件的寬度就是屏幕這么寬,因?yàn)槟阒付ǖ淖笥议g距都是0,也就是沒有間距.
如果指定了left&&right&&top&bottom都是0的情況,那么這個組件就是和Stack大小一樣填滿它.

3. GridTile

GridTile 是一個Flutter 提供的組件的,用來在GridView中給Item 增加更豐富的展示用的,GridTile 的布局方式就是Stack,在源代碼中就到Positioned 來進(jìn)行位置控制,主要提供三個Widget的展示分別為child、header、footer,我們看一下源代碼:

class GridTile extends StatelessWidget {
  /// Creates a grid tile.
  ///
  /// Must have a child. Does not typically have both a header and a footer.
  const GridTile({
    Key key,
    this.header,
    this.footer,
    @required this.child,
  }) : assert(child != null),
       super(key: key);

  /// The widget to show over the top of this grid tile.
  ///
  /// Typically a [GridTileBar].
  final Widget header;

  /// The widget to show over the bottom of this grid tile.
  ///
  /// Typically a [GridTileBar].
  final Widget footer;

  /// The widget that fills the tile.
  ///
  /// {@macro flutter.widgets.child}
  final Widget child;

  @override
  Widget build(BuildContext context) {
    if (header == null && footer == null)
      return child;

    final List<Widget> children = <Widget>[
      Positioned.fill(
        child: child,
      ),
    ];
    if (header != null) {
      children.add(Positioned(
        top: 0.0,
        left: 0.0,
        right: 0.0,
        child: header,
      ));
    }
    if (footer != null) {
      children.add(Positioned(
        left: 0.0,
        bottom: 0.0,
        right: 0.0,
        child: footer,
      ));
    }
    return Stack(children: children);
  }
}

4. alignment

A.沒有使用postioned定位的情況:

在第一個stack組件的代碼中使用的就是positioned定位的情況,默認(rèn)的子組件的對齊方式就是以左上角為基準(zhǔn)開始排列的,我們再來了解一下其他的對齊方式.

①.AlignmentDirectional.bottomEnd

AlignmentDirectional.bottomEnd的方式是從右下角為基準(zhǔn)開始對齊:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(
          title: Text("Postion Title"),
        ),
        body: Stack(
//        fit: StackFit.expand,
          alignment: AlignmentDirectional.bottomEnd,
          children: <Widget>[
          Container(
            width: 100,
            height: 100,
            color: Colors.red,
          ),
          Container(
            width: 90,
            height: 90,
            color: Colors.blue,
          ),
          Container(
            width: 80,
            height: 80,
            color: Colors.green,
          ),
        ]),
      ),
    );
  }
}
AlignmentDirectional.bottomEnd

②.AlignmentDirectional.topEnd

AlignmentDirectional.bottomEnd的方式是從左上角為基準(zhǔn)開始對齊:

AlignmentDirectional.topEnd

③.AlignmentDirectional.center

AlignmentDirectional.center的方式是以stack的中心位置:

AlignmentDirectional.center

④.AlignmentDirectional.centerEnd

所有的Widget 在Stack的中心位置并且右邊跟stack右邊挨著

AlignmentDirectional.centerEnd

⑤.AlignmentDirectional.centerStart

所有的Widget 在Stack的中心位置并且左邊跟stack左邊挨著

AlignmentDirectional.centerStart

B.使用postioned定位的情況:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(
          title: Text("Postion Title"),
        ),
        body: Stack(
//        alignment: AlignmentDirectional.bottomEnd,
          overflow: Overflow.visible,
          children: <Widget>[
            Positioned.fill(
              child: Container(
                color: Colors.black45,
              ),
            ),
            Positioned(
              top: 100.0,
              left: 0,
              right: 20,
              child: Container(
                color: Colors.blue,
                child: Text("第一個組件"),
              ),
            ),
            Positioned(
              top: 200,
              bottom: 20,
              child: Container(
                color: Colors.yellow,
                child: Text("第二個組件"),
              ),
            ),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: Container(
                color: Colors.red,
                child: Text("第三個組件"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
使用postioned定位的情況

這種情況是alignment 是默認(rèn)值的效果,下面我們修改一下alignment的對應(yīng)的值.

①.AlignmentDirectional.bottomEnd

bottomEnd是子Widget的底部和Stack底部對齊,并且子Widget的右邊和Stack右邊對齊.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'title',
      home: Scaffold(
        appBar: AppBar(
          title: Text("Postion Title"),
        ),
        body: Stack(
        alignment: AlignmentDirectional.bottomEnd,
          overflow: Overflow.visible,
          children: <Widget>[
            Positioned.fill(
              child: Container(
                color: Colors.black45,
              ),
            ),
            Positioned(
              top: 100.0,
              left: 0,
              right: 20,
              child: Container(
                color: Colors.blue,
                child: Text("第一個組件"),
              ),
            ),
            Positioned(
              top: 200,
              bottom: 20,
              child: Container(
                color: Colors.yellow,
                child: Text("第二個組件"),
              ),
            ),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: Container(
                color: Colors.red,
                child: Text("第三個組件"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
AlignmentDirectional.bottomEnd

會發(fā)現(xiàn)這個圖的效果和上一個圖的效果唯一區(qū)別就是黃色的第二個組件的位置有變化,這是為什么呢?
先說第一個組件和第三組件的位置為什么沒有改變:

Positioned(
   top: 100.0,
   left: 0,
   right: 20,
   child: Container(
   color: Colors.blue,
   child: Text("第一個組件"),
   ),
),

Positioned(
   bottom: 0,
   left: 0,
   right: 0,
   child: Container(
   color: Colors.red,
   child: Text("第三個組件"),
   ),
),

第一個組件top是100,說明這個組件距離頂部的距離是固定的,雖然Stack的aligment=AlignmentDirectional.bottomEnd,是不生效的,當(dāng)這兩個屬性沖突時,以Positioned的距離為主,為什么第一組件右邊也沒有Stack的右邊對齊呢?因?yàn)閞ight=20,第一個組件右邊距離已經(jīng)可以確認(rèn)了,所以也不受到aligment=AlignmentDirectional.bottomEnd的影響.

第三個組件也是一樣的,第三個組件的寬度是Stack的寬度,高度取決于Text組件的高度,最關(guān)鍵的是它的bottom=0,也就是第三個組件要和Stack組件的低邊界對齊,所以它的效果和上面的圖是沒有變化的.

Positioned(
  top: 200,
  bottom: 20,
  child: Container(
    color: Colors.yellow,
    child: Text("第二個組件"),
  ),
),

第二個組件為什么會跑到右邊呢?

因?yàn)榈诙€組件的高度是可以確認(rèn)出來的,top=200,bottom=20,設(shè)置這兩個屬性就能推斷出第二組的高度是多大,但是第二個組件的寬度取決于Text("第二個組件") 的寬度,顯然是水平方向上是不能填滿Stack的,這個時候AlignmentDirectional.bottomEnd屬性起作用了,bottom的距離已經(jīng)確定了,所以底部的對齊方式是不會變化了,但是第二組件右邊的對齊方式是可以收到AlignmentDirectional.bottomEnd影響的,所以第二組件展示的位置就是圖片上展示的位置.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容