【第二阶段-Flutter入门】第四章:常用布局Widget

文章目录

      • [2.1 ListView - 就像滚动的清单](#2.1 ListView - 就像滚动的清单)
      • [2.2 GridView - 就像网格展示](#2.2 GridView - 就像网格展示)
      • [2.3 Stack - 就像叠放的卡片](#2.3 Stack - 就像叠放的卡片)
      • [2.4 Wrap - 就像自动换行的文字](#2.4 Wrap - 就像自动换行的文字)
    • [🎯 实践练习:创建一个完整的应用界面](#🎯 实践练习:创建一个完整的应用界面)
    • [📚 学习总结](#📚 学习总结)
      • [🧱 常用布局Widget](#🧱 常用布局Widget)
      • [🎨 实践能力](#🎨 实践能力)
      • [🚀 下一步](#🚀 下一步)

2.1 ListView - 就像滚动的清单

ListView就像一个可以滚动的清单,比如购物清单、联系人列表、新闻列表等。

dart 复制代码
class ListViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('列表展示大全')),
      body: DefaultTabController(
        length: 4,
        child: Column(
          children: [
            TabBar(
              labelColor: Colors.blue,
              unselectedLabelColor: Colors.grey,
              tabs: [
                Tab(text: '基础列表'),
                Tab(text: '分隔线'),
                Tab(text: '构建器'),
                Tab(text: '自定义'),
              ],
            ),
            Expanded(
              child: TabBarView(
                children: [
                  _buildBasicListView(),
                  _buildSeparatedListView(),
                  _buildBuilderListView(),
                  _buildCustomListView(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
  
  // 基础列表 - 就像简单的购物清单
  Widget _buildBasicListView() {
    return ListView(
      padding: EdgeInsets.all(16),
      children: [
        ListTile(
          leading: Icon(Icons.shopping_cart),
          title: Text('苹果'),
          subtitle: Text('新鲜水果'),
          trailing: Text('¥5.00'),
        ),
        ListTile(
          leading: Icon(Icons.shopping_cart),
          title: Text('香蕉'),
          subtitle: Text('进口水果'),
          trailing: Text('¥3.00'),
        ),
        ListTile(
          leading: Icon(Icons.shopping_cart),
          title: Text('橙子'),
          subtitle: Text('维C丰富'),
          trailing: Text('¥4.00'),
        ),
        // 也可以放其他Widget
        Container(
          height: 100,
          margin: EdgeInsets.symmetric(vertical: 8),
          decoration: BoxDecoration(
            color: Colors.blue[100],
            borderRadius: BorderRadius.circular(8),
          ),
          child: Center(
            child: Text('这是一个自定义容器'),
          ),
        ),
      ],
    );
  }
  
  // 带分隔线的列表 - 就像有分割线的笔记本
  Widget _buildSeparatedListView() {
    List<String> items = List.generate(20, (index) => '项目 ${index + 1}');
    
    return ListView.separated(
      padding: EdgeInsets.all(16),
      itemCount: items.length,
      separatorBuilder: (context, index) => Divider(), // 分隔线
      itemBuilder: (context, index) {
        return ListTile(
          leading: CircleAvatar(
            child: Text('${index + 1}'),
          ),
          title: Text(items[index]),
          subtitle: Text('这是第${index + 1}个项目的描述'),
          onTap: () {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('点击了${items[index]}')),
            );
          },
        );
      },
    );
  }
  
  // 构建器列表 - 就像按需制作的清单
  Widget _buildBuilderListView() {
    return ListView.builder(
      padding: EdgeInsets.all(16),
      itemCount: 1000, // 可以是很大的数字
      itemBuilder: (context, index) {
        return Card(
          child: ListTile(
            leading: Icon(
              index % 2 == 0 ? Icons.star : Icons.favorite,
              color: index % 2 == 0 ? Colors.orange : Colors.red,
            ),
            title: Text('动态项目 $index'),
            subtitle: Text('这是第$index个动态生成的项目'),
            trailing: IconButton(
              icon: Icon(Icons.more_vert),
              onPressed: () {
                _showItemMenu(context, index);
              },
            ),
          ),
        );
      },
    );
  }
  
  // 自定义列表项 - 就像精心设计的名片
  Widget _buildCustomListView() {
    List<Map<String, dynamic>> contacts = [
      {'name': '张三', 'phone': '138****1234', 'avatar': Icons.person, 'color': Colors.blue},
      {'name': '李四', 'phone': '139****5678', 'avatar': Icons.person_outline, 'color': Colors.green},
      {'name': '王五', 'phone': '137****9012', 'avatar': Icons.account_circle, 'color': Colors.orange},
      {'name': '赵六', 'phone': '136****3456', 'avatar': Icons.face, 'color': Colors.purple},
    ];
    
    return ListView.builder(
      padding: EdgeInsets.all(16),
      itemCount: contacts.length,
      itemBuilder: (context, index) {
        final contact = contacts[index];
        return Container(
          margin: EdgeInsets.only(bottom: 12),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(12),
            boxShadow: [
              BoxShadow(
                color: Colors.grey.withOpacity(0.2),
                spreadRadius: 1,
                blurRadius: 4,
                offset: Offset(0, 2),
              ),
            ],
          ),
          child: ListTile(
            contentPadding: EdgeInsets.all(16),
            leading: CircleAvatar(
              backgroundColor: contact['color'],
              child: Icon(contact['avatar'], color: Colors.white),
            ),
            title: Text(
              contact['name'],
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            subtitle: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                SizedBox(height: 4),
                Text(contact['phone']),
                SizedBox(height: 4),
                Text(
                  '在线',
                  style: TextStyle(color: Colors.green, fontSize: 12),
                ),
              ],
            ),
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  icon: Icon(Icons.call, color: Colors.green),
                  onPressed: () => _makeCall(contact['name']),
                ),
                IconButton(
                  icon: Icon(Icons.message, color: Colors.blue),
                  onPressed: () => _sendMessage(contact['name']),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
  
  void _showItemMenu(BuildContext context, int index) {
    showModalBottomSheet(
      context: context,
      builder: (context) => Container(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ListTile(
              leading: Icon(Icons.edit),
              title: Text('编辑'),
              onTap: () => Navigator.pop(context),
            ),
            ListTile(
              leading: Icon(Icons.delete),
              title: Text('删除'),
              onTap: () => Navigator.pop(context),
            ),
          ],
        ),
      ),
    );
  }
  
  void _makeCall(String name) {
    print('拨打电话给 $name');
  }
  
  void _sendMessage(String name) {
    print('发送消息给 $name');
  }
}

2.2 GridView - 就像网格展示

GridView就像商品展示柜、相册、应用图标网格等。

dart 复制代码
class GridViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('网格展示大全')),
      body: DefaultTabController(
        length: 3,
        child: Column(
          children: [
            TabBar(
              labelColor: Colors.blue,
              unselectedLabelColor: Colors.grey,
              tabs: [
                Tab(text: '固定列数'),
                Tab(text: '固定大小'),
                Tab(text: '瀑布流'),
              ],
            ),
            Expanded(
              child: TabBarView(
                children: [
                  _buildCountGridView(),
                  _buildExtentGridView(),
                  _buildStaggeredGridView(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
  
  // 固定列数网格 - 就像整齐的商品陈列
  Widget _buildCountGridView() {
    return GridView.count(
      padding: EdgeInsets.all(16),
      crossAxisCount: 2, // 每行2个
      crossAxisSpacing: 10, // 水平间距
      mainAxisSpacing: 10, // 垂直间距
      childAspectRatio: 0.8, // 宽高比
      children: List.generate(20, (index) {
        return Container(
          decoration: BoxDecoration(
            color: Colors.primaries[index % Colors.primaries.length],
            borderRadius: BorderRadius.circular(12),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                Icons.shopping_bag,
                size: 40,
                color: Colors.white,
              ),
              SizedBox(height: 8),
              Text(
                '商品 ${index + 1}',
                style: TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                ),
              ),
              Text(
                '¥${(index + 1) * 10}',
                style: TextStyle(
                  color: Colors.white70,
                ),
              ),
            ],
          ),
        );
      }),
    );
  }
  
  // 固定大小网格 - 就像固定尺寸的相框
  Widget _buildExtentGridView() {
    return GridView.extent(
      padding: EdgeInsets.all(16),
      maxCrossAxisExtent: 150, // 最大宽度150
      crossAxisSpacing: 10,
      mainAxisSpacing: 10,
      children: List.generate(30, (index) {
        return Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: [
                Colors.blue[300]!,
                Colors.blue[600]!,
              ],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
            ),
            borderRadius: BorderRadius.circular(8),
          ),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.photo, size: 30, color: Colors.white),
                SizedBox(height: 4),
                Text(
                  '照片 $index',
                  style: TextStyle(color: Colors.white),
                ),
              ],
            ),
          ),
        );
      }),
    );
  }
  
  // 瀑布流网格 - 就像Pinterest的布局
  Widget _buildStaggeredGridView() {
    return GridView.builder(
      padding: EdgeInsets.all(16),
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        crossAxisSpacing: 10,
        mainAxisSpacing: 10,
        childAspectRatio: 0.7,
      ),
      itemCount: 20,
      itemBuilder: (context, index) {
        // 随机高度,模拟瀑布流效果
        double height = 150 + (index % 3) * 50.0;
        
        return Container(
          height: height,
          decoration: BoxDecoration(
            color: Colors.primaries[index % Colors.primaries.length].withOpacity(0.3),
            borderRadius: BorderRadius.circular(12),
            border: Border.all(
              color: Colors.primaries[index % Colors.primaries.length],
              width: 2,
            ),
          ),
          child: Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Icon(
                  Icons.image,
                  size: 30,
                  color: Colors.primaries[index % Colors.primaries.length],
                ),
                SizedBox(height: 8),
                Text(
                  '卡片 ${index + 1}',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 16,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  '这是第${index + 1}个卡片的描述内容,高度会根据内容自动调整。',
                  style: TextStyle(
                    color: Colors.grey[600],
                    fontSize: 12,
                  ),
                ),
                Spacer(),
                Row(
                  children: [
                    Icon(Icons.favorite_border, size: 16),
                    SizedBox(width: 4),
                    Text('${index + 1}'),
                    Spacer(),
                    Icon(Icons.share, size: 16),
                  ],
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

2.3 Stack - 就像叠放的卡片

Stack就像把多张卡片叠在一起,可以创建层叠效果。

dart 复制代码
class StackExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('层叠布局演示')),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            // 基础层叠 - 就像叠放的照片
            Text('基础层叠:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Container(
              height: 200,
              child: Stack(
                children: [
                  // 底层 - 背景
                  Container(
                    width: double.infinity,
                    height: double.infinity,
                    decoration: BoxDecoration(
                      gradient: LinearGradient(
                        colors: [Colors.blue[300]!, Colors.blue[600]!],
                      ),
                      borderRadius: BorderRadius.circular(12),
                    ),
                  ),
                  
                  // 中层 - 装饰
                  Positioned(
                    top: 20,
                    right: 20,
                    child: Container(
                      width: 60,
                      height: 60,
                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.3),
                        shape: BoxShape.circle,
                      ),
                    ),
                  ),
                  
                  // 顶层 - 内容
                  Positioned(
                    bottom: 20,
                    left: 20,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          '层叠标题',
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 20,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        Text(
                          '这是层叠在背景上的文字',
                          style: TextStyle(color: Colors.white70),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
            
            SizedBox(height: 32),
            
            // 用户头像卡片 - 实际应用示例
            Text('用户头像卡片:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Container(
              height: 180,
              child: Stack(
                children: [
                  // 背景卡片
                  Container(
                    margin: EdgeInsets.only(top: 40),
                    padding: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(16),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.grey.withOpacity(0.3),
                          spreadRadius: 2,
                          blurRadius: 8,
                          offset: Offset(0, 4),
                        ),
                      ],
                    ),
                    child: Column(
                      children: [
                        Text(
                          '张三',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        SizedBox(height: 4),
                        Text(
                          'Flutter开发工程师',
                          style: TextStyle(
                            color: Colors.grey[600],
                            fontSize: 14,
                          ),
                        ),
                        SizedBox(height: 16),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            _buildStatItem('项目', '12'),
                            _buildStatItem('经验', '3年'),
                            _buildStatItem('评分', '4.8'),
                          ],
                        ),
                      ],
                    ),
                  ),
                  
                  // 头像(浮在卡片上方)
                  Positioned(
                    top: 0,
                    left: 0,
                    right: 0,
                    child: Center(
                      child: Container(
                        width: 80,
                        height: 80,
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          border: Border.all(color: Colors.white, width: 4),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.grey.withOpacity(0.3),
                              spreadRadius: 2,
                              blurRadius: 8,
                            ),
                          ],
                        ),
                        child: CircleAvatar(
                          radius: 36,
                          backgroundColor: Colors.blue,
                          child: Icon(Icons.person, size: 40, color: Colors.white),
                        ),
                      ),
                    ),
                  ),
                  
                  // 在线状态指示器
                  Positioned(
                    top: 50,
                    left: 0,
                    right: -30,
                    child: Center(
                      child: Container(
                        width: 20,
                        height: 20,
                        decoration: BoxDecoration(
                          color: Colors.green,
                          shape: BoxShape.circle,
                          border: Border.all(color: Colors.white, width: 2),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            
            SizedBox(height: 32),
            
            // 商品卡片 - 带标签的示例
            Text('商品卡片:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Container(
              height: 200,
              child: Stack(
                children: [
                  // 主卡片
                  Container(
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(12),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.grey.withOpacity(0.2),
                          spreadRadius: 1,
                          blurRadius: 4,
                          offset: Offset(0, 2),
                        ),
                      ],
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        // 商品图片区域
                        Container(
                          height: 120,
                          decoration: BoxDecoration(
                            color: Colors.grey[200],
                            borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
                          ),
                          child: Center(
                            child: Icon(Icons.image, size: 50, color: Colors.grey),
                          ),
                        ),
                        
                        // 商品信息
                        Padding(
                          padding: EdgeInsets.all(12),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                'iPhone 15 Pro',
                                style: TextStyle(
                                  fontWeight: FontWeight.bold,
                                  fontSize: 16,
                                ),
                              ),
                              SizedBox(height: 4),
                              Text(
                                '¥8999',
                                style: TextStyle(
                                  color: Colors.red,
                                  fontSize: 18,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                  
                  // 折扣标签
                  Positioned(
                    top: 8,
                    left: 8,
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                      decoration: BoxDecoration(
                        color: Colors.red,
                        borderRadius: BorderRadius.circular(4),
                      ),
                      child: Text(
                        '8折',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 12,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                  
                  // 收藏按钮
                  Positioned(
                    top: 8,
                    right: 8,
                    child: Container(
                      width: 32,
                      height: 32,
                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.9),
                        shape: BoxShape.circle,
                      ),
                      child: Icon(
                        Icons.favorite_border,
                        size: 20,
                        color: Colors.grey[600],
                      ),
                    ),
                  ),
                  
                  // 新品标签
                  Positioned(
                    top: 50,
                    left: 8,
                    child: Container(
                      padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
                      decoration: BoxDecoration(
                        color: Colors.orange,
                        borderRadius: BorderRadius.circular(3),
                      ),
                      child: Text(
                        'NEW',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 10,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
  
  Widget _buildStatItem(String label, String value) {
    return Column(
      children: [
        Text(
          value,
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
            color: Colors.blue,
          ),
        ),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }
}

2.4 Wrap - 就像自动换行的文字

Wrap就像写文章时的自动换行,当一行放不下时会自动换到下一行。

dart 复制代码
class WrapExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('自动换行布局')),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 标签云 - 就像文章的标签
            Text('标签云:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Wrap(
              spacing: 8, // 水平间距
              runSpacing: 8, // 垂直间距
              children: [
                'Flutter', 'Dart', 'Mobile', 'iOS', 'Android', 
                'Web', 'Desktop', 'UI', 'UX', 'Design',
                'Development', 'Programming', 'Code', 'App'
              ].map((tag) => Chip(
                label: Text(tag),
                backgroundColor: Colors.blue[100],
                labelStyle: TextStyle(color: Colors.blue[800]),
              )).toList(),
            ),
            
            SizedBox(height: 32),
            
            // 颜色选择器 - 就像调色板
            Text('颜色选择器:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Wrap(
              spacing: 12,
              runSpacing: 12,
              children: Colors.primaries.map((color) => GestureDetector(
                onTap: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('选择了颜色: ${color.toString()}')),
                  );
                },
                child: Container(
                  width: 40,
                  height: 40,
                  decoration: BoxDecoration(
                    color: color,
                    shape: BoxShape.circle,
                    border: Border.all(color: Colors.grey[300]!, width: 2),
                  ),
                ),
              )).toList(),
            ),
            
            SizedBox(height: 32),
            
            // 尺寸选择 - 就像服装尺码
            Text('尺寸选择:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: ['XS', 'S', 'M', 'L', 'XL', 'XXL'].map((size) => 
                Container(
                  padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.grey),
                    borderRadius: BorderRadius.circular(20),
                  ),
                  child: Text(size),
                )
              ).toList(),
            ),
            
            SizedBox(height: 32),
            
            // 功能按钮组 - 就像工具栏
            Text('功能按钮组:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                {'icon': Icons.copy, 'label': '复制'},
                {'icon': Icons.cut, 'label': '剪切'},
                {'icon': Icons.paste, 'label': '粘贴'},
                {'icon': Icons.undo, 'label': '撤销'},
                {'icon': Icons.redo, 'label': '重做'},
                {'icon': Icons.save, 'label': '保存'},
                {'icon': Icons.print, 'label': '打印'},
                {'icon': Icons.share, 'label': '分享'},
              ].map((item) => ElevatedButton.icon(
                onPressed: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('点击了${item['label']}')),
                  );
                },
                icon: Icon(item['icon'] as IconData, size: 16),
                label: Text(item['label'] as String),
                style: ElevatedButton.styleFrom(
                  padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                ),
              )).toList(),
            ),
            
            SizedBox(height: 32),
            
            // 联系人头像 - 就像群聊头像
            Text('联系人头像:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: List.generate(15, (index) => CircleAvatar(
                radius: 25,
                backgroundColor: Colors.primaries[index % Colors.primaries.length],
                child: Text(
                  String.fromCharCode(65 + index), // A, B, C...
                  style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
                ),
              )),
            ),
            
            SizedBox(height: 32),
            
            // 自定义间距的Wrap
            Text('自定义间距:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 16),
            
            Container(
              padding: EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.grey[100],
                borderRadius: BorderRadius.circular(8),
              ),
              child: Wrap(
                spacing: 20, // 更大的水平间距
                runSpacing: 15, // 更大的垂直间距
                alignment: WrapAlignment.center, // 居中对齐
                children: [
                  '居中', '对齐', '的', '标签', '会', '自动', '换行', '并且', '保持', '居中'
                ].map((text) => Container(
                  padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                  decoration: BoxDecoration(
                    color: Colors.blue[200],
                    borderRadius: BorderRadius.circular(15),
                  ),
                  child: Text(text),
                )).toList(),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

🎯 实践练习:创建一个完整的应用界面

让我们综合运用所学的布局知识,创建一个仿微信朋友圈的界面:

dart 复制代码
class SocialMediaApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '社交媒体',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: SocialMediaHome(),
    );
  }
}

class SocialMediaHome extends StatelessWidget {
  final List<Map<String, dynamic>> posts = [
    {
      'user': '张三',
      'avatar': Icons.person,
      'time': '2小时前',
      'content': '今天天气真不错,出来散散步,心情都变好了! 🌞',
      'images': 3,
      'likes': 15,
      'comments': 3,
    },
    {
      'user': '李四',
      'avatar': Icons.face,
      'time': '4小时前',
      'content': '刚刚完成了一个Flutter项目,感觉Flutter真的很棒!分享一些开发心得...',
      'images': 1,
      'likes': 28,
      'comments': 7,
    },
    {
      'user': '王五',
      'avatar': Icons.account_circle,
      'time': '6小时前',
      'content': '周末和朋友们一起去爬山,虽然累但是很开心!',
      'images': 4,
      'likes': 42,
      'comments': 12,
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('朋友圈'),
        backgroundColor: Colors.green[600],
        actions: [
          IconButton(
            icon: Icon(Icons.camera_alt),
            onPressed: () {},
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: posts.length,
        itemBuilder: (context, index) {
          return _buildPostCard(context, posts[index]);
        },
      ),
    );
  }

  Widget _buildPostCard(BuildContext context, Map<String, dynamic> post) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 4),
      padding: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border(
          bottom: BorderSide(color: Colors.grey[200]!, width: 1),
        ),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 用户信息行
          Row(
            children: [
              // 头像
              CircleAvatar(
                radius: 25,
                backgroundColor: Colors.blue,
                child: Icon(post['avatar'], color: Colors.white),
              ),
              
              SizedBox(width: 12),
              
              // 用户名和时间
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      post['user'],
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                    SizedBox(height: 2),
                    Text(
                      post['time'],
                      style: TextStyle(
                        color: Colors.grey[600],
                        fontSize: 12,
                      ),
                    ),
                  ],
                ),
              ),
              
              // 更多按钮
              IconButton(
                icon: Icon(Icons.more_horiz, color: Colors.grey),
                onPressed: () {},
              ),
            ],
          ),
          
          SizedBox(height: 12),
          
          // 文字内容
          Text(
            post['content'],
            style: TextStyle(fontSize: 15, height: 1.4),
          ),
          
          SizedBox(height: 12),
          
          // 图片网格
          if (post['images'] > 0) _buildImageGrid(post['images']),
          
          SizedBox(height: 12),
          
          // 互动按钮行
          Row(
            children: [
              // 点赞按钮
              Expanded(
                child: _buildActionButton(
                  icon: Icons.thumb_up_outlined,
                  label: '${post['likes']}',
                  onTap: () {},
                ),
              ),
              
              // 评论按钮
              Expanded(
                child: _buildActionButton(
                  icon: Icons.comment_outlined,
                  label: '${post['comments']}',
                  onTap: () {},
                ),
              ),
              
              // 分享按钮
              Expanded(
                child: _buildActionButton(
                  icon: Icons.share_outlined,
                  label: '分享',
                  onTap: () {},
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  Widget _buildImageGrid(int imageCount) {
    if (imageCount == 1) {
      // 单张图片
      return Container(
        height: 200,
        decoration: BoxDecoration(
          color: Colors.grey[200],
          borderRadius: BorderRadius.circular(8),
        ),
        child: Center(
          child: Icon(Icons.image, size: 50, color: Colors.grey),
        ),
      );
    } else if (imageCount <= 4) {
      // 2-4张图片,使用2x2网格
      return GridView.count(
        shrinkWrap: true,
        physics: NeverScrollableScrollPhysics(),
        crossAxisCount: 2,
        crossAxisSpacing: 4,
        mainAxisSpacing: 4,
        childAspectRatio: 1,
        children: List.generate(imageCount, (index) => Container(
          decoration: BoxDecoration(
            color: Colors.grey[200],
            borderRadius: BorderRadius.circular(4),
          ),
          child: Center(
            child: Icon(Icons.image, color: Colors.grey),
          ),
        )),
      );
    } else {
      // 5张以上图片,使用3x3网格
      return GridView.count(
        shrinkWrap: true,
        physics: NeverScrollableScrollPhysics(),
        crossAxisCount: 3,
        crossAxisSpacing: 4,
        mainAxisSpacing: 4,
        childAspectRatio: 1,
        children: List.generate(
          imageCount > 9 ? 9 : imageCount,
          (index) => Container(
            decoration: BoxDecoration(
              color: Colors.grey[200],
              borderRadius: BorderRadius.circular(4),
            ),
            child: Stack(
              children: [
                Center(
                  child: Icon(Icons.image, color: Colors.grey),
                ),
                if (index == 8 && imageCount > 9)
                  Container(
                    decoration: BoxDecoration(
                      color: Colors.black.withOpacity(0.5),
                      borderRadius: BorderRadius.circular(4),
                    ),
                    child: Center(
                      child: Text(
                        '+${imageCount - 9}',
                        style: TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ),
              ],
            ),
          ),
        ),
      );
    }
  }

  Widget _buildActionButton({
    required IconData icon,
    required String label,
    required VoidCallback onTap,
  }) {
    return InkWell(
      onTap: onTap,
      child: Padding(
        padding: EdgeInsets.symmetric(vertical: 8),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon, size: 20, color: Colors.grey[600]),
            SizedBox(width: 4),
            Text(
              label,
              style: TextStyle(
                color: Colors.grey[600],
                fontSize: 14,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

📚 学习总结

通过这一章的学习,你已经掌握了:

🧱 常用布局Widget

  1. ListView:创建可滚动的列表
  2. GridView:创建网格布局
  3. Stack:创建层叠布局
  4. Wrap:创建自动换行布局

🎨 实践能力

  1. 组合布局:能够组合多种布局创建复杂界面
  2. 响应式设计:理解如何适配不同屏幕尺寸
  3. 实际应用:完成了社交媒体界面的制作

🚀 下一步

接下来我们将学习状态管理,了解如何让应用变得更加动态和交互性更强。

每篇文章并不长,这个系列是由浅到深来设计的。如果文章对您有帮助,麻烦动动发财的小手点赞、关注和收藏,您的反馈将是作者不断更新的动力🙏🏻

相关推荐
QuantumLeap丶2 小时前
《Flutter全栈开发实战指南:从零到高级》- 13 -状态管理GetX
android·flutter·ios·前端框架
woshijunjunstudy5 小时前
Flutter .obx 与 Rxn<T>的区别
flutter·getx
初遇你时动了情10 小时前
flutter vscode 终端无法使用fvm 版本切换、项目运行
ide·vscode·flutter
一叶难遮天1 天前
Dart语言之面向对象
flutter·面向对象·dart·方法·mixins·泛型·抽象类
汤面不加鱼丸1 天前
flutter实践:DropdownButton2使用示例
flutter
心随雨下1 天前
Flutter Material 3设计语言详解
javascript·flutter·设计语言
猫林老师1 天前
Flutter for HarmonyOS开发指南(六):测试、调试与质量保障体系
flutter·华为·harmonyos
stringwu1 天前
Flutter DevTools 全景介绍
flutter