通过 Stack + Positioned实现Flutter绝对定位学习。

简单Demo

import 'package:flutter/material.dart';

class MyPositionedDemoPage extends StatelessWidget {
  const MyPositionedDemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("MyPositionedDemoPage"),
      ),
      body: Container(
        width: MediaQuery.sizeOf(context).width,
        height: MediaQuery.sizeOf(context).height,
        margin: const EdgeInsets.all(15),
        child: Stack(
          children: <Widget>[
            MaterialButton(
              onPressed: () {},
              color: Colors.grey,
            ),
            Positioned(
              left: MediaQuery.sizeOf(context).width / 3,
              child: MaterialButton(onPressed: () {}, color: Colors.blue),
            ),
            Positioned(
              left: MediaQuery.sizeOf(context).width / 4,
              top: MediaQuery.sizeOf(context).height / 5 * 3,
              child: MaterialButton(
                onPressed: () {},
                color: Colors.orange,
              ),
            ),
            Positioned(
                left: MediaQuery.sizeOf(context).width / 2 -
                    Theme.of(context).buttonTheme.minWidth / 3,
                top: MediaQuery.sizeOf(context).height / 2 -
                    MediaQuery.paddingOf(context).top -
                    kToolbarHeight,
                child: MaterialButton(
                  onPressed: () {},
                  color: Colors.red,
                ))
          ],
        ),
      ),
    );
  }
}

       

代码解析

Positioned       

         在 Flutter 中,`Positioned` 是一个用于定位子组件的 widget,它通常在 `Stack` widget 中使用。通过 `Positioned`,你可以在父级 `Stack` 的边界内精确定位子组件,指定其相对于父级的四个边界的位置(`top`、`right`、`bottom`、`left`)。以下是对 `Positioned` 的详细解析:

 基本用法

        `Positioned` widget 需要与 `Stack` widget 一起使用,因为 `Stack` 提供了子组件可以相互重叠并相对于彼此进行定位的布局环境。

Stack(
  children: <Widget>[
	MaterialButton(
	  onPressed: () {},
	  color: Colors.grey,
	),
	Positioned(
	  left: MediaQuery.sizeOf(context).width / 3,
	  child: MaterialButton(onPressed: () {}, color: Colors.blue),
	),
	Positioned(
	  left: MediaQuery.sizeOf(context).width / 4,
	  top: MediaQuery.sizeOf(context).height / 5 * 3,
	  child: MaterialButton(
		onPressed: () {},
		color: Colors.orange,
	  ),
	),
	Positioned(
		left: MediaQuery.sizeOf(context).width / 2 -
			Theme.of(context).buttonTheme.minWidth / 3,
		top: MediaQuery.sizeOf(context).height / 2 -
			MediaQuery.paddingOf(context).top -
			kToolbarHeight,
		child: MaterialButton(
		  onPressed: () {},
		  color: Colors.red,
		))
  ],
)

`Positioned` 属性

  • `top`: 子组件距离 `Stack` 顶部的距离。
  • `right`: 子组件距离 `Stack` 右边的距离。
  • `bottom`: 子组件距离 `Stack` 底部的距离。
  • `left`: 子组件距离 `Stack` 左边的距离。
  • `width` 和 `height`: 直接设置子组件的宽度和高度。如果没有设置,子组件会按照其自身的尺寸进行布局。

如何使用

  1. 在 `Stack` 中定位:`Positioned` 必须是 `Stack` 的直接子组件,它通过指定相对于 `Stack` 边界的偏移值来定位自己。
  2. 支持多种组合:可以同时使用多个属性(如 `top` 和 `left`),也可以单独使用一个属性(如 `right`)来实现不同的定位效果。
  3. 灵活性:`Positioned` 提供了灵活的布局方式,可以在屏幕上精确控制子组件的位置,适合需要重叠和复杂布局的场景。
注意事项
  • 重叠顺序:在 `Stack` 中,后添加的 `Positioned` widget 会显示在前面。

Stack

        `Stack` 是 Flutter 中一个非常强大的布局 widget,它允许子组件相互重叠。通过 `Stack`,你可以创建复杂的、层叠的界面布局。以下是对 `Stack` widget 的详细解析:

`Stack` 的基本概念

  • 层叠布局:`Stack` 中的子组件是按顺序从底到顶叠加的。最早添加的组件位于底层,最后添加的组件位于顶层。
  • 定位子组件:通常使用 `Positioned` widget 来精确定位 `Stack` 中的子组件。

`Stack` 的主要属性

  • `alignment`:定义没有位置约束的子组件如何在 `Stack` 中对齐。默认为 `AlignmentDirectional.topStart`,即左上角。你可以使用 `Alignment` 类提供的常量(如 `Alignment.center`)来更改此行为。
  • `fit`:决定 `Stack` 如何调整其非定位子组件的大小。常用的值有:
  • `StackFit.loose`(默认):子组件根据自身大小布局。
  • `StackFit.expand`:子组件扩展以填充 `Stack`。
  • `StackFit.passthrough`:子组件尽可能大。
  • `clipBehavior`:定义 `Stack` 的裁剪行为。常用的值有 `Clip.none`(不裁剪)和 `Clip.hardEdge`(裁剪到 `Stack` 的边界)。

注意事项
  1. 子组件顺序:`Stack` 中子组件的顺序决定了它们的绘制顺序。后添加的组件会在前面绘制。
  2. 动态布局:`Stack` 适合用于需要相互重叠的布局场景,例如徽章、图像叠加文字等。
  3. 性能考虑:当 `Stack` 中有很多子组件时,可能会影响性能,特别是在复杂布局中。尽量优化子组件的数量和复杂度。
  4. 调试布局:使用 Flutter 提供的调试工具(如 `flutter inspector`)可以帮助你更好地理解 `Stack` 中子组件的布局和对齐方式。

通过 `Stack`,你可以灵活地创建复杂的 UI 布局,利用 `Positioned` 和对 `alignment` 的合理设置,可以实现高度自定义的界面布局。

以下是一些关于 `Stack` 的高级用法和技巧,它们可以帮助你更好地理解和利用 `Stack` 的特性:

高级用法和技巧

使用 `IndexedStack`:

  • `IndexedStack` 是 `Stack` 的一个变体,它一次只显示一个子组件。可以通过指定索引来控制哪个子组件可见。
  • 适用于需要在多个子组件之间切换显示的场景,例如分页或选项卡界面。
IndexedStack(
  index: 1,  // 显示第二个子组件
  children: <Widget>[
    Container(color: Colors.red),
    Container(color: Colors.green),
    Container(color: Colors.blue),
  ],
)

动画和交互:

  • 使用 `AnimatedPositioned` 可以在 `Stack` 中实现平滑的子组件位置变化,适用于需要动画效果的场合。
  • 可以结合手势检测(如 `GestureDetector`)来实现交互式的布局调整。

混合不同布局:

  • 在 `Stack` 内部可以嵌套其他布局组件(如 `Column`、`Row`),以实现更复杂的布局结构。
  • 通过组合使用 `Positioned` 和 `Align` 可以实现子组件的精确定位和对齐。

响应式布局:

  • 利用 `MediaQuery` 或 `LayoutBuilder` 动态调整 `Stack` 的布局,以适应不同的屏幕尺寸和方向。
  • 创建自适应的 UI,确保在各种设备上都有良好的显示效果。

处理溢出问题:

  • 如果子组件超出 `Stack` 的边界,可以通过设置 `clipBehavior` 来控制裁剪行为。
  • 使用 `OverflowBox` 或 `SizedOverflowBox` 来处理可能的溢出布局需求。

调试和优化:

  • 使用 `Flutter Inspector` 工具可以可视化查看 `Stack` 中子组件的布局,帮助定位问题。
  • 通过减少不必要的重叠和复杂子组件,优化 `Stack` 的性能。

实践中的注意事项

  • 布局调试:当遇到布局问题时,可以使用 `Container` 的边框和颜色属性来帮助可视化布局边界。
  • 性能优化:尽量减少 `Stack` 中子组件的数量和复杂度,避免不必要的重叠和复杂布局。
  • 组合布局:`Stack` 可以与其他布局 widget(如 `Flexible`、`Expanded`)结合使用,以创建灵活且响应式的界面。

通过理解这些高级用法和技巧,你可以更灵活地使用 `Stack` 来实现复杂的 UI 布局。无论是用于简单的重叠布局还是复杂的交互式界面,`Stack` 都提供了强大的功能和灵活性。

MediaQuery

`MediaQuery` 是 Flutter 中用于获取设备屏幕信息的一个类,它可以帮助你根据设备的尺寸、方向、像素密度等来调整你的应用布局。`MediaQuery.sizeOf(context)` 是一种用法,它返回当前设备屏幕的尺寸。

基本概念
  • `MediaQuery`:提供当前设备屏幕的环境信息,包括尺寸、方向、缩放因子等。
  • `size`:返回一个 `Size` 对象,包含屏幕的宽度(`width`)和高度(`height`)。

使用场景

`MediaQuery` 的 `size` 属性通常用于创建响应式布局,使得应用程序能够在不同设备和屏幕尺寸上保持良好的显示效果。

注意事项
  1. 上下文正确性:确保在 widget 树中适当的位置使用 `MediaQuery.of(context)`。在 `build` 方法中使用它是最常见的方式。
  2. 响应式设计:利用 `MediaQuery` 提供的信息,可以根据屏幕尺寸动态调整布局和样式。例如,使用不同的字体大小、边距以及组件尺寸。
  3. 设备旋转:`MediaQuery` 会自动适应设备的方向变化(如从竖屏到横屏),这使得布局调整更加灵活。
  4. 与其他工具结合:`MediaQuery` 可以与 `LayoutBuilder` 和 `OrientationBuilder` 结合使用,以实现更复杂的响应式布局。

通过 `MediaQuery.of(context).size`,你可以轻松获取屏幕尺寸信息,从而为各种屏幕尺寸和方向提供优化的用户体验。这是 Flutter 开发中实现响应式设计的一个重要工具。

Theme.of(context).buttonTheme.minWidth

在 Flutter 中,`Theme.of(context)` 提供了访问当前主题(`ThemeData`)的能力,`ThemeData` 定义了应用的主题相关属性,包括颜色、字体、按钮样式等。`Theme.of(context).buttonTheme` 返回一个 `ButtonThemeData` 对象,该对象包含与按钮相关的主题配置。

基本概念
  • `Theme.of(context)`:用于获取当前的主题数据(`ThemeData`),这是 Flutter 中实现应用外观一致性的核心方式。
  • `buttonTheme`:`ThemeData` 中的一个属性,返回 `ButtonThemeData`,包含按钮的全局配置,如按钮的最小宽度、高度、形状、布局行为等。
  • `minWidth`:`ButtonThemeData` 的一个属性,指定按钮的最小宽度。默认情况下,Flutter 中的按钮会至少扩展到这个宽度。如果按钮内容(如文本)需要更大的空间,按钮将根据内容自动调整大小。
使用场景

`buttonTheme.minWidth` 通常用于统一应用中按钮的最小宽度,确保按钮在不同屏幕和上下文中看起来一致。

注意事项
  1. 默认值和覆盖:如果没有明确设置 `minWidth`,则使用 Flutter 的默认值。你可以在特定的按钮上使用 `ButtonStyle` 来覆盖全局设置。
  2. 响应式设计:在不同的屏幕尺寸上,确保 `minWidth` 合理设置以避免按钮看起来过大或过小。
  3. 主题一致性:使用 `Theme` 可以确保应用中组件的一致性,但在某些情况下,可能需要为特定的组件设置不同的样式。

通过理解 `buttonTheme.minWidth`,你可以更好地控制按钮的布局和外观,为用户提供一致而美观的界面体验。这是主题定制化的一部分,使得应用可以适应品牌需求或设计规范。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部