Flutter入门-定位布局

135 阅读4分钟

Stack

在 Flutter 中,Stack 是一个用于叠加多个小部件的布局小部件。它允许你将多个小部件重叠放置,通过控制每个小部件的位置和大小,你可以创建出复杂的布局效果,如徽章图标、对话框、自定义形状等

Stack({
  Key key,
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.overflow = Overflow.clip,
  List<Widget> children = const <Widget>[],
})

因为Stack中的child是重叠关系,所以需要对child进行定位,根据定位的不同Stack中的child可以分为两种类型,分别是positioned和non-positioned。

所谓positioned,是指child widget被包装在Positioned中。Positioned是专门用来定位Stack中的child位置的一个widget。所以Positioned必须用在Stack中,那么这个对象就是一个Stack中的positioned对象。Positioned中除了封装的child之外,还有6个属性,如下所示:

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

这六个属性分别是left,top,right,bottom,width和height。其中left,top,right,bottom分别表示到左,顶,右,底的距离,这个距离是相对stack来说的。而width和height则表示的是Positioned的宽度和高度。

事实上,使用left和right可以定义出width,使用top和bottom可以定义出height。

如果在一个轴方向的三个值都不存在,那么会使用Stack.alignment来定位子元素。

如果六个值都不存在,那么这个child就是一个non-positioned的child。

对于non-positioned的child,是通过Stack的alignment来进行布局的

属性

  • Stack常用属性

    • children:子视图

    • alignment:子视图的对齐方式

      • topLeft:顶部左对齐
      • topCenter:顶部居中对齐
      • topRight:顶部右对齐
      • centerLeft:中间左对齐
      • center:中间对齐
      • centerRight:中间右对齐
      • bottomLeft:底部左对齐
      • bottomCenter:底部居中对齐
      • bottomRight:底部右对齐
    • clipBehavior,裁剪,可能会影响性能

      • Clip.hardEdge: Stack默认为此选项
      • Clip.antiAlias: 平滑裁剪
      • Clip.antiAliasWithSaveLayer
      • Clip.none: 不需要裁剪
    • fit:子视图填充方式

      • StackFit.loose: 使用子组件的大小
      • StackFit.expand: 充满父视图的区域
      • StackFit.passthrough: 透传,使用Stack的父视图的布局方式
    • textDirection

      • TextDirection.ltr
      • TextDirection.rtl

使用

no-positioned使用

 Widget build(BuildContext context) {
   return Stack(
     alignment: Alignment.center,
     children: [
       const CircleAvatar(
         backgroundImage: AssetImage('images/head.jpg'),
         radius: 100,
       ),
       Container(
         decoration: const BoxDecoration(
           color: Colors.green,
         ),
         child: const Text(
           '编辑',
           style: TextStyle(
             fontSize: 20,
             fontWeight: FontWeight.bold,
             color: Colors.white,
           ),
         ),
       ),
     ],
   );

image.png

positioned使用

class PositionScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return 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("第三个组件"),
            ),
          ),
        ],
      ),
    );
  }
}

image.png    这个例子的效果就是

  • 第一个组件距离顶部Stack 有100的间距
  • 第二个组件距离顶部200,距离右边100间距
  • 第三个组件距离左边100间距

IndexStack

IndexedStack是Flutter中的一个布局组件,用于在多个子组件之间切换,并且只显示当前子组件。下面是关于使用IndexedStack的详细说明:

IndexedStack(
  index: 0, // 初始显示子组件的下标
  children: [
    // 子组件列表
    Container(color: Colors.blue),
    Container(color: Colors.green),
    Container(color: Colors.red),
  ],
)

属性解析:

index:代表当前显示的子组件的下标。比如上面设置为0,就是显示第1个子组件。如果改成1,则是显示第2个子组件。 children:是一个列表,其中包含IndexedStack要显示的所有子组件。

实现原理

IndexedStack的实现原理其实很简单,只是在显示某个子组件时,将其他的子组件隐藏了。要实现这个功能,Flutter内部是通过一个Stack和多个Offstage实现的。

Stack:是一个无限制大小的布局模型,它的子组件可以叠放在一起。IndexedStack实际上就是一个Stack。 Offstage:用于将一个组件隐藏,可以通过将其offstage属性设置为true来实现。 IndexedStack会将除了当前显示的子组件以外的所有子组件的offstage属性都设置为true,这样就实现了不显示这些子组件的目的。

注意事项:

IndexedStack会同时加载所有的子组件,所以如果子组件比较多或者占用内存较大,这种方式可能会对性能产生影响。 当需要动态切换子组件时,可以通过修改index来实现,比如将index设置为一个变量,然后在需要切换时,修改这个变量即可