30天计划第5天-Flutter 基础布局(1)

-
-
2025-10-23 12:20
  • 学习内容

    1. 线性布局:Row(水平排列)、Column(垂直排列)

    2. 容器组件:Container(控制宽高、背景色、边距)

  • 实践任务

    1. Column 做一个页面:顶部标题、中间图片、底部两个按钮(水平排列用 Row

    2. 给容器加边距(margin)、内边距(padding)、背景色

一、前置知识:Flutter布局的核心概念

1. 布局的本质

通过“父组件”约束“子组件”的位置和大小,子组件在约束范围内排列

2. 主轴与交叉轴

  • 主轴:组件排列的方向(Row的主轴是水平方向,Column的主轴是垂直方向)

  • 交叉轴:与主轴垂直的方向(Row的交叉轴是垂直方向,Column的交叉轴是水平方向)

3. 约束传递

父组件给子组件设置约束(如最大宽高),子组件根据约束确定自身大小,再进行排列

二、线性布局:Row(水平排列)

1. 核心作用

将子组件按水平方向依次排列,是实现横向布局(如导航栏、按钮组)的核心组件。

2. 核心属性

属性名作用常用值(以Row为例)
children子组件列表(必须传,可以放多个Widget)[Text('A'), Text('B), …]
mainAxisAlignment主轴对齐方式(水平方向)MainAxisAlignment.start(默认左对齐)、center(居中)、end(右对齐)、spaceBetween(两端对齐,中间均匀分布)、spaceAround(均匀分布,两侧留空)、spaceEvenly(完全均匀分布)
crossAxisAlignment交叉轴对齐方式(垂直方向)CrossAxisAlignment.center(默认居中)、start(顶部对齐)、end(底部对齐)、stretch(拉伸填满交叉轴)
mainAxisSize主轴占用空间(水平方向宽度)MainAxisSize.max(默认,占满父组件)、min(仅占用子组件总宽度)
children子组件列表任意Widget组合

3. 代码示例

(1)示例1:基础Row(默认对齐)

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Row基础示例')),
        body: Padding(
          padding: const EdgeInsets.all(16), // 页面内边距,避免组件贴边
          child: Row(
            // 默认:mainAxisAlignment=start(左对齐),crossAxisAlignment=center(垂直居中)
            children: [
              // 子组件1:红色容器
              Container(width: 80, height: 80, color: Colors.red),

              // 水平边距组件
              const SizedBox(width: 16),

              // 子组件2:蓝色容器
              Container(width: 80, height: 80, color: Colors.blue),

              const SizedBox(width: 16),

              // 子组件3:绿色容器
              Container(width: 80, height: 80, color: Colors.green)
            ],
          ),
        ),
      ),
    );
  }
}
  • 运行效果:

(2)示例2:主轴对齐(mainAxisAlignment)

  • 修改示例1的Row属性,测试不同主轴对齐方式
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween, // 两端对齐,中间均匀分布
  children: [
    Container(width: 80, height: 80, color: Colors.red),
    Container(width: 80, height: 80, color: Colors.blue),
    Container(width: 80, height: 80, color: Colors.green),
  ],
)
  • 运行效果:
  • 常见对齐效果
    • start:左对齐(默认)
    • center:水平居中
    • end:右对齐
    • spaceBetween:左右两侧贴边,中间组件均匀分布
    • spaceAround:组件之间间距相等,两侧留一半间距
    • spaceEvenly:组件之间和两侧间距完全相等

(3)示例3:交叉轴对齐(crossAxisAlignment)

  • 让子组件高度不同,测试交叉轴对齐:
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.end, // 垂直底部对齐
  children: [
    Container(width: 80, height: 60, color: Colors.red), // 高度60
    const SizedBox(width: 16),
    Container(width: 80, height: 80, color: Colors.blue), // 高度80
    const SizedBox(width: 16),
    Container(width: 80, height: 100, color: Colors.green), // 高度100
  ],
)
  • 运行效果:
  • 重点:crossAxisAlignment:stretch会让所有组件拉伸到与Row交叉轴(垂直方向)同高(需确保Row的高度足够)

(4)示例4:mainAixsSize控制主轴占用空间

Row(
  mainAxisSize: MainAxisSize.min, // 仅占用子组件总宽度(不占满屏幕)
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Container(width: 80, height: 80, color: Colors.red),
    const SizedBox(width: 16),
    Container(width: 80, height: 80, color: Colors.blue),
  ],
)
  • 运行效果

4. 常见问题:Row布局溢出(Overflow)

  • 当子组件总宽度超过父组件宽度时,会出现黄色警告条(溢出)
// 错误示例:3个120px的容器+间距,总宽度=120+16+120+16+120=392px,超过手机屏幕宽度(约360px)
Row(
  children: [
    Container(width: 120, height: 80, color: Colors.red),
    const SizedBox(width: 16),
    Container(width: 120, height: 80, color: Colors.blue),
    const SizedBox(width: 16),
    Container(width: 120, height: 80, color: Colors.green),
  ],
)
  • 运行效果
  • 解决办法:用SingleChildScrollView包裹Row,实现水平滚动

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('Row基础示例')),
            body: Padding(
              padding: const EdgeInsets.all(16), // 页面内边距,避免组件贴边
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal, // 水平滚动
                child: Row(
                  // 默认:mainAxisAlignment=start(左对齐),crossAxisAlignment=center(垂直居中)
                  children: [
                    // 子组件1:红色容器
                    Container(width: 120, height: 80, color: Colors.red),
    
                    // 水平边距组件
                    const SizedBox(width: 16),
    
                    // 子组件2:蓝色容器
                    Container(width: 120, height: 80, color: Colors.blue),
    
                    const SizedBox(width: 16),
    
                    // 子组件3:绿色容器
                    Container(width: 120, height: 100, color: Colors.green)
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
  • 运行效果

三、线性布局:Column(垂直排列)

1. 核心作用

将子组件按垂直方向依次排列,是实现纵向布局(如:页面主体、列表项内部结构)的核心组件。

2. 核心属性(与Row一致,仅主轴/交叉轴方向不同)

四、容器组件Container

1. 核心作用

Container是Flutter中最常用的“容器组件”,用于包裹子组件并控制其:

  • 大小(宽高)
  • 背景(颜色、图片、渐变)
  • 边距(内边距padding、外边距margin)
  • 装饰(边框、圆角、阴影)
  • 定位(配合Stack布局时)

2. 核心属性

属性名作用示例值
width/height容器宽高(固定值)width: 200、height: 150
color背景色(与decoration互斥,不能同时设置)color: Colors.yellow
padding内边距(容器与子组件之间的间距)

padding: EdgeInset.all(16)、

EdgeInsets.symmetric(horizontal: 12, vertical: 8)

margin外边距(容器与其他组件之间的间距)margin: EdgeInsets.only(left: 10, top: 20)
decoration装饰(边框、圆角、阴影、背景图片等)BoxDecoration(…)
alignment子组件在容器内的对齐方式Alignment.center、Alignment.bottomRight
constraints容器的约束(最小/最大宽高)BoxConstraints(minWidth: 100, maxHeight: 200)

五、综合实战

  • 需求:
    • 顶部是标题栏(AppBar)
    • 主体式个人信息卡片(Column垂直排列)
      • 头像(圆形Container)
      • 姓名、昵称(文本、水平居中)
      • 功能按钮栏(Row水平排列,3个按钮)
    • 卡片有边框、圆角、阴影,整体居中显示
  • 代码参考
import 'package:flutter/material.dart';

void main() => runApp(const InfoCard());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('个人信息卡片'),
          centerTitle: true, // 标题居中
        ),
        body: Center(
          child: Container(
            width: 320,
            padding: const EdgeInsets.all(20),
            margin: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(16),
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withValues(alpha: 0.3),
                  spreadRadius: 1,
                  blurRadius: 8,
                  offset: const Offset(0, 3)
                )
              ],
              border: Border.all(color: Colors.blue[100]!, width: 1)
            ),
            // 卡片内部:垂直排列
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Container(
                  width: 100,
                  height: 100,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    image: const DecorationImage(
                      image: NetworkImage("https://upyun.askrabbit.net/avatar.png") ,
                      fit: BoxFit.cover
                    ),
                    border: Border.all(color: Colors.yellow, width: 3)
                  ),
                ),
                const SizedBox(height: 16),
                const Text(
                  '清遥',
                  style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                const Text(
                  '@安全研发工程师',
                  style: TextStyle(color: Colors.yellow, fontSize: 16),
                ),
                const SizedBox(height: 20),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    Column(
                      children: const [
                        Text('32', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                        Text('学习天数', style: TextStyle(color: Colors.blue, fontSize: 14))
                      ],
                    ),
                    Column(
                      children: const [
                        Text('8', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                        Text('已学习课程', style: TextStyle(color: Colors.blue, fontSize: 14))
                      ],
                    ),
                    Column(
                      children: const [
                        Text('3', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                        Text('收藏', style: TextStyle(color: Colors.blue, fontSize: 14))
                      ],
                    )
                  ],
                ),
                const SizedBox(height: 16),
                Container(
                  width: double.infinity,
                  height: 44,
                  decoration: BoxDecoration(
                    color: Colors.pink,
                    borderRadius: BorderRadius.circular(8)
                  ),
                  child: const Center(
                    child: Text(
                      '关注+',
                      style: TextStyle(color: Colors.white, fontSize: 16),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}
  • 运行效果


目录