Skip to content

Flutter 组件

Container 容器组件

属性名类型说明示例
alignmentAlignmentGeometry子元素对齐方式topCenter/topLeft/topRight
center/centerLeft/centerRight
bottomCenter/bottomLeft/bottomRight
decorationBoxDecoration容器装饰样式(背景/边框/阴影等)dart<br>BoxDecoration(<br> color: Colors.blue,<br> border: Border.all(<br> color: Colors.red,<br> width: 2.0<br> ),<br> borderRadius: BorderRadius.circular(8),<br> boxShadow: [BoxShadow(<br> color: Colors.black54,<br> offset: Offset(2.0, 2.0),<br> blurRadius: 10.0<br> )],<br> gradient: LinearGradient( // 或 RadialGradient<br> colors: [Colors.red, Colors.orange]<br> )<br>)<br>
marginEdgeInsetsGeometry容器外间距(与外部组件的距离)EdgeInsets.all(20.0)
EdgeInsets.only(top: 10)
paddingEdgeInsetsGeometry容器内边距(边缘与子元素的间距)EdgeInsets.all(10.0)
EdgeInsets.symmetric(horizontal: 15)
transformMatrix4矩阵变换(旋转/缩放/平移等)Matrix4.rotationZ(0.2)
Matrix4.translationValues(10, 0, 0)
heightdouble容器高度height: 100.0
widthdouble容器宽度width: 200.0
childWidget子元素内容child: Text("Hello")
child: Icon(Icons.star)

关键说明:

  1. alignment 使用 Alignment 类的静态常量控制子元素位置,如:

    dart
    alignment: Alignment.topCenter  // 顶部居中
  2. decoration 支持嵌套多种样式效果:

    • color:背景色(与 gradient 冲突)
    • border:边框样式
    • borderRadius:圆角半径
    • boxShadow:阴影效果
    • gradient:线性/径向渐变背景
  3. margin & padding 使用 EdgeInsets 类定义间距,常用构造方法:

    dart
    EdgeInsets.all(20)       // 全方向
    EdgeInsets.only(left:10) // 指定方向
    EdgeInsets.symmetric(vertical: 10) // 对称方向
  4. transform 通过 4x4 矩阵实现 3D 变换,常用操作:

    dart
    Matrix4.rotationZ(angle) // Z轴旋转
    Matrix4.translationValues(x, y, z) // 平移

Text 组件属性详解

属性名类型说明可选值/示例
textAlignTextAlign文本水平对齐方式TextAlign.center(居中)
TextAlign.left(左对齐)
TextAlign.right(右对齐)
TextAlign.justify(两端对齐)
textDirectionTextDirection文本阅读方向TextDirection.ltr(从左至右)
TextDirection.rtl(从右至左)
overflowTextOverflow文本溢出处理方式TextOverflow.clip(裁剪)
TextOverflow.fade(渐隐)
TextOverflow.ellipsis(省略号)
textScaleFactordouble字体缩放比例(基于系统字体大小)1.0(默认大小)
1.2(放大 20%)
0.8(缩小 20%)
maxLinesint最大显示行数1(单行显示)
3(最多 3 行)
null(无限制)
styleTextStyle文本样式设置(支持嵌套多种样式)dart<br>TextStyle(<br> color: Colors.red,<br> fontSize: 16.0,<br> fontWeight: FontWeight.bold,<br> fontStyle: FontStyle.italic,<br> letterSpacing: 1.5,<br> decoration: TextDecoration.underline<br>)

关键属性详解:

  1. textAlign
    控制文本在水平方向的对齐方式:

    dart
    Text('Hello World', textAlign: TextAlign.center)
  2. textDirection
    影响对齐方向和溢出行为:

    dart
    Text('مرحبا', textDirection: TextDirection.rtl) // 阿拉伯语从右向左
  3. overflow 与 maxLines 配合使用
    常见组合:

    dart
    Text('长文本内容...',
      maxLines: 1,
      overflow: TextOverflow.ellipsis // 单行省略
    )
  4. textScaleFactor
    响应系统字体设置:

    dart
    Text('可缩放的文本', textScaleFactor: 1.2)
  5. style 常用样式属性

    样式属性类型/值说明
    colorColor文字颜色
    fontSizedouble字体大小
    fontWeightFontWeight.w100-w900字体粗细
    fontStyleFontStyle.normal/italic正常/斜体
    letterSpacingdouble字符间距
    decorationTextDecoration.underline/lineTrough下划线/删除线
    fontFamilyString自定义字体

💡 最佳实践

  • 多语言文本务必设置正确的 textDirection
  • 长文本推荐组合使用 maxLinesoverflow
  • 使用 textScaleFactor 保持 UI 可访问性
  • 通过 stylecopyWith() 复用文本样式:
    dart
    TextStyle baseStyle = TextStyle(fontSize: 16);
    Text('标题', style: baseStyle.copyWith(fontWeight: FontWeight.bold))

Image组件属性详解

  • Image.asset, 本地图片
  • Image.network 远程图片 | 属性名 | 类型 | 说明 | 可选值/示例 | | ------------------------------- | ------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | alignment | Alignment | 图片在容器内的对齐方式 | Alignment.topCenter
    Alignment.bottomLeft
    Alignment(-0.5, 0.5) | | color
    colorBlendMode | Color
    BlendMode | 背景色与混合模式(需配合使用) | dart<br>color: Colors.green,<br>colorBlendMode: BlendMode.difference | | fit | BoxFit | 图片填充方式 | BoxFit.fill
    BoxFit.contain
    BoxFit.cover
    BoxFit.fitWidth
    BoxFit.fitHeight
    BoxFit.scaleDown | | repeat | ImageRepeat | 图片平铺方式 | ImageRepeat.repeat
    ImageRepeat.repeatX
    ImageRepeat.repeatY
    ImageRepeat.noRepeat | | width | double | 图片宽度(常配合圆形裁剪使用) | width: 120.0 | | height | double | 图片高度(常配合圆形裁剪使用) | height: 120.0 |

关键属性详解:

1. color & colorBlendMode

dart
Image.asset('assets/icon.png',
  color: Colors.green,
  colorBlendMode: BlendMode.difference
)
  • 常用混合模式
    • BlendMode.difference:反色效果
    • BlendMode.multiply:叠加效果
    • BlendMode.saturation:饱和度混合

2. fit(图片填充方式)

效果说明视觉表现
BoxFit.fill拉伸充满容器(可能变形)⬛ 全填充(变形)
BoxFit.contain保持比例全显示(可能有留白)⬛ 完整显示(带留白)
BoxFit.cover保持比例充满(可能裁剪)⬛ 充满(裁剪边缘)
BoxFit.fitWidth宽度充满(高度自适应)↔️ 宽度撑满
BoxFit.fitHeight高度充满(宽度自适应)↕️ 高度撑满
BoxFit.scaleDown保持比例但不放大(contain 变体)⬛ 完整显示(不放大)

3. repeat(平铺模式)

dart
Image.asset('pattern.png',
  repeat: ImageRepeat.repeatX,
  width: double.infinity
)
  • 应用场景
    • 背景纹理平铺
    • 创建连续图案
    • 无缝拼接大图

4. width/height(尺寸控制)

dart
ClipOval(
  child: Image.network('url',
    width: 100,
    height: 100,
    fit: BoxFit.cover,
  )
)

最佳实践:

  1. 混合模式选择

    • 使用color属性实现单色图标
    • 通过colorBlendMode创建特殊滤镜效果
  2. 响应式图片处理

    dart
    LayoutBuilder(
      builder: (context, constraints) {
        return Image.asset('bg.jpg',
          width: constraints.maxWidth,
          fit: BoxFit.cover
        );
      }
    )
  3. 圆形头像实现

    dart
    ClipOval(
      child: Image.asset('avatar.jpg',
        width: 80,
        height: 80,
        fit: BoxFit.cover,  // 关键:确保图片居中裁剪
      ),
    )
  4. 平铺背景优化

    dart
    Container(
      decoration: BoxDecoration(
        image: DecorationImage(
          image: AssetImage('tile.png'),
          repeat: ImageRepeat.repeat,
          scale: 0.5  // 控制图案密度
        )
      )
    )

💡 性能提示

  • 网络图片使用cacheHeight/cacheWidth减少内存占用
  • 需要圆角效果优先考虑ClipRRect而非borderRadius
  • 平铺小图替代大图可显著降低内存消耗

Flutter 实现圆形图片的 5 种方法

方法 1:使用 ClipOval(最常用)

dart
ClipOval(
  child: Image.network(
    'https://example.com/avatar.jpg',
    width: 100,
    height: 100,
    fit: BoxFit.cover,  // 关键:确保图片填充裁剪区域
  ),
)

方法 2:使用 CircleAvatar(头像专用)

dart
CircleAvatar(
  radius: 60,  // 控制圆形大小
  backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
  child: Text('AB'),  // 可添加备用文本
)

方法 3:使用 Container + BoxDecoration

dart
Container(
  width: 120,
  height: 120,
  decoration: BoxDecoration(
    shape: BoxShape.circle,
    // borderRadius: BorderRadius.circular(75),
    image: DecorationImage(
      image: NetworkImage('https://example.com/avatar.jpg'),
      fit: BoxFit.cover,
    ),
  ),
)

方法 4:使用 ClipRRect + 圆角(可调整圆角程度)

dart
ClipRRect(
  borderRadius: BorderRadius.circular(100),  // 设置为宽高的一半
  child: Image.asset(
    'assets/profile.jpg',
    width: 100,
    height: 100,
    fit: BoxFit.cover,
  ),
)

方法 5:使用 PhysicalModel(带物理效果)

dart
PhysicalModel(
  color: Colors.transparent,
  shape: BoxShape.circle,
  elevation: 5,  // 添加阴影深度
  child: Image.network(
    'https://example.com/avatar.jpg',
    width: 100,
    height: 100,
    fit: BoxFit.cover,
  ),
)

最佳实践建议:

  1. 尺寸控制

    • 宽高必须相等才能得到正圆
    • 使用 LayoutBuilder 实现响应式圆形:
    dart
    LayoutBuilder(
      builder: (context, constraints) {
        double size = constraints.maxWidth;
        return ClipOval(
          child: Image.asset('avatar.jpg',
            width: size,
            height: size,
            fit: BoxFit.cover,
          ),
        );
      }
    )
  2. 占位与错误处理

    dart
    ClipOval(
      child: CachedNetworkImage(
        imageUrl: 'https://example.com/avatar.jpg',
        width: 100,
        height: 100,
        fit: BoxFit.cover,
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
      ),
    )
  3. 添加边框

    dart
    Container(
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        border: Border.all(color: Colors.blue, width: 3),
      ),
      child: ClipOval(
        child: Image.asset(...),
      ),
    )
  4. 带点击效果的圆形图片

    dart
    InkWell(
      onTap: () {},  // 点击事件
      borderRadius: BorderRadius.circular(50),
      child: ClipOval(
        child: Image.asset(...),
      ),
    )

💡 性能优化

  • 网络图片使用 cached_network_image 包缓存
  • 本地图片使用 Image.asset 并指定精确尺寸
  • 对于小图标,优先使用 Icon 而不是图片资源

官方图标组件

dart
import 'package:flutter/material.dart';

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: const [
          Icon(Icons.search, color: Colors.red, size: 40),
          SizedBox(height: 10),
          Icon(Icons.home),
          SizedBox(height: 10),
          Icon(Icons.category),
          SizedBox(height: 10),
          Icon(Icons.shop),
          SizedBox(height: 10),
        ],
      )
    );
  }
}

集成阿里巴巴图标库自定义字体图标指南

完整实现步骤

dart
// 步骤1:定义图标映射类
class Iconfont {
  static const String _family = 'iconfont'; // 与pubspec.yaml中定义一致

  Iconfont._();

  // 使用Unicode码点定义图标
  static const IconData home = IconData(0xe600, fontFamily: _family);
  static const IconData search = IconData(0xe601, fontFamily: _family);
  static const IconData cart = IconData(0xe602, fontFamily: _family);
  static const IconData user = IconData(0xe603, fontFamily: _family);
  static const IconData settings = IconData(0xe604, fontFamily: _family);
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('阿里巴巴图标库示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 步骤2:使用自定义图标
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _buildIcon(Iconfont.home, '首页', Colors.blue),
                _buildIcon(Iconfont.search, '搜索', Colors.red),
                _buildIcon(Iconfont.cart, '购物车', Colors.orange),
              ],
            ),
            const SizedBox(height: 40),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _buildIcon(Iconfont.user, '用户', Colors.green),
                _buildIcon(Iconfont.settings, '设置', Colors.purple),
              ],
            ),
            const SizedBox(height: 40),
            // 使用Text显示图标
            Text(
              String.fromCharCode(Iconfont.home.codePoint),
              style: TextStyle(
                fontFamily: Iconfont._family,
                fontSize: 40,
                color: Colors.blue,
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildIcon(IconData icon, String label, Color color) {
    return Column(
      children: [
        Icon(icon, size: 36, color: color),
        const SizedBox(height: 8),
        Text(label, style: TextStyle(color: color)),
      ],
    );
  }
}

配置步骤详解

步骤 1:获取图标文件

  1. 访问阿里巴巴图标库
  2. 创建项目并添加所需图标
  3. 下载图标包(选择 Font class 方式)
  4. 解压后得到:
    • iconfont.ttf(字体文件)
    • iconfont.css(包含 Unicode 编码)

步骤 2:配置 Flutter 项目

  1. 在项目根目录创建 assets/fonts 文件夹
  2. iconfont.ttf 复制到此文件夹
  3. 修改 pubspec.yaml 添加字体配置:
yaml
flutter:
  uses-material-design: true
  assets:
    - assets/images/ # 如果有其他资源
  fonts:
    - family: iconfont # 自定义字体名称
      fonts:
        - asset: assets/fonts/iconfont.ttf

高级用法:自动生成映射类

  1. 安装代码生成工具:

    bash
    flutter pub global activate iconfont_builder
  2. 使用命令生成 Dart 类:

    bash
    flutter pub global run iconfont_builder --from path/to/iconfont.css --to lib/iconfont.dart

常见问题解决

  1. 图标显示为方块 □

    • 检查字体文件路径是否正确
    • 确保 pubspec.yaml 缩进正确
    • 执行 flutter clean 后重新运行
  2. 图标颜色不生效

    • 确认图标不是多色图标(多色图标需要使用 SVG)
    • 检查是否在父组件中覆盖了图标颜色
  3. 图标大小异常

    • 使用 size 参数明确指定尺寸
    • 避免在父容器中使用固定宽高限制
  4. 图标偏移问题

    dart
    Icon(
      Iconfont.cart,
      size: 24,
      textDirection: TextDirection.ltr, // 解决方向问题
    )

最佳实践建议

  1. 图标管理

    • 创建单独的 iconfont.dart 文件管理所有图标
    • 使用脚本自动更新图标映射类
    • 为图标添加详细注释说明用途
  2. 性能优化

    • 使用 const 修饰图标组件
    • 避免在动画中频繁改变图标
    • 复杂图标优先考虑 SVG 格式
  3. 多主题适配

    dart
    Icon(
      Iconfont.home,
      color: Theme.of(context).iconTheme.color,
    )
  4. 图标命名规范

    dart
    class AppIcons {
      static const IconData home = IconData(0xe600, fontFamily: 'iconfont');
      static const IconData search = IconData(0xe601, fontFamily: 'iconfont');
      // 按功能模块分组
    }

提示:对于多色图标或需要动态变化的图标,建议使用 flutter_svg 包加载 SVG 格式图标

ListView 列表组件

列表类型及核心属性

列表类型实现方式特点描述
垂直列表ListView默认垂直滚动,适合单列数据展示
垂直图文列表ListView + ListTile图文混合布局,适合商品列表、新闻列表等复杂内容
水平列表ListView(scrollDirection: Axis.horizontal)横向滚动,适合轮播图、分类标签等
动态列表ListView.builder按需构建列表项,性能最优,适合大数据集

核心属性详解

属性名类型说明
scrollDirectionAxis滚动方向:Axis.vertical(垂直,默认),Axis.horizontal(水平)
paddingEdgeInsetsGeometry列表内边距,如:EdgeInsets.all(10)
reversebool是否反向显示列表(从下往上/从右往左)
childrenList<Widget>直接子组件列表(静态列表使用)
itemCountint动态列表项数量(仅builder使用)
itemBuilderIndexedWidgetBuilder动态列表项构建函数((context, index) => Widget

1. 基础垂直列表

dart
ListView(
  padding: const EdgeInsets.all(16),
  children: const [
    ListTile(title: Text('选项1')),
    ListTile(title: Text('选项2')),
    ListTile(title: Text('选项3')),
    ListTile(title: Text('选项4')),
  ],
)

2. 垂直图文列表

dart
ListView(
  children: [
    ListTile(
      leading: Image.network('https://example.com/img1.jpg', width: 50),
      title: const Text('商品标题1'),
      subtitle: const Text('商品描述信息'),
      trailing: const Icon(Icons.arrow_forward),
    ),
    ListTile(
      leading: Image.network('https://example.com/img2.jpg', width: 50),
      title: const Text('商品标题2'),
      subtitle: const Text('商品描述信息'),
      trailing: const Icon(Icons.arrow_forward),
    ),
    // 更多列表项...
  ],
)

3. 水平列表

dart
SizedBox(
  height: 120, // 必须指定高度
  child: ListView(
    scrollDirection: Axis.horizontal,
    padding: const EdgeInsets.symmetric(horizontal: 12),
    children: [
      _buildCategory('美食', Icons.restaurant),
      _buildCategory('购物', Icons.shopping_cart),
      _buildCategory('旅行', Icons.flight),
      _buildCategory('娱乐', Icons.movie),
      _buildCategory('学习', Icons.school),
    ],
  ),
)

Widget _buildCategory(String name, IconData icon) {
  return Container(
    width: 100,
    margin: const EdgeInsets.symmetric(horizontal: 8),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, size: 40),
        const SizedBox(height: 8),
        Text(name),
      ],
    ),
  );
}

4. 动态列表(性能优化)

dart
final List<String> items = List.generate(100, (i) => '列表项 ${i + 1}');

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(child: Text('${index + 1}')),
      title: Text(items[index]),
      subtitle: Text('索引: $index'),
      trailing: Icon(
        index % 3 == 0 ? Icons.star : Icons.star_border,
        color: Colors.amber,
      ),
    );
  },
)

高级列表技巧

1. 分隔线控制

dart
ListView.separated(
  itemCount: 50,
  separatorBuilder: (context, index) => const Divider(height: 1),
  itemBuilder: (context, index) => ListTile(title: Text('项目 $index')),
)

2. 滚动控制器

dart
final ScrollController _controller = ScrollController();

@override
void initState() {
  super.initState();
  _controller.addListener(() {
    print('滚动位置: ${_controller.offset}');
  });
}

ListView.builder(
  controller: _controller,
  // ...
)

3. 无限滚动加载

dart
ListView.builder(
  itemCount: items.length + 1,
  itemBuilder: (context, index) {
    if (index == items.length) {
      return _buildLoadingIndicator();
    }
    return ListTile(title: Text(items[index]));
  },
)

Widget _buildLoadingIndicator() {
  return const Padding(
    padding: EdgeInsets.all(16),
    child: Center(child: CircularProgressIndicator()),
  );
}

4. 吸顶头部

dart
CustomScrollView(
  slivers: [
    SliverAppBar(
      pinned: true,
      expandedHeight: 200,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('吸顶标题'),
        background: Image.network('https://example.com/header.jpg', fit: BoxFit.cover),
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => ListTile(title: Text('内容 $index')),
        childCount: 50,
      ),
    ),
  ],
)

性能优化建议

  1. 静态列表

    dart
    ListView(
      children: const [ // 使用const
        ItemWidget(), // 组件也使用const
        ItemWidget(),
      ],
    )
  2. 动态列表黄金法则

    • 始终使用 ListView.builder
    • 设置精确的 itemCount
    • 避免在 itemBuilder 中创建新函数
  3. 懒加载图片

    dart
    ListView.builder(
      itemBuilder: (context, index) => CachedNetworkImage(
        imageUrl: images[index],
        placeholder: (_, __) => LoadingWidget(),
      ),
    )
  4. 列表项保持单一职责

    dart
    // ❌ 避免
    itemBuilder: (c, i) => Column(
      children: [
        if (i == 0) Header(),
        Item(data[i])
      ]
    )
    
    // ✅ 正确
    Column(
      children: [
        Header(),
        Expanded(child: ListView.builder(...))
      ]
    )
  5. 使用 RepaintBoundary

    dart
    ListView.builder(
      itemBuilder: (c, i) => RepaintBoundary(
        child: ComplexItemWidget(data[i])
      )
    )

常见问题解决方案

  1. 嵌套滚动冲突

    dart
    ListView(
      physics: const ClampingScrollPhysics(), // 解决嵌套滚动
      // ...
    )
  2. 列表高度错误

    dart
    // 在Column中包裹ListView
    Expanded(
      child: ListView(...)
    )
  3. 滚动位置保持

    dart
    class _MyListState extends State<MyList> with AutomaticKeepAliveClientMixin {
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(context) {
        super.build(context);
        return ListView(...);
      }
    }
  4. 动态更新优化

    dart
    // 使用Key强制重建特定项
    ListView.builder(
      itemBuilder: (c, i) => ItemWidget(
        key: ValueKey(items[i].id), // 唯一Key
        data: items[i],
      )
    )

    GridView 网格组件

核心属性总览

属性名类型说明
scrollDirectionAxis滚动方向:Axis.vertical(垂直,默认),Axis.horizontal(水平)
paddingEdgeInsetsGeometry内边距
reversebool是否反向排序(默认 false)
crossAxisSpacingdouble水平子 Widget 之间的间距
mainAxisSpacingdouble垂直子 Widget 之间的间距
crossAxisCountintGridView.count中使用,指定一行中的网格数量
maxCrossAxisExtentdoubleGridView.extent中使用,指定横轴子元素的最大长度
childAspectRatiodouble子 Widget 宽高比例(宽度/高度)
childrenList<Widget>子组件列表(用于静态网格)
gridDelegateSliverGridDelegate布局控制器(用于动态网格),有两种类型:
SliverGridDelegateWithFixedCrossAxisCount
SliverGridDelegateWithMaxCrossAxisExtent

三种创建方式详解

1. GridView.count - 固定数量网格

dart
GridView.count(
  crossAxisCount: 3, // 每行3个元素
  crossAxisSpacing: 8, // 水平间距
  mainAxisSpacing: 12, // 垂直间距
  childAspectRatio: 0.8, // 宽高比(宽/高)
  padding: const EdgeInsets.all(16),
  children: List.generate(12, (index) {
    return Container(
      color: Colors.blue[100 * (index % 9)],
      child: Center(child: Text('Item $index')),
    );
  }),
)

2. GridView.extent - 最大宽度网格

dart
GridView.extent(
  maxCrossAxisExtent: 150, // 每个网格最大宽度150
  mainAxisSpacing: 10,
  crossAxisSpacing: 10,
  childAspectRatio: 1, // 正方形网格
  children: List.generate(20, (index) {
    return Card(
      elevation: 3,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(Icons.star, size: 40, color: Colors.amber),
          Text('商品 $index')
        ],
      ),
    );
  }),
)

3. GridView.builder - 动态网格(推荐)

dart
final List<Product> products = [
  Product(name: '手机', icon: Icons.phone_iphone),
  Product(name: '电脑', icon: Icons.computer),
  // ...更多数据
];

GridView.builder(
  padding: const EdgeInsets.all(16),
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 4, // 每行4个
    crossAxisSpacing: 8,
    mainAxisSpacing: 8,
    childAspectRatio: 0.9,
  ),
  itemCount: products.length,
  itemBuilder: (context, index) {
    return ProductItem(product: products[index]);
  },
)

高级网格布局技巧

1. 响应式网格布局

dart
LayoutBuilder(
  builder: (context, constraints) {
    int crossAxisCount;
    if (constraints.maxWidth > 600) {
      crossAxisCount = 4; // 大屏幕4列
    } else if (constraints.maxWidth > 400) {
      crossAxisCount = 3; // 中等屏幕3列
    } else {
      crossAxisCount = 2; // 小屏幕2列
    }

    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: crossAxisCount,
        // ...
      ),
      // ...
    );
  },
)

2. 网格与列表混合布局

dart
CustomScrollView(
  slivers: [
    SliverGrid(
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200,
        mainAxisSpacing: 10,
        crossAxisSpacing: 10,
        childAspectRatio: 0.8,
      ),
      delegate: SliverChildBuilderDelegate(
        (context, index) => ProductCard(products[index]),
        childCount: products.length,
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => ListTile(title: Text('推荐商品 $index')),
        childCount: 10,
      ),
    ),
  ],
)

3. 网格头部吸顶效果

dart
CustomScrollView(
  slivers: [
    SliverAppBar(
      pinned: true,
      expandedHeight: 200,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('商品分类'),
        background: Image.network('header.jpg', fit: BoxFit.cover),
      ),
    ),
    SliverGrid(
      // ...网格配置
    ),
  ],
)

4. 网格项动画效果

dart
GridView.builder(
  gridDelegate: /*...*/,
  itemBuilder: (context, index) {
    return AnimatedContainer(
      duration: Duration(milliseconds: 300),
      curve: Curves.easeInOut,
      child: /*...*/,
    );
  },
)

网格布局最佳实践

1. 性能优化方案

dart
GridView.builder( // 始终使用builder
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
  ),
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ProductCard( // 提取为独立组件
      key: ValueKey(items[index].id), // 唯一键值
      product: items[index],
    );
  },
)

2. 网格卡片组件示例

dart
class ProductCard extends StatelessWidget {
  final Product product;

  const ProductCard({super.key, required this.product});

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: ClipRRect(
              borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
              child: Image.network(product.imageUrl, fit: BoxFit.cover),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(product.name, style: Theme.of(context).textTheme.titleMedium),
                const SizedBox(height: 4),
                Text(${product.price}', style: const TextStyle(color: Colors.red)),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

3. 网格布局常见问题解决

问题:网格高度不正确

dart
// 解决方案:使用Expanded包裹
Scaffold(
  body: Column(
    children: [
      Expanded( // 关键
        child: GridView.count(
          crossAxisCount: 3,
          // ...
        ),
      ),
    ],
  ),
)

问题:滚动冲突

dart
GridView.builder(
  physics: const ClampingScrollPhysics(), // 解决嵌套滚动
  // ...
)

问题:网格项大小不一致

dart
SliverGridDelegateWithMaxCrossAxisExtent(
  maxCrossAxisExtent: 150,
  mainAxisExtent: 180, // 固定高度
  // ...
)

网格布局使用场景示例

电商商品网格

dart
GridView.builder(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    mainAxisSpacing: 12,
    crossAxisSpacing: 12,
    childAspectRatio: 0.75,
  ),
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
  itemCount: products.length,
  itemBuilder: (context, index) {
    return ProductGridItem(product: products[index]);
  },
)

照片墙布局

dart
GridView.builder(
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 150,
    mainAxisSpacing: 4,
    crossAxisSpacing: 4,
  ),
  itemCount: images.length,
  itemBuilder: (context, index) {
    return GestureDetector(
      onTap: () => _openFullScreen(index),
      child: Hero(
        tag: 'image-$index',
        child: Image.network(images[index], fit: BoxFit.cover),
      ),
    );
  },
)

设置选项网格

dart
GridView.count(
  crossAxisCount: 4,
  padding: const EdgeInsets.all(24),
  children: [
    _buildSettingItem(Icons.wifi, '网络'),
    _buildSettingItem(Icons.bluetooth, '蓝牙'),
    _buildSettingItem(Icons.battery_full, '电池'),
    _buildSettingItem(Icons.storage, '存储'),
    // ...更多设置项
  ],
)

Widget _buildSettingItem(IconData icon, String label) {
  return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Icon(icon, size: 32),
      const SizedBox(height: 8),
      Text(label),
    ],
  );
}

性能提示

  • 对于大型网格使用 GridView.builder + SliverGridDelegate
  • 使用 const 构造函数优化静态网格项
  • 为网格项添加 RepaintBoundary 减少重绘
  • 使用 cacheExtent 预加载网格区域提高滚动流畅度

Padding 组件

属性名类型说明示例
paddingEdgeInsetsGeometry设置内边距(核心属性)EdgeInsets.all(16)
EdgeInsets.only(top: 20)
EdgeInsets.symmetric(horizontal: 10)
childWidget需要添加内边距的子组件child: Text('Hello')
child: Icon(Icons.star)

1. 统一内边距

dart
Padding(
  padding: const EdgeInsets.all(20), // 所有方向20像素内边距
  child: Container(
    color: Colors.blue,
    child: Text('统一内边距'),
  ),
)

2. 非对称内边距

dart
Padding(
  padding: const EdgeInsets.only(
    top: 30,
    left: 15,
    right: 15
  ), // 仅顶部/左侧/右侧有内边距
  child: Card(
    child: ListTile(
      title: Text('非对称内边距示例'),
      subtitle: Text('底部无内边距'),
    ),
  ),
)

3. 对称内边距

dart
Padding(
  padding: const EdgeInsets.symmetric(
    vertical: 10,   // 垂直方向10px
    horizontal: 20   // 水平方向20px
  ),
  child: ElevatedButton(
    onPressed: () {},
    child: Text('对称内边距按钮'),
  ),
)

4. 组合内边距

dart
Padding(
  padding: const EdgeInsets.fromLTRB(15, 10, 15, 5), // 左15, 上10, 右15, 下5
  child: Text('自定义组合内边距'),
)

线性布局(Row 和 Column)

属性名Row(水平布局)Column(垂直布局)说明
主轴方向水平(从左到右)垂直(从上到下)布局的主轴方向
主轴对齐MainAxisAlignmentMainAxisAlignment控制子组件在主轴上的排列方式
交叉轴对齐CrossAxisAlignmentCrossAxisAlignment控制子组件在交叉轴上的排列方式
主轴尺寸MainAxisSizeMainAxisSize主轴方向占用的空间(max 或 min)
文本方向TextDirection-控制水平方向子组件的排列顺序
垂直方向-VerticalDirection控制垂直方向子组件的排列顺序
子组件列表childrenchildren包含的子组件

1. 主轴对齐(MainAxisAlignment)

dart
enum MainAxisAlignment {
  start,    // 从起始位置开始排列
  end,      // 从结束位置开始排列
  center,   // 居中排列
  spaceBetween, // 两端对齐,子组件之间间距相等
  spaceAround,  // 每个子组件两侧间距相等
  spaceEvenly,  // 所有间距相等(包括两端)
}

2. 交叉轴对齐(CrossAxisAlignment)

dart
enum CrossAxisAlignment {
  start,    // 沿交叉轴起始位置对齐
  end,      // 沿交叉轴结束位置对齐
  center,   // 居中对齐(默认)
  stretch,  // 拉伸子组件填满交叉轴
  baseline, // 按文本基线对齐
}

3. 主轴尺寸(MainAxisSize)

dart
enum MainAxisSize {
  min, // 占用最小空间
  max, // 占用最大可用空间(默认)
}

基础用法示例

1. 基本水平布局(Row)

dart
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Icon(Icons.star, color: Colors.amber),
    Text('评分:4.8', style: TextStyle(fontSize: 16)),
    ElevatedButton(
      onPressed: () {},
      child: Text('购买'),
    ),
  ],
)

2. 基本垂直布局(Column)

dart
Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('标题', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
    SizedBox(height: 8),
    Text('副标题', style: TextStyle(fontSize: 16, color: Colors.grey)),
    SizedBox(height: 16),
    Text('详细内容...' * 5),
  ],
)

高级布局技巧

1. 灵活空间分配(Expanded)

dart
Row(
  children: [
    Expanded(
      flex: 2, // 占2份
      child: Container(height: 50, color: Colors.red),
    ),
    Expanded(
      flex: 3, // 占3份
      child: Container(height: 50, color: Colors.blue),
    ),
  ],
)

2. 相对空间分配(Flexible)

dart
Row(
  children: [
    Flexible(
      fit: FlexFit.loose, // 宽松分配
      child: Container(width: 150, color: Colors.green),
    ),
    Flexible(
      fit: FlexFit.tight, // 紧密分配
      child: Container(color: Colors.yellow),
    ),
  ],
)

3. 基线对齐(跨轴)

dart
Row(
  crossAxisAlignment: CrossAxisAlignment.baseline,
  textBaseline: TextBaseline.alphabetic,
  children: [
    Text(
      '24',
      style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
    ),
    Text(
      '°C',
      style: TextStyle(fontSize: 20),
    ),
  ],
)

4. 嵌套布局

dart
Column(
  children: [
    // 顶部导航栏
    Row(
      children: [
        IconButton(icon: Icon(Icons.menu),
        Expanded(child: TextField()),
        IconButton(icon: Icon(Icons.search)),
      ],
    ),

    // 内容区域
    Expanded(
      child: ListView(/*...*/),
    ),

    // 底部导航
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        IconButton(icon: Icon(Icons.home)),
        IconButton(icon: Icon(Icons.shopping_cart)),
        IconButton(icon: Icon(Icons.person)),
      ],
    ),
  ],
)

最佳实践与解决方案

1. 处理溢出问题

dart
Row(
  children: [
    Expanded( // 使用Expanded防止溢出
      child: Text('这是一个非常长的文本内容' * 5),
    ),
    IconButton(icon: Icon(Icons.more_vert)),
  ],
)

2. 响应式布局

dart
LayoutBuilder(
  builder: (context, constraints) {
    bool isWide = constraints.maxWidth > 600;

    return isWide
      ? Row(children: [NavigationRail(), ContentArea()]) // 宽屏布局
      : Column(children: [AppBar(), ContentArea()]);    // 窄屏布局
  },
)

3. 动态列表构建

dart
Column(
  children: List.generate(10, (index) =>
    ListTile(
      leading: CircleAvatar(child: Text('${index+1}')),
      title: Text('项目 $index'),
    ),
  ),
)

4. 复杂对齐场景

dart
Row(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    // 左侧头像
    CircleAvatar(radius: 30),

    // 右侧内容(使用Column)
    Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Text('用户名', style: TextStyle(fontWeight: FontWeight.bold)),
              SizedBox(width: 8),
              Text('@userhandle', style: TextStyle(color: Colors.grey)),
            ],
          ),
          SizedBox(height: 4),
          Text('内容文本...'),
        ],
      ),
    ),
  ],
)

性能优化指南

  1. 使用 const 构造函数

    dart
    const Row(
      children: [
        Icon(Icons.star),
        Text('固定内容'),
      ],
    )
  2. 提取子组件

    dart
    // 提取为独立组件
    class RatingBar extends StatelessWidget {
      const RatingBar({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Row(/*...*/);
      }
    }
  3. 避免深层嵌套

    dart
    // ❌ 不推荐(嵌套过深)
    Row(children: [Column(children: [Row(children: [...]])])
    
    // ✅ 推荐(提取子组件)
    CustomComplexWidget()
  4. 使用 IntrinsicHeight(谨慎使用):

    dart
    IntrinsicHeight(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // 等高子组件
        ],
      ),
    )

常见布局模式

1. 居中图标+文字按钮

dart
ElevatedButton(
  onPressed: () {},
  child: Row(
    mainAxisSize: MainAxisSize.min,
    children: [
      Icon(Icons.download),
      SizedBox(width: 8),
      Text('下载文件'),
    ],
  ),
)

2. 标签+数值展示

dart
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Text('温度', style: TextStyle(color: Colors.grey)),
    Text('24°C', style: TextStyle(fontSize: 20)),
  ],
)

3. 带图标的输入框

dart
Row(
  children: [
    Icon(Icons.search, color: Colors.grey),
    SizedBox(width: 8),
    Expanded(
      child: TextField(
        decoration: InputDecoration(
          hintText: '搜索...',
          border: InputBorder.none,
        ),
      ),
    ),
  ],
)

4. 卡片底部操作栏

dart
Card(
  child: Column(
    children: [
      Image.network('image.jpg'),
      Padding(
        padding: const EdgeInsets.all(16),
        child: Text('描述内容'),
      ),
      const Divider(height: 1),
      Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          TextButton(onPressed: () {}, child: Text('取消')),
          TextButton(onPressed: () {}, child: Text('确定')),
        ],
      ),
    ],
  ),
)

提示

  • 使用 Spacer() 在布局中创建灵活空间
  • 结合 Wrap 组件实现自动换行
  • 在需要滚动时使用 SingleChildScrollView 包裹 Row/Column
  • 使用 LayoutBuilder 获取父容器约束实现响应式布局

层叠布局(Stack, Align, Positioned)

组件主要功能核心特点
Stack层叠布局容器,允许子组件重叠显示支持相对定位和绝对定位
Align控制子组件在父容器中的对齐位置支持精确的分数定位(FractionalOffset)
Positioned在 Stack 中精确控制子组件的位置可设置上下左右四个方向的偏移值

Stack 属性

属性名类型说明
alignmentAlignmentDirectional默认子组件对齐方式(默认左上角)
textDirectionTextDirection文本方向(影响对齐方向)
fitStackFit未定位子组件的尺寸调整方式(loose/expand/passthrough)
clipBehaviorClip内容溢出处理方式(默认为 Clip.hardEdge)
childrenList<Widget>子组件列表

Align 属性

属性名类型说明
alignmentAlignment对齐方式(如 Alignment.center)
widthFactordouble宽度因子(相对于子组件宽度)
heightFactordouble高度因子(相对于子组件高度)
childWidget子组件

Positioned 属性

属性名类型说明
leftdouble距离 Stack 左边的距离
topdouble距离 Stack 顶部的距离
rightdouble距离 Stack 右边的距离
bottomdouble距离 Stack 底部的距离
widthdouble指定宽度(非必须)
heightdouble指定高度(非必须)
childWidget子组件

基础用法示例

1. 基础层叠布局

dart
Stack(
  children: [
    // 底层背景
    Container(color: Colors.blue, height: 200, width: double.infinity),

    // 居中对齐的文本
    const Align(
      alignment: Alignment.center,
      child: Text('居中文本', style: TextStyle(color: Colors.white, fontSize: 24)),

    // 右上角图标
    const Positioned(
      top: 10,
      right: 10,
      child: Icon(Icons.star, color: Colors.amber, size: 30),
    ),

    // 左下角按钮
    Positioned(
      bottom: 20,
      left: 20,
      child: ElevatedButton(
        onPressed: () {},
        child: const Text('点击'),
      ),
    ),
  ],
)

2. 相对定位(FractionalOffset)

dart
Align(
  alignment: const FractionalOffset(0.7, 0.3), // 70%宽度,30%高度处
  child: Container(
    width: 50,
    height: 50,
    decoration: const BoxDecoration(
      color: Colors.red,
      shape: BoxShape.circle,
    ),
  ),
)

3. 部分定位

dart
Positioned(
  left: 20,
  right: 20, // 左右定位,宽度自适应
  bottom: 40,
  child: Container(
    height: 50,
    decoration: BoxDecoration(
      color: Colors.green,
      borderRadius: BorderRadius.circular(10),
    ),
    child: const Center(child: Text('底部横幅')),
  ),
)

高级应用技巧

1. 响应式层叠布局

dart
LayoutBuilder(
  builder: (context, constraints) {
    return Stack(
      children: [
        // 背景图片
        Image.network('background.jpg',
          width: constraints.maxWidth,
          height: constraints.maxHeight,
          fit: BoxFit.cover),

        // 根据屏幕尺寸调整位置
        Positioned(
          top: constraints.maxHeight * 0.1,
          left: constraints.maxWidth * 0.15,
          child: const Text('响应式标题', style: TextStyle(fontSize: 24)),
        ),
      ],
    );
  },
)

2. 动画浮动按钮

dart
AnimatedPositioned(
  duration: const Duration(milliseconds: 300),
  bottom: _expanded ? 100 : 20,
  right: _expanded ? 100 : 20,
  child: FloatingActionButton(
    onPressed: () => setState(() => _expanded = !_expanded),
    child: const Icon(Icons.add),
  ),
)

3. 自定义对话框

dart
Stack(
  children: [
    // 半透明遮罩
    Positioned.fill(
      child: Container(color: Colors.black54),
    ),

    // 居中对话框
    Center(
      child: Container(
        width: 300,
        padding: const EdgeInsets.all(20),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(15),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text('确认删除?', style: TextStyle(fontSize: 18)),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                TextButton(onPressed: () {}, child: const Text('取消')),
                TextButton(onPressed: () {}, child: const Text('确定')),
              ],
            )
          ],
        ),
      ),
    ),
  ],
)

4. 图片水印效果

dart
Stack(
  children: [
    // 主图片
    Image.network('photo.jpg'),

    // 水印文本(重复排列)
    Positioned.fill(
      child: Column(
        children: List.generate(5, (row) => Expanded(
          child: Row(
            children: List.generate(4, (col) => Expanded(
              child: Center(
                child: Transform.rotate(
                  angle: -0.2,
                  child: const Text('机密',
                    style: TextStyle(
                      color: Colors.white30,
                      fontSize: 24,
                      fontWeight: FontWeight.bold
                    ),
                  ),
                ),
              ),
            )),
          ),
        )),
      ),
    ),
  ],
)

最佳实践与解决方案

1. 处理溢出问题

dart
Stack(
  clipBehavior: Clip.none, // 允许内容溢出
  children: [
    Container(color: Colors.blue, width: 100, height: 100),
    Positioned(
      top: 80, // 部分溢出容器
      left: 80,
      child: Container(width: 50, height: 50, color: Colors.red),
    ),
  ],
)

2. 等比例缩放元素

dart
Align(
  alignment: Alignment.center,
  widthFactor: 0.8, // 容器宽度为子组件的80%
  heightFactor: 0.6, // 容器高度为子组件的60%
  child: Container(
    color: Colors.green,
    child: const Center(child: Text('等比例缩放')),
),

3. 复杂定位系统

dart
Stack(
  children: [
    // 使用Align定位
    const Align(
      alignment: Alignment(0.8, -0.8), // 右上区域
      child: Icon(Icons.star, size: 40),
    ),

    // 使用Positioned定位
    Positioned(
      top: MediaQuery.of(context).size.height * 0.4,
      left: MediaQuery.of(context).size.width * 0.2,
      child: const Icon(Icons.circle, size: 40),
    ),

    // 使用FractionalTranslation
    FractionalTranslation(
      translation: const Offset(0.5, 0.5), // 中心点
      child: const Icon(Icons.add, size: 60),
    ),
  ],
)

4. 性能优化

dart
// 使用const避免重绘
const Stack(
  children: [
    Positioned(top: 10, child: Icon(Icons.star)),
    Align(alignment: Alignment.center, child: Text('固定内容')),
  ],
)

// 提取独立组件
class Badge extends StatelessWidget {
  const Badge({super.key});

  @override
  Widget build(BuildContext context) {
    return const Positioned(
      top: 5,
      right: 5,
      child: CircleAvatar(radius: 10, backgroundColor: Colors.red),
    );
  }
}

常见使用场景

1. 带徽章的头像

dart
Stack(
  clipBehavior: Clip.none, // 允许徽章溢出
  children: [
    const CircleAvatar(radius: 30, backgroundImage: NetworkImage('avatar.jpg')),
    Positioned(
      top: -5,
      right: -5,
      child: Container(
        padding: const EdgeInsets.all(2),
        decoration: const BoxDecoration(
          color: Colors.red,
          shape: BoxShape.circle,
        ),
        child: const Text('3', style: TextStyle(color: Colors.white)),
      ),
    ),
  ],
)

2. 卡片重叠效果

dart
Stack(
  children: [
    Positioned( // 底层卡片
      top: 20,
      left: 20,
      right: 20,
      child: Card(
        elevation: 4,
        child: Container(height: 150),
      ),
    ),
    Positioned( // 中层卡片
      top: 40,
      left: 40,
      right: 40,
      child: Card(
        elevation: 8,
        child: Container(height: 150),
      ),
    ),
    Positioned( // 顶层卡片
      top: 60,
      left: 60,
      right: 60,
      child: Card(
        elevation: 12,
        child: Container(height: 150),
      ),
    ),
  ],
)

3. 视频播放器控制层

dart
Stack(
  children: [
    // 视频内容
    VideoPlayer(controller: _controller),

    // 控制层(半透明背景)
    Positioned.fill(
      child: Container(
        color: Colors.black45,
        child: Column(
          children: [
            // 顶部标题栏
            AppBar(title: const Text('视频标题'), backgroundColor: Colors.transparent),

            // 中间播放按钮
            Expanded(
              child: Center(
                child: IconButton(
                  icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
                  iconSize: 50,
                  color: Colors.white,
                  onPressed: _togglePlay,
                ),
              ),
            ),

            // 底部进度条
            VideoProgressIndicator(_controller, allowScrubbing: true),
          ],
        ),
      ),
    ),
  ],
)

4. 地图标记点

dart
Stack(
  children: [
    // 地图组件
    GoogleMap(/*...*/),

    // 标记点
    Positioned(
      left: 100,
      top: 200,
      child: Column(
        children: [
          const Icon(Icons.location_pin, color: Colors.red, size: 40),
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(10),
            child: const Text('位置A'),
          ),
        ],
      ),
    ),
  ],
)

提示

  1. 优先使用 Positioned 进行精确控制,使用 Align 进行相对定位
  2. 使用 LayoutBuilder 实现响应式层叠布局
  3. 对于复杂布局,组合使用 Stack + Transform + Opacity
  4. 使用 Clip.none 允许内容溢出容器边界
  5. 避免在 Stack 中嵌套过多子组件(超过 5 个考虑重构)

Card 组件全面指南

Card 是 Flutter 中用于创建卡片式布局的核心组件,具有圆角、阴影和立体效果,非常适合展示内容区块。以下是 Card 组件的详细解析:

属性名类型默认值说明
marginEdgeInsetsGeometrynull卡片外部的边距
childWidget必填卡片内容区域
elevationdouble1.0阴影深度(值越大阴影越明显)
colorColorTheme.cardColor卡片背景颜色
shadowColorColorTheme.shadowColor阴影颜色
shapeShapeBorderRoundedRectangleBorder(borderRadius: BorderRadius.circular(4.0))卡片形状(可自定义圆角)
clipBehaviorClipClip.none内容裁剪方式(none/antiAlias/antiAliasWithSaveLayer/hardEdge)
borderOnForegroundbooltrue是否在子组件前绘制边框
surfaceTintColorColornull材料表面色调颜色(Material 3)

基础用法示例

1. 基础卡片

dart
Card(
  child: ListTile(
    leading: Icon(Icons.person),
    title: Text('用户姓名'),
    subtitle: Text('用户描述信息'),
    trailing: Icon(Icons.arrow_forward),
  ),
)

2. 自定义卡片

dart
Card(
  elevation: 8, // 更强的阴影效果
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(16), // 更大的圆角
  ),
  color: Colors.blue[50], // 自定义背景色
  margin: EdgeInsets.all(16), // 外边距
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('卡片标题', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
        SizedBox(height: 8),
        Text('这里是卡片的内容区域,可以放置各种组件...'),
        SizedBox(height: 16),
        Align(
          alignment: Alignment.centerRight,
          child: ElevatedButton(
            onPressed: () {},
            child: Text('操作按钮'),
          ),
        ),
      ],
    ),
  ),
)

3. 图文卡片

dart
Card(
  clipBehavior: Clip.antiAlias, // 平滑裁剪
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [
      Image.network(
        'https://example.com/image.jpg',
        height: 150,
        fit: BoxFit.cover,
      ),
      Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('产品名称', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 8),
            Text('¥299', style: TextStyle(color: Colors.red, fontSize: 20)),
            SizedBox(height: 8),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  children: List.generate(5, (i) =>
                    Icon(Icons.star, size: 16, color: i < 4 ? Colors.amber : Colors.grey)
                  ),
                ),
                Text('128条评价', style: TextStyle(color: Colors.grey)),
              ],
            ),
          ],
        ),
      ),
    ],
  ),
)

高级用法与技巧

1. 响应式卡片设计

dart
LayoutBuilder(
  builder: (context, constraints) {
    bool isWide = constraints.maxWidth > 600;

    return Card(
      margin: EdgeInsets.symmetric(
        vertical: 16,
        horizontal: isWide ? 40 : 16,
      ),
      child: Flex(
        direction: isWide ? Axis.horizontal : Axis.vertical,
        children: [
          if (isWide) Expanded(
            flex: 2,
            child: Image.asset('product.jpg', fit: BoxFit.cover),
          ),
          Expanded(
            flex: 3,
            child: Padding(
              padding: EdgeInsets.all(20),
              child: /* 内容区域 */
            ),
          ),
        ],
      ),
    );
  },
)

2. 动画卡片

dart
AnimatedContainer(
  duration: Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  child: Card(
    elevation: _isHovered ? 12 : 4, // 悬停时提升阴影
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(_isHovered ? 20 : 10),
    ),
    child: InkWell(
      onTap: () {},
      onHover: (hover) => setState(() => _isHovered = hover),
      child: /* 卡片内容 */
    ),
  ),
)

3. 卡片堆叠效果

dart
Stack(
  children: [
    Positioned(
      top: 20,
      left: 20,
      right: 20,
      child: Card(
        elevation: 2,
        color: Colors.grey[200],
        child: Container(height: 120),
      ),
    ),
    Positioned(
      top: 40,
      left: 40,
      right: 40,
      child: Card(
        elevation: 6,
        color: Colors.grey[300],
        child: Container(height: 120),
      ),
    ),
    Card(
      elevation: 12,
      margin: EdgeInsets.symmetric(horizontal: 60),
      child: Container(
        height: 120,
        padding: EdgeInsets.all(16),
        child: Center(child: Text('顶层卡片')),
      ),
    ),
  ],
)

4. 卡片式表单

dart
Card(
  margin: EdgeInsets.all(24),
  child: Form(
    key: _formKey,
    child: Padding(
      padding: EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text('登录账户', style: Theme.of(context).textTheme.titleLarge),
          SizedBox(height: 24),
          TextFormField(
            decoration: InputDecoration(labelText: '用户名'),
            validator: (value) => value!.isEmpty ? '请输入用户名' : null,
          ),
          SizedBox(height: 16),
          TextFormField(
            obscureText: true,
            decoration: InputDecoration(labelText: '密码'),
            validator: (value) => value!.isEmpty ? '请输入密码' : null,
          ),
          SizedBox(height: 24),
          ElevatedButton(
            onPressed: _submitForm,
            child: Text('登录'),
          ),
        ],
      ),
    ),
  ),
)

最佳实践指南

1. 内容组织原则

dart
Card(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // 1. 标题区域
      Padding(
        padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
        child: Text('卡片标题', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
      ),

      // 2. 分隔线(可选)
      Divider(height: 1, thickness: 1),

      // 3. 内容主体
      Padding(
        padding: EdgeInsets.all(16),
        child: Text('这里是卡片的主要内容区域...'),
      ),

      // 4. 操作区域
      ButtonBar(
        alignment: MainAxisAlignment.end,
        children: [
          TextButton(onPressed: () {}, child: Text('取消')),
          ElevatedButton(onPressed: () {}, child: Text('确定')),
        ],
      ),
    ],
  ),
)

2. 性能优化

dart
// 使用const减少重建
const Card(
  child: ListTile(
    title: Text('固定内容'),
  ),
)

// 复杂卡片提取为独立组件
class ProductCard extends StatelessWidget {
  const ProductCard({super.key});

  @override
  Widget build(BuildContext context) {
    return Card(/* ... */);
  }
}

3. 主题一致性

dart
MaterialApp(
  theme: ThemeData(
    cardTheme: CardTheme(
      elevation: 6,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
    ),
  ),
  home: Scaffold(/* ... */),
)

4. 无障碍支持

dart
Card(
  semanticContainer: true, // 将卡片作为语义容器
  child: MergeSemantics(
    child: ListTile(
      title: Text('无障碍卡片'),
      subtitle: Text('屏幕阅读器会朗读整个卡片内容'),
      onTap: () {},
    ),
  ),
)

常见问题解决方案

1. 阴影效果不明显

dart
Card(
  elevation: 8, // 增加阴影深度
  shadowColor: Colors.black.withOpacity(0.5), // 加深阴影颜色
  // ...
)

2. 圆角不生效

dart
Card(
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(20), // 设置圆角半径
  ),
  // ...
)

3. 内容超出边界

dart
Card(
  clipBehavior: Clip.antiAlias, // 裁剪超出内容
  child: Column(
    children: [
      Image.asset('large-image.jpg'), // 可能超出边界
      // ...
    ],
  ),
)

4. 响应式边距

dart
Card(
  margin: EdgeInsets.symmetric(
    vertical: 16,
    horizontal: MediaQuery.of(context).size.width > 600 ? 40 : 16,
  ),
  // ...
)

实际应用场景

1. 设置项卡片

dart
Card(
  child: Column(
    children: [
      SwitchListTile(
        title: Text('通知设置'),
        value: _notificationsEnabled,
        onChanged: (v) => setState(() => _notificationsEnabled = v),
      ),
      Divider(height: 1),
      SwitchListTile(
        title: Text('夜间模式'),
        value: _darkMode,
        onChanged: (v) => setState(() => _darkMode = v),
      ),
    ],
  ),
)

2. 数据展示卡片

dart
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text('今日数据', style: TextStyle(fontSize: 18)),
        SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _buildDataItem(Icons.visibility, '访问量', '1,248'),
            _buildDataItem(Icons.shopping_cart, '订单', '86'),
            _buildDataItem(Icons.attach_money, '收入', '¥5,280'),
          ],
        ),
      ],
    ),
  ),
)

Widget _buildDataItem(IconData icon, String label, String value) {
  return Column(
    children: [
      Icon(icon, size: 30, color: Colors.blue),
      SizedBox(height: 8),
      Text(label, style: TextStyle(color: Colors.grey)),
      SizedBox(height: 4),
      Text(value, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
    ],
  );
}

3. 用户资料卡片

dart
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Row(
      children: [
        CircleAvatar(
          radius: 40,
          backgroundImage: NetworkImage('avatar.jpg'),
        ),
        SizedBox(width: 16),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('张小明', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
              SizedBox(height: 4),
              Text('高级UI设计师', style: TextStyle(color: Colors.grey)),
              SizedBox(height: 8),
              Wrap(
                spacing: 8,
                children: [
                  Chip(label: Text('UI设计')),
                  Chip(label: Text('插画')),
                  Chip(label: Text('动效')),
                ],
              ),
            ],
          ),
        ),
      ],
    ),
  ),
)

4. 天气信息卡片

dart
Card(
  color: Colors.blue[700],
  child: Padding(
    padding: EdgeInsets.all(20),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text('武汉市', style: TextStyle(fontSize: 24, color: Colors.white)),
            Text('2023-07-15', style: TextStyle(color: Colors.white70)),
          ],
        ),
        SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            Column(
              children: [
                Text('28°', style: TextStyle(fontSize: 48, color: Colors.white)),
                Text('多云', style: TextStyle(color: Colors.white)),
              ],
            ),
            WeatherIcon(weather: 'cloudy', size: 80),
          ],
        ),
        SizedBox(height: 20),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text('湿度: 65%', style: TextStyle(color: Colors.white)),
            Text('风速: 3km/h', style: TextStyle(color: Colors.white)),
            Text('气压: 1013hPa', style: TextStyle(color: Colors.white)),
          ],
        ),
      ],
    ),
  ),
)

提示

  1. 使用 elevation 控制卡片层次感,但避免过度使用(建议 1-12 范围)
  2. 结合 ClipRRect 实现图片圆角效果更自然
  3. 使用 InkWellInkResponse 为卡片添加水波纹效果
  4. 在暗色模式下调整卡片颜色:color: Theme.of(context).cardColor
  5. 使用 Card 嵌套 Card 创建分层设计效果
  6. 遵循 Material Design 指南,卡片间距至少 8dp,内容内边距至少 16dp

CircleAvatar 组件

CircleAvatar 是一个圆形头像组件,用于展示用户头像、图标或文字,支持图片、图标、文字等多种内容形式,是用户界面中展示个人信息的核心组件。

核心属性表

属性名类型默认值说明
backgroundColorColor主题默认色圆形背景颜色(当没有背景图片时)
backgroundImageImageProvidernull背景图片(如 NetworkImage、AssetImage)
foregroundImageImageProvidernull前景图片(覆盖在背景之上)
childWidgetnull子组件(通常是文字或图标)
radiusdouble20圆形半径(定义大小)
minRadiusdoublenull最小半径
maxRadiusdoublenull最大半径
onBackgroundImageErrorFunctionnull背景图片加载失败的回调函数
onForegroundImageErrorFunctionnull前景图片加载失败的回调函数

基础用法示例

1. 文字头像

dart
CircleAvatar(
  radius: 30,
  backgroundColor: Colors.blue,
  child: Text(
    'AB',
    style: TextStyle(fontSize: 20, color: Colors.white),
  ),
)

2. 图片头像

dart
CircleAvatar(
  radius: 40,
  backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
)

3. 图标头像

dart
CircleAvatar(
  radius: 30,
  backgroundColor: Colors.amber,
  child: Icon(Icons.person, color: Colors.white),
)

4. 带边框的头像

dart
CircleAvatar(
  radius: 40,
  backgroundColor: Colors.white, // 边框颜色
  child: CircleAvatar(
    radius: 38,
    backgroundImage: AssetImage('assets/avatar.jpg'),
  ),
)

高级用法与技巧

1. 加载失败处理

dart
CircleAvatar(
  radius: 40,
  backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
  onBackgroundImageError: (exception, stackTrace) {
    // 图片加载失败时显示默认头像
    debugPrint('头像加载失败: $exception');
  },
  child: Text('加载失败', style: TextStyle(fontSize: 12)), // 备用显示
)

2. 动态头像状态

dart
Stack(
  clipBehavior: Clip.none,
  children: [
    CircleAvatar(
      radius: 30,
      backgroundImage: NetworkImage(user.avatarUrl),
    ),
    Positioned(
      bottom: 0,
      right: 0,
      child: Container(
        padding: EdgeInsets.all(3),
        decoration: BoxDecoration(
          color: user.isOnline ? Colors.green : Colors.grey,
          shape: BoxShape.circle,
          border: Border.all(color: Colors.white, width: 2),
        ),
        width: 12,
        height: 12,
      ),
    ),
  ],
)

3. 头像组合

dart
Stack(
  children: [
    CircleAvatar(
      radius: 25,
      backgroundColor: Colors.grey[300],
      child: Text('+5', style: TextStyle(color: Colors.black)),
    ),
    Positioned(
      left: 15,
      child: CircleAvatar(
        radius: 25,
        backgroundColor: Colors.blue[300],
        child: Text('+4'),
      ),
    ),
    Positioned(
      left: 30,
      child: CircleAvatar(
        radius: 25,
        backgroundColor: Colors.green[300],
        child: Text('+3'),
      ),
    ),
    // 更多头像...
  ],
)

4. 头像动画效果

dart
AnimatedContainer(
  duration: Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: _isExpanded ? 100 : 50,
  height: _isExpanded ? 100 : 50,
  child: CircleAvatar(
    backgroundImage: NetworkImage('avatar.jpg'),
  ),
)

最佳实践指南

1. 统一头像尺寸

dart
// 创建统一尺寸的头像组件
class AppAvatar extends StatelessWidget {
  final String? imageUrl;
  final String? text;

  const AppAvatar({this.imageUrl, this.text});

  @override
  Widget build(BuildContext context) {
    return CircleAvatar(
      radius: 24,
      backgroundColor: Theme.of(context).primaryColor,
      backgroundImage: imageUrl != null ? NetworkImage(imageUrl!) : null,
      child: text != null
          ? Text(text!.substring(0, 2),
          : Icon(Icons.person),
    );
  }
}

2. 性能优化

dart
// 使用const减少重建
const CircleAvatar(
  radius: 30,
  child: Icon(Icons.person),
)

// 本地资源使用AssetImage
CircleAvatar(
  backgroundImage: AssetImage('assets/avatar.png'),
)

// 网络图片使用缓存
CachedNetworkImage(
  imageUrl: 'https://example.com/avatar.jpg',
  imageBuilder: (context, imageProvider) => CircleAvatar(
    backgroundImage: imageProvider,
  ),
  placeholder: (context, url) => CircleAvatar(
    backgroundColor: Colors.grey[200],
    child: CircularProgressIndicator(),
  ),
  errorWidget: (context, url, error) => CircleAvatar(
    backgroundColor: Colors.grey[200],
    child: Icon(Icons.error),
  ),
)

3. 主题一致性

dart
MaterialApp(
  theme: ThemeData(
    avatarTheme: AvatarThemeData(
      backgroundColor: Colors.blue[800],
      radius: 22,
    ),
  ),
)

4. 无障碍支持

dart
CircleAvatar(
  backgroundImage: NetworkImage('avatar.jpg'),
  child: ExcludeSemantics( // 隐藏文本内容
    child: Text(user.name.substring(0, 2)),
  ),
  semanticLabel: '${user.name}的头像', // 屏幕阅读器会朗读此内容
)

实际应用场景

1. 用户列表项

dart
ListTile(
  leading: CircleAvatar(
    backgroundImage: NetworkImage(user.avatarUrl),
  ),
  title: Text(user.name),
  subtitle: Text(user.email),
  trailing: Icon(Icons.arrow_forward),
)

2. 聊天界面

dart
Row(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    CircleAvatar(
      radius: 20,
      backgroundImage: NetworkImage(message.sender.avatar),
    ),
    SizedBox(width: 12),
    Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(message.sender.name, style: TextStyle(fontWeight: FontWeight.bold)),
          Text(message.content),
        ],
      ),
    ),
  ],
)

3. 个人资料页

dart
Column(
  children: [
    CircleAvatar(
      radius: 60,
      backgroundImage: NetworkImage(profile.avatar),
    ),
    SizedBox(height: 16),
    Text(profile.name, style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
    Text(profile.title, style: TextStyle(color: Colors.grey)),
  ],
)

4. 评论组件

dart
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            CircleAvatar(radius: 20, backgroundImage: NetworkImage(comment.user.avatar)),
            SizedBox(width: 12),
            Text(comment.user.name, style: TextStyle(fontWeight: FontWeight.bold)),
          ],
        ),
        SizedBox(height: 12),
        Text(comment.content),
        SizedBox(height: 12),
        Text(comment.timeAgo, style: TextStyle(color: Colors.grey, fontSize: 12)),
      ],
    ),
  ),
)

5. 头像选择器

dart
GridView.builder(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 4,
    crossAxisSpacing: 16,
    mainAxisSpacing: 16,
  ),
  itemCount: avatarOptions.length,
  itemBuilder: (context, index) {
    return GestureDetector(
      onTap: () => _selectAvatar(avatarOptions[index]),
      child: CircleAvatar(
        radius: 40,
        backgroundColor: _selectedIndex == index
            ? Colors.blue.withOpacity(0.3)
            : Colors.transparent,
        child: CircleAvatar(
          radius: 36,
          backgroundImage: AssetImage(avatarOptions[index]),
        ),
      ),
    );
  },
)

常见问题解决方案

1. 图片变形问题

dart
CircleAvatar(
  backgroundImage: NetworkImage('avatar.jpg'),
  child: ClipOval( // 确保圆形裁剪
    child: Image.network(
      'avatar.jpg',
      fit: BoxFit.cover,
      width: 80, // 需要指定宽高
      height: 80,
    ),
  ),
)

2. 加载状态处理

dart
CircleAvatar(
  radius: 30,
  child: FutureBuilder(
    future: _loadAvatar(),
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return CircularProgressIndicator();
      }
      if (snapshot.hasError) {
        return Icon(Icons.error);
      }
      return Image.memory(snapshot.data!);
    },
  ),
)

3. 头像大小自适应

dart
LayoutBuilder(
  builder: (context, constraints) {
    double avatarSize = constraints.maxWidth > 600 ? 80 : 50;

    return CircleAvatar(
      radius: avatarSize,
      backgroundImage: NetworkImage('avatar.jpg'),
    );
  },
)

4. 文字自动缩放

dart
CircleAvatar(
  radius: 40,
  backgroundColor: Colors.blue,
  child: FittedBox( // 文字自适应缩放
    fit: BoxFit.scaleDown,
    child: Text(
      user.initials,
      style: TextStyle(fontSize: 40), // 初始大字体
    ),
  ),
)

提示

  1. 使用 CachedNetworkImage 优化网络头像加载性能
  2. 对于未知用户,使用姓名首字母 + 随机背景色方案
  3. 使用 Hero 动画实现头像详情页的平滑过渡
  4. 在暗色模式下调整背景色:backgroundColor: Theme.of(context).colorScheme.surfaceVariant
  5. 使用 Badge 组件添加通知徽章
  6. 组合 Tooltip 提供用户信息提示
  7. 重要用户头像添加 BoxShadow 提升视觉层次

Button 按钮组件

Flutter 提供了丰富的按钮组件体系,满足不同场景下的交互需求。以下是完整的按钮组件分类与详解:

按钮类型对比表

按钮类型视觉风格适用场景核心组件
凸起按钮有阴影和背景色主要操作、强调操作ElevatedButton
文本按钮无背景、仅文字次要操作、对话框操作TextButton
描边按钮带边框、无填充中等重要性操作、取消操作OutlinedButton
图标按钮仅图标工具栏、紧凑空间操作IconButton
悬浮按钮圆形悬浮按钮主屏幕主要操作FloatingActionButton
下拉按钮带下拉菜单选项选择DropdownButton
弹出菜单按钮三点菜单更多操作菜单PopupMenuButton
自定义按钮完全自定义样式品牌化设计、特殊交互InkWell/GestureDetector

一、基础按钮组件

1. ElevatedButton (凸起按钮)

dart
ElevatedButton(
  onPressed: () {
    print('按钮被点击');
  },
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue, // 背景色
    foregroundColor: Colors.white, // 文字/图标颜色
    elevation: 5, // 阴影深度
    padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), // 内边距
    shape: RoundedRectangleBorder( // 形状
      borderRadius: BorderRadius.circular(8),
    ),
  ),
  child: const Text('确认提交'),
)

2. TextButton (文本按钮)

dart
TextButton(
  onPressed: () {},
  style: TextButton.styleFrom(
    foregroundColor: Colors.blue,
    padding: EdgeInsets.all(12),
  ),
  child: const Text('取消操作'),
)

3. OutlinedButton (描边按钮)

dart
OutlinedButton(
  onPressed: () {},
  style: OutlinedButton.styleFrom(
    side: BorderSide(color: Colors.blue, width: 2), // 边框样式
    shape: StadiumBorder(), // 圆角形状
    padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
  ),
  child: const Text('查看详情'),
)

4. IconButton (图标按钮)

dart
IconButton(
  onPressed: () {},
  icon: Icon(Icons.favorite),
  iconSize: 30,
  color: Colors.red,
  tooltip: '收藏', // 长按提示
)

5. FloatingActionButton (悬浮按钮)

dart
FloatingActionButton(
  onPressed: () {},
  backgroundColor: Colors.blue,
  foregroundColor: Colors.white,
  elevation: 6,
  shape: CircleBorder(), // 圆形
  child: Icon(Icons.add),
)

二、高级按钮组件

1. DropdownButton (下拉按钮)

dart
String? _selectedValue = '选项1';

DropdownButton<String>(
  value: _selectedValue,
  onChanged: (newValue) {
    setState(() => _selectedValue = newValue);
  },
  items: <String>['选项1', '选项2', '选项3']
      .map<DropdownMenuItem<String>>((String value) {
    return DropdownMenuItem<String>(
      value: value,
      child: Text(value),
    );
  }).toList(),
)

2. PopupMenuButton (弹出菜单按钮)

dart
PopupMenuButton<String>(
  icon: Icon(Icons.more_vert),
  itemBuilder: (context) => [
    PopupMenuItem(value: 'edit', child: Text('编辑')),
    PopupMenuItem(value: 'delete', child: Text('删除')),
  ],
  onSelected: (value) {
    if (value == 'delete') _deleteItem();
  },
)

3. ButtonBar (按钮组)

dart
ButtonBar(
  alignment: MainAxisAlignment.end, // 对齐方式
  children: [
    TextButton(onPressed: () {}, child: Text('取消')),
    ElevatedButton(onPressed: () {}, child: Text('确定')),
  ],
)

三、按钮状态管理

1. 禁用状态

dart
ElevatedButton(
  onPressed: isLoading ? null : _submitForm, // null表示禁用
  child: isLoading
      ? CircularProgressIndicator(color: Colors.white)
      : Text('提交'),
)

2. 加载状态

dart
bool _isLoading = false;

ElevatedButton(
  onPressed: _isLoading ? null : _fetchData,
  child: _isLoading
      ? SizedBox(
          width: 20,
          height: 20,
          child: CircularProgressIndicator(strokeWidth: 2),
        )
      : Text('加载数据'),
)

3. 按钮状态样式

dart
ElevatedButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.resolveWith<Color>(
      (Set<MaterialState> states) {
        if (states.contains(MaterialState.disabled)) {
          return Colors.grey; // 禁用状态
        }
        if (states.contains(MaterialState.pressed)) {
          return Colors.blue[700]!; // 按下状态
        }
        return Colors.blue; // 默认状态
      },
    ),
  ),
  // ...
)

四、自定义按钮

1. 使用 InkWell 创建自定义按钮

dart
InkWell(
  onTap: () {},
  borderRadius: BorderRadius.circular(8),
  splashColor: Colors.blue[100], // 水波纹颜色
  child: Container(
    padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
    decoration: BoxDecoration(
      color: Colors.blue[50],
      borderRadius: BorderRadius.circular(8),
      border: Border.all(color: Colors.blue),
    ),
    child: Text('自定义按钮'),
  ),
)

2. 渐变按钮

dart
ElevatedButton(
  style: ElevatedButton.styleFrom(
    padding: EdgeInsets.zero, // 清除默认内边距
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10),
    ),
  ),
  onPressed: () {},
  child: Ink(
    decoration: BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.blue, Colors.purple],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
      borderRadius: BorderRadius.circular(10),
    ),
    padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
    child: Text('渐变按钮', style: TextStyle(color: Colors.white)),
  ),
)

3. 图标+文字按钮

dart
ElevatedButton.icon(
  onPressed: () {},
  icon: Icon(Icons.cloud_upload),
  label: Text('上传文件'),
  style: ElevatedButton.styleFrom(
    padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
  ),
)

五、最佳实践指南

1. 按钮尺寸规范

dart
// 小尺寸按钮
TextButton(
  style: TextButton.styleFrom(padding: EdgeInsets.all(8)),
  child: Text('小按钮'),
)

// 中尺寸按钮(默认)
ElevatedButton(child: Text('中按钮'),)

// 大尺寸按钮
ElevatedButton(
  style: ElevatedButton.styleFrom(padding: EdgeInsets.all(16)),
  child: Text('大按钮'),
)

2. 响应式按钮设计

dart
LayoutBuilder(
  builder: (context, constraints) {
    bool isMobile = constraints.maxWidth < 600;
    return isMobile
        ? FloatingActionButton(onPressed: () {}, child: Icon(Icons.add))
        : ElevatedButton.icon(
            onPressed: () {},
            icon: Icon(Icons.add),
            label: Text('添加新项目'),
          );
  },
)

3. 按钮主题统一

dart
MaterialApp(
  theme: ThemeData(
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
        textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
      ),
    ),
    textButtonTheme: TextButtonThemeData(
      style: TextButton.styleFrom(
        foregroundColor: Colors.blue,
      ),
    ),
  ),
)

4. 按钮无障碍优化

dart
ElevatedButton(
  onPressed: () {},
  child: Text('确认'),
  autofocus: true, // 自动获取焦点
  focusNode: _focusNode, // 焦点控制
  onFocusChange: (hasFocus) {
    if (hasFocus) _announceAccessibility('确认按钮已聚焦');
  },
)

六、常见问题解决方案

1. 按钮点击无响应

dart
// 原因:onPressed为null
ElevatedButton(
  onPressed: _isEnabled ? _handlePress : null, // 正确管理状态
  // ...
)

2. 按钮样式不生效

dart
// 解决方案:使用styleFrom代替已弃用属性
ElevatedButton(
  style: ElevatedButton.styleFrom( // 正确方式
    backgroundColor: Colors.red,
  ),
  // ...
)

3. 按钮尺寸异常

dart
// 解决方案:包裹约束容器
SizedBox(
  width: double.infinity, // 撑满宽度
  height: 50, // 固定高度
  child: ElevatedButton(child: Text('登录')),
)

4. 按钮水波纹效果超出边界

dart
Material(
  borderRadius: BorderRadius.circular(10), // 设置裁剪边界
  child: InkWell(
    borderRadius: BorderRadius.circular(10), // 与Material一致
    onTap: () {},
    child: Container(
      padding: EdgeInsets.all(16),
      child: Text('自定义按钮'),
    ),
  ),
)

七、实际应用场景

1. 表单提交按钮组

dart
Column(
  children: [
    // 表单字段...
    ButtonBar(
      alignment: MainAxisAlignment.spaceBetween,
      children: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('取消'),
        ),
        ElevatedButton(
          onPressed: _submitForm,
          child: Text('保存设置'),
        ),
      ],
    ),
  ],
)

2. 购物车操作按钮

dart
Row(
  children: [
    IconButton(
      icon: Icon(Icons.favorite_border),
      onPressed: _toggleFavorite,
    ),
    SizedBox(width: 16),
    Expanded(
      child: ElevatedButton.icon(
        style: ElevatedButton.styleFrom(
          padding: EdgeInsets.symmetric(vertical: 16),
        ),
        icon: Icon(Icons.shopping_cart),
        label: Text('加入购物车'),
        onPressed: _addToCart,
      ),
    ),
  ],
)

3. 社交登录按钮组

dart
Column(
  children: [
    OutlinedButton.icon(
      icon: Image.asset('assets/google.png', width: 24),
      label: Text('使用Google登录'),
      onPressed: _signInWithGoogle,
    ),
    SizedBox(height: 12),
    OutlinedButton.icon(
      icon: Image.asset('assets/facebook.png', width: 24),
      label: Text('使用Facebook登录'),
      onPressed: _signInWithFacebook,
    ),
  ],
)

4. 多功能悬浮按钮

dart
FloatingActionButton(
  onPressed: () {},
  backgroundColor: Colors.blue,
  foregroundColor: Colors.white,
  child: Icon(Icons.add),
  heroTag: 'main_fab', // 唯一标识
)

// 次级悬浮按钮
ScaleTransition(
  scale: CurvedAnimation(
    parent: _animationController,
    curve: Curves.easeInOut,
  ),
  child: FloatingActionButton(
    heroTag: 'fab1', // 不同heroTag
    mini: true, // 小型按钮
    onPressed: () {},
    child: Icon(Icons.camera),
  ),
)

提示

  1. 使用 ButtonStyle 替代已弃用的按钮样式属性
  2. 对于重要操作,使用 ElevatedButton 提高视觉优先级
  3. 在长列表中使用 NotificationListener 优化滚动时按钮性能
  4. 使用 FocusFocusTraversalGroup 管理按钮焦点顺序
  5. 通过 MaterialStateProperty 创建响应不同状态的按钮样式
  6. 在暗色模式下使用 ThemeData 自动适配按钮颜色
  7. 为图标按钮添加 tooltip 属性提升无障碍访问体验

Wrap 组件

Flutter 的 Wrap 组件是一种流式布局(Flow Layout)工具,用于在空间不足时自动换行排列子组件,特别适合标签云、动态列表等场景。以下是其核心特性和使用指南:


属性说明默认值
direction主轴方向(Axis.horizontal 水平 / Axis.vertical 垂直)Axis.horizontal
alignment主轴对齐方式(如 WrapAlignment.start.center.spaceEvenlyWrapAlignment.start
spacing主轴方向子组件的间距0.0
runSpacing交叉轴方向行/列的间距(控制换行后的间隔)0.0
runAlignment交叉轴的行/列对齐方式(需父容器高度足够才生效)WrapAlignment.start
crossAxisAlignment子组件在交叉轴的对齐方式(如 WrapCrossAlignment.startWrapCrossAlignment.start
children子组件列表必填

💡 关键特性
当主轴空间不足时,Wrap 会自动换行(水平布局换行,垂直布局换列),而 Row/Column 会溢出报错。


🛠️ 二、基本用法示例

水平标签布局(自动换行)

dart
Wrap(
  spacing: 10.0,      // 水平间距
  runSpacing: 15.0,   // 行间距
  alignment: WrapAlignment.spaceEvenly,
  children: List.generate(10, (index) =>
    Chip(
      label: Text("标签${index + 1}"),
      backgroundColor: Colors.blue[100],
    ),
  ),
)

垂直流式布局

dart
Wrap(
  direction: Axis.vertical, // 垂直主轴
  runSpacing: 20.0,
  children: [
    Icon(Icons.star),
    Icon(Icons.favorite),
    // 更多子组件...
  ],
)

⚙️ 三、高级技巧

1. 响应式布局

根据屏幕宽度动态切换方向:

dart
Wrap(
  direction: MediaQuery.of(context).size.width > 600
      ? Axis.horizontal
      : Axis.vertical,
  children: [/*...*/],
)

2. 嵌套组合

嵌套 Wrap 实现复杂流式结构:

dart
Wrap(
  children: [
    Wrap( // 内层垂直Wrap
      direction: Axis.vertical,
      children: [Chip(label: Text("A")), Chip(label: Text("B"))],
    ),
    Chip(label: Text("主内容区")),
  ],
)

3. 与状态管理结合

动态添加/删除标签:

dart
List<String> tags = ["Flutter", "Dart"];
Wrap(
  children: tags.map((tag) =>
    InputChip(
      label: Text(tag),
      onDeleted: () => setState(() => tags.remove(tag)),
    ),
  ).toList(),
)

⚠️ 四、常见问题解决

1. 换行后行对齐不生效

原因runAlignment 需父容器有额外空间才能生效。
方案:为父容器设置足够高度:

dart
Container(
  height: 300, // 明确高度
  child: Wrap(runAlignment: WrapAlignment.end, children: [...]),
)

2. 子组件间距异常

  • 使用 spacing 控制主轴间距,runSpacing 控制行间距。
  • 避免子组件自身外边距(如 Padding),优先用 Wrap 的间距属性。

3. 性能优化

  • 子组件数量过大时,用 Wrap 替代嵌套 Row/Column 避免布局溢出。
  • 复杂子组件提取为 const 或独立 Widget 减少重建。

🎯 五、应用场景

1. 标签选择器

dart
Wrap(
  spacing: 8.0,
  children: ["喜剧", "动作", "科幻"].map((genre) =>
    FilterChip(
      label: Text(genre),
      selected: _selectedGenre == genre,
      onSelected: (v) => setState(() => _selectedGenre = genre),
    ),
  ).toList(),
)

2. 自适应按钮组

dart
Wrap(
  runSpacing: 10.0,
  children: [
    ElevatedButton(onPressed: () {}, child: Text("保存")),
    OutlinedButton(onPressed: () {}, child: Text("取消")),
    // 更多按钮...
  ],
)

3. 瀑布流图片布局

dart
Wrap(
  spacing: 5.0,
  runSpacing: 5.0,
  children: imageUrls.map((url) =>
    Image.network(url, width: 100, height: 100, fit: BoxFit.cover),
  ).toList(),
)

🔍 六、对比其他布局

组件特点适用场景
Wrap自动换行,简单易用标签、动态内容流
Flow精确控制子组件位置,性能更高但代码复杂自定义复杂流式布局
Row/Column单行/列布局,溢出报错线性固定内容

💡 提示
对于大多数场景,Wrap 的简洁性和自动换行特性已足够;需要像素级控制时(如重叠布局),再考虑 Flow


💎 综合示例

dart
Wrap(
  spacing: 12,
  runSpacing: 16,
  alignment: WrapAlignment.center,
  children: [
    _buildCategory("音乐", Icons.music_note),
    _buildCategory("电影", Icons.movie),
    // 添加更多分类...
  ],
)

Widget _buildCategory(String name, IconData icon) {
  return Column(
    children: [
      Icon(icon, size: 36),
      SizedBox(height: 8),
      Text(name),
    ],
  );
}

通过灵活组合 Wrap 的属性,可轻松实现各类自适应布局,尤其适合内容动态生成的场景(如搜索关键词、用户兴趣标签等)。