Flutter 鸿蒙应用开发进阶组件实战宝典:从页面切换到数据表格【跨平台技术在开源鸿蒙中的使用】

在 Flutter 鸿蒙应用开发中,基础组件搭建界面骨架,而进阶组件则是实现“多页面滑动切换”“底部弹出交互”“结构化数据展示”等核心场景的关键。本文聚焦 3 类企业级开发高频进阶组件——PageView 页面切换、BottomSheet 底部弹窗、DataTable 数据表格,通过“核心原理拆解 + 关键属性解析 + 完整实战案例”的形式,帮助开发者攻克复杂交互与数据展示难题,提升应用质感与开发效率。

一、PageView:实现流畅的页面切换体验

PageView 是 Flutter 提供的页面级滚动组件,核心用于实现“引导页”“首页轮播”“多标签内容切换”等场景,支持垂直/水平滚动、页面缓存、滑动监听等核心能力,配合 PageController 可实现精准的页面控制。

1. 基础用法:水平分页切换

PageView 基础用法通过构建子页面列表实现分页展示,默认支持水平滑动,配合 PageIndicator 可提升用户体验。核心依赖 PageView.builder 实现懒加载,避免一次性构建所有页面导致的性能问题。

实战代码:基础引导页实现

需求:实现 3 页引导页,支持滑动切换,最后一页显示“进入应用”按钮。

class GuidePageDemo extends StatefulWidget {
  const GuidePageDemo({super.key});

  
  State<GuidePageDemo> createState() => _GuidePageDemoState();
}

class _GuidePageDemoState extends State<GuidePageDemo> {
  // 页面控制器:控制页面切换、获取当前页码
  final PageController _pageController = PageController(initialPage: 0);
  // 引导页数据:图片+描述
  final List<Map<String, String>> _guideData = [
    {
      "img": "https://picsum.photos/800/1200?1",
      "desc": "高效开发 跨平台适配"
    },
    {
      "img": "https://picsum.photos/800/1200?2",
      "desc": "原生质感 流畅体验"
    },
    {
      "img": "https://picsum.photos/800/1200?3",
      "desc": "一键发布 多端兼容"
    }
  ];
  // 当前页码
  int _currentPage = 0;

  
  void initState() {
    super.initState();
    // 监听页面切换事件
    _pageController.addListener(() {
      setState(() {
        // 取整数页码,避免滑动过程中页码波动
        _currentPage = _pageController.page!.round();
      });
    });
  }

  
  void dispose() {
    // 销毁控制器,避免内存泄漏
    _pageController.dispose();
    super.dispose();
  }

  // 进入应用逻辑
  void _enterApp() {
    // 实际开发中可替换为跳转到首页的逻辑
    print("进入应用");
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 分页内容
          PageView.builder(
            controller: _pageController,
            itemCount: _guideData.length,
            // 禁止页面回弹(提升原生质感)
            physics: const ClampingScrollPhysics(),
            itemBuilder: (context, index) {
              final item = _guideData[index];
              return Column(
                children: [
                  // 引导图(占满屏幕大部分空间)
                  Expanded(
                    child: Image.network(
                      item["img"]!,
                      fit: BoxFit.cover,
                      width: double.infinity,
                    ),
                  ),
                  // 描述文本
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 30),
                    child: Text(
                      item["desc"]!,
                      style: const TextStyle(fontSize: 18, fontWeight: 500),
                    ),
                  ),
                  // 最后一页显示进入按钮
                  if (index == _guideData.length - 1)
                    Padding(
                      padding: const EdgeInsets.only(bottom: 60),
                      child: ElevatedButton(
                        onPressed: _enterApp,
                        style: ElevatedButton.styleFrom(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 60,
                            vertical: 12,
                          ),
                          textStyle: const TextStyle(fontSize: 16),
                        ),
                        child: const Text("进入应用"),
                      ),
                    )
                ],
              );
            },
          ),
          // 页码指示器(固定在底部)
          Positioned(
            bottom: 40,
            left: 0,
            right: 0,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(_guideData.length, (index) {
                return Container(
                  width: _currentPage == index ? 16 : 8,
                  height: 8,
                  margin: const EdgeInsets.symmetric(horizontal: 4),
                  decoration: BoxDecoration(
                    color: _currentPage == index ? Colors.blue : Colors.grey[300],
                    borderRadius: BorderRadius.circular(4),
                  ),
                );
              }),
            ),
          )
        ],
      ),
    );
  }
}

关键注意点

  • 控制器生命周期管理:PageController 必须在 dispose 中销毁,否则会导致内存泄漏,这是新手常犯的错误。

  • 页面懒加载:使用 PageView.builder 而非 PageView 构造函数,仅当页面即将显示时才会构建,提升初始化性能。

  • 滚动物理效果:默认使用 BouncingScrollPhysics(回弹效果),iOS 风格适配良好;Android 可使用 ClampingScrollPhysics 实现无回弹的生硬停止效果。

2. 进阶用法:结合 TabBar 实现标签切换

实际开发中常需要“顶部标签 + 底部页面”的组合布局(如首页分类切换),通过 TabController 关联 TabBar 和 PageView,实现标签与页面的同步切换。

实战代码:TabBar + PageView 组合布局

class TabPageDemo extends StatefulWidget {
  const TabPageDemo({super.key});

  
  State<TabPageDemo> createState() => _TabPageDemoState();
}

class _TabPageDemoState extends State<TabPageDemo> with SingleTickerProviderStateMixin {
  late TabController _tabController;
  // 标签数据
  final List<String> _tabs = ["推荐", "热点", "科技", "娱乐", "体育"];

  
  void initState() {
    super.initState();
    // 初始化标签控制器:参数为标签数量和动画提供者
    _tabController = TabController(
      length: _tabs.length,
      vsync: this,
      initialIndex: 0,
    );
  }

  
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  // 构建每个标签对应的页面
  Widget _buildTabPage(String tabName) {
    return Center(
      child: Text(
        "$tabName 页面",
        style: const TextStyle(fontSize: 20),
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("标签页切换"),
        // 顶部标签栏
        bottom: TabBar(
          controller: _tabController,
          // 标签过多时可滚动
          isScrollable: true,
          // 选中标签样式
          labelStyle: const TextStyle(fontSize: 16, fontWeight: 500),
          labelColor: Colors.white,
          // 未选中标签样式
          unselectedLabelStyle: const TextStyle(fontSize: 14),
          unselectedLabelColor: Colors.grey[200],
          // 下划线样式
          indicatorColor: Colors.white,
          indicatorWeight: 3,
          indicatorSize: TabIndicatorSize.label,
          tabs: _tabs.map((tab) => Tab(text: tab)).toList(),
        ),
      ),
      // 页面内容
      body: PageView.builder(
        controller: PageController(initialPage: 0),
        // 页面切换时同步更新标签
        onPageChanged: (index) => _tabController.animateTo(index),
        itemCount: _tabs.length,
        itemBuilder: (context, index) => _buildTabPage(_tabs[index]),
      ),
      // 底部导航栏也可关联标签控制器
      bottomNavigationBar: TabBar(
        controller: _tabController,
        tabs: _tabs.map((tab) => Tab(text: tab)).toList(),
      ),
    );
  }
}

二、BottomSheet:优雅的底部弹出交互

BottomSheet 是从屏幕底部弹出的交互组件,相比 Dialog 更符合“轻量交互”场景,如“分享面板”“筛选条件”“详情弹窗”等。Flutter 提供两种核心类型:ModalBottomSheet(模态,遮罩背景)和 PersistentBottomSheet(持久化,可与主界面交互)。

1. 基础用法:模态底部弹窗

ModalBottomSheet 是最常用的底部弹窗类型,弹出时会遮挡背景并禁止主界面交互,点击背景或滑动可关闭,适合“临时交互”场景。

实战代码:分享面板实现

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

  // 显示分享面板
  void _showShareSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      // 取消默认圆角(自定义样式需要)
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      // 弹窗高度适应内容
      isScrollControlled: false,
      // 点击背景是否关闭
      enableDrag: true,
      builder: (context) {
        return Padding(
          padding: const EdgeInsets.symmetric(vertical: 20),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // 标题
              const Text(
                "分享到",
                style: TextStyle(fontSize: 18, fontWeight: 500),
              ),
              const SizedBox(height: 20),
              // 分享渠道网格
              GridView.count(
                // 禁止网格滚动,高度适应内容
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                crossAxisCount: 4,
                childAspectRatio: 1.2,
                children: [
                  _buildShareItem(Icons.wechat, "微信"),
                  _buildShareItem(Icons.weibo, "微博"),
                  _buildShareItem(Icons.qq, "QQ"),
                  _buildShareItem(Icons.email, "邮件"),
                  _buildShareItem(Icons.copy, "复制链接"),
                  _buildShareItem(Icons.more_horiz, "更多"),
                ],
              ),
              const SizedBox(height: 20),
              // 取消按钮
              TextButton(
                onPressed: () => Navigator.pop(context),
                style: TextButton.styleFrom(
                  minimumSize: const Size(double.infinity, 48),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                  backgroundColor: Colors.grey[100],
                ),
                child: const Text(
                  "取消",
                  style: TextStyle(fontSize: 16, color: Colors.black87),
                ),
              ),
              const SizedBox(height: 10),
            ],
          ),
        );
      },
    );
  }

  // 构建单个分享项
  Widget _buildShareItem(IconData icon, String name) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Container(
          width: 56,
          height: 56,
          decoration: BoxDecoration(
            color: Colors.blue[50],
            borderRadius: BorderRadius.circular(28),
          ),
          child: Icon(icon, color: Colors.blue, size: 28),
        ),
        const SizedBox(height: 8),
        Text(name, style: const TextStyle(fontSize: 14)),
      ],
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("模态底部弹窗")),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showShareSheet(context),
          child: const Text("打开分享面板"),
        ),
      ),
    );
  }
}

2. 进阶用法:可滚动的筛选面板

当底部弹窗内容较多(如多条件筛选)时,需要支持滚动,通过设置 isScrollControlled: true 并结合 ListView 实现可滚动布局,同时可实现“全屏”效果。

实战代码:筛选条件面板

class ScrollableBottomSheetDemo extends StatefulWidget {
  const ScrollableBottomSheetDemo({super.key});

  
  State<ScrollableBottomSheetDemo> createState() => _ScrollableBottomSheetDemoState();
}

class _ScrollableBottomSheetDemoState extends State<ScrollableBottomSheetDemo> {
  // 筛选条件状态
  String _selectedPrice = "全部价格";
  List<String> _selectedTags = [];

  void _showFilterSheet(BuildContext context) {
    showModalBottomSheet(
      context: context,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      // 允许滚动并适配键盘
      isScrollControlled: true,
      builder: (context) {
        // 解决底部安全区域问题
        return Padding(
          padding: EdgeInsets.only(
            bottom: MediaQuery.of(context).viewInsets.bottom,
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // 顶部拖动指示器
              Container(
                width: 40,
                height: 4,
                margin: const EdgeInsets.symmetric(vertical: 12),
                decoration: BoxDecoration(
                  color: Colors.grey[300],
                  borderRadius: BorderRadius.circular(2),
                ),
              ),
              const Padding(
                padding: EdgeInsets.symmetric(horizontal: 20),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(
                      "筛选条件",
                      style: TextStyle(fontSize: 18, fontWeight: 500),
                    ),
                    Text("重置", style: TextStyle(color: Colors.blue)),
                  ],
                ),
              ),
              const SizedBox(height: 20),
              // 可滚动的筛选内容
              SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // 价格筛选
                      const Text("价格区间", style: TextStyle(fontSize: 16, fontWeight: 500)),
                      const SizedBox(height: 12),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          _buildPriceTag("全部价格"),
                          _buildPriceTag("0-100元"),
                          _buildPriceTag("100-300元"),
                          _buildPriceTag("300元以上"),
                        ],
                      ),
                      const SizedBox(height: 24),
                      // 标签筛选
                      const Text("商品标签", style: TextStyle(fontSize: 16, fontWeight: 500)),
                      const SizedBox(height: 12),
                      Wrap(
                        spacing: 12,
                        runSpacing: 12,
                        children: [
                          _buildTag("新品"),
                          _buildTag("热销"),
                          _buildTag("折扣"),
                          _buildTag("包邮"),
                          _buildTag("官方正品"),
                          _buildTag("次日达"),
                        ],
                      ),
                      const SizedBox(height: 20),
                    ),
                  ),
                ),
              ),
              // 底部按钮
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
                child: Row(
                  children: [
                    Expanded(
                      child: OutlinedButton(
                        onPressed: () => Navigator.pop(context),
                        child: const Text("取消"),
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: ElevatedButton(
                        onPressed: () {
                          // 执行筛选逻辑
                          print("筛选条件:价格=$_selectedPrice,标签=$_selectedTags");
                          Navigator.pop(context);
                        },
                        child: const Text("确定"),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  // 构建价格标签
  Widget _buildPriceTag(String text) {
    bool isSelected = _selectedPrice == text;
    return GestureDetector(
      onTap: () => setState(() => _selectedPrice = text),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
        decoration: BoxDecoration(
          color: isSelected ? Colors.blue[50] : Colors.grey[100],
          borderRadius: BorderRadius.circular(16),
          border: isSelected ? Border.all(color: Colors.blue) : null,
        ),
        child: Text(
          text,
          style: TextStyle(
            color: isSelected ? Colors.blue : Colors.black87,
            fontSize: 14,
          ),
        ),
      ),
    );
  }

  // 构建商品标签
  Widget _buildTag(String text) {
    bool isSelected = _selectedTags.contains(text);
    return GestureDetector(
      onTap: () {
        setState(() {
          if (isSelected) {
            _selectedTags.remove(text);
          } else {
            _selectedTags.add(text);
          }
        });
      },
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
        decoration: BoxDecoration(
          color: isSelected ? Colors.blue[50] : Colors.grey[100],
          borderRadius: BorderRadius.circular(16),
          border: isSelected ? Border.all(color: Colors.blue) : null,
        ),
        child: Text(
          text,
          style: TextStyle(
            color: isSelected ? Colors.blue : Colors.black87,
            fontSize: 14,
          ),
        ),
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("可滚动底部弹窗")),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showFilterSheet(context),
          child: const Text("打开筛选面板"),
        ),
      ),
    );
  }
}

三、DataTable:结构化数据高效展示

DataTable 是 Flutter 用于展示结构化数据的表格组件,适合“订单列表”“数据统计”“后台管理系统”等场景,支持表头固定、行选择、排序、分页等核心能力,配合 PaginatedDataTable 可快速实现分页表格。

1. 基础用法:简单数据表格

基础 DataTable 由表头(DataColumn)和表体(DataRow)组成,通过 columns 定义列结构,通过 rows 定义行数据,支持基本的单元格样式自定义。

实战代码:订单列表表格

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

  // 模拟订单数据
  final List<Map<String, String>> _orderData = [
    {
      "id": "OD20251201001",
      "user": "张三",
      "amount": "¥199.00",
      "status": "已完成",
      "time": "2025-12-01 09:30"
    },
    {
      "id": "OD20251201002",
      "user": "李四",
      "amount": "¥329.00",
      "status": "待付款",
      "time": "2025-12-01 10:15"
    },
    {
      "id": "OD20251201003",
      "user": "王五",
      "amount": "¥89.00",
      "status": "已取消",
      "time": "2025-12-01 11:20"
    },
    {
      "id": "OD20251201004",
      "user": "赵六",
      "amount": "¥569.00",
      "status": "已发货",
      "time": "2025-12-01 14:05"
    },
  ];

  // 根据订单状态返回不同颜色
  Color _getStatusColor(String status) {
    switch (status) {
      case "已完成":
        return Colors.green;
      case "待付款":
        return Colors.orange;
      case "已取消":
        return Colors.grey;
      case "已发货":
        return Colors.blue;
      default:
        return Colors.black87;
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("基础数据表格")),
      body: SingleChildScrollView(
        // 水平滚动适配表格宽度
        scrollDirection: Axis.horizontal,
        child: DataTable(
          // 表格边框样式
          border: TableBorder.all(color: Colors.grey[200]!),
          // 列间距
          columnSpacing: 24,
          // 表头样式
          headingRowColor: MaterialStateColor.resolveWith(
            (states) => Colors.grey[50]!,
          ),
          headingRowHeight: 56,
          // 行高
          dataRowHeight: 52,
          // 表头定义
          columns: const [
            DataColumn(label: Text("订单编号", style: TextStyle(fontWeight: 500))),
            DataColumn(label: Text("用户名称", style: TextStyle(fontWeight: 500))),
            DataColumn(label: Text("订单金额", style: TextStyle(fontWeight: 500))),
            DataColumn(label: Text("订单状态", style: TextStyle(fontWeight: 500))),
            DataColumn(label: Text("创建时间", style: TextStyle(fontWeight: 500))),
          ],
          // 表体数据
          rows: _orderData.map((order) {
            return DataRow(cells: [
              DataCell(Text(order["id"]!)),
              DataCell(Text(order["user"]!)),
              DataCell(Text(
                order["amount"]!,
                style: const TextStyle(color: Colors.red, fontWeight: 500),
              )),
              DataCell(Text(
                order["status"]!,
                style: TextStyle(color: _getStatusColor(order["status"]!)),
              )),
              DataCell(Text(order["time"]!)),
            ]);
          }).toList(),
        ),
      ),
    );
  }
}

2. 进阶用法:带分页和排序的表格

当数据量较大时,需要分页展示和排序功能,PaginatedDataTable 封装了分页控件,配合 DataSource 可实现数据排序、分页加载等高级功能。

实战代码:分页排序表格

class PaginatedDataTableDemo extends StatefulWidget {
  const PaginatedDataTableDemo({super.key});

  
  State<PaginatedDataTableDemo> createState() => _PaginatedDataTableDemoState();
}

class _PaginatedDataTableDemoState extends State<PaginatedDataTableDemo> {
  // 排序状态
  bool _sortAscending = true;
  // 排序字段
  int _sortColumnIndex = 0;
  // 订单数据源
  late OrderDataSource _orderDataSource;

  // 模拟大量订单数据(20条)
  final List<Map<String, dynamic>> _allOrderData = List.generate(20, (index) {
    return {
      "id": "OD20251201${index.toString().padLeft(3, '0')}",
      "user": ["张三", "李四", "王五", "赵六"][index % 4],
      "amount": 100 + index * 20,
      "status": ["已完成", "待付款", "已取消", "已发货"][index % 4],
      "time": "2025-12-01 ${(9 + index % 6).toString().padLeft(2, '0')}:${(index * 5 % 60).toString().padLeft(2, '0')}"
    };
  });

  
  void initState() {
    super.initState();
    _orderDataSource = OrderDataSource(_allOrderData);
  }

  // 排序回调
  void _onSort(int columnIndex, bool ascending) {
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
      // 根据列索引排序
      if (columnIndex == 2) {
        // 按金额排序
        _allOrderData.sort((a, b) {
          return ascending ? a["amount"].compareTo(b["amount"]) : b["amount"].compareTo(a["amount"]);
        });
      } else if (columnIndex == 4) {
        // 按时间排序
        _allOrderData.sort((a, b) {
          return ascending ? a["time"].compareTo(b["time"]) : b["time"].compareTo(a["time"]);
        });
      }
      _orderDataSource = OrderDataSource(_allOrderData);
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("分页排序表格")),
      body: SingleChildScrollView(
        child: PaginatedDataTable(
          // 每页条数
          rowsPerPage: 5,
          // 可选择的每页条数
          availableRowsPerPage: const [5, 10, 20],
          // 分页控件位置
          actionsOverflowDirection: VerticalDirection.down,
          // 表头样式
          headingRowColor: MaterialStateColor.resolveWith(
            (states) => Colors.grey[50]!,
          ),
          // 排序配置
          sortColumnIndex: _sortColumnIndex,
          sortAscending: _sortAscending,
          // 表头定义(带排序功能)
          columns: [
            const DataColumn(label: Text("订单编号", style: TextStyle(fontWeight: 500))),
            const DataColumn(label: Text("用户名称", style: TextStyle(fontWeight: 500))),
            DataColumn(
              label: const Text("订单金额", style: TextStyle(fontWeight: 500)),
              numeric: true, // 数字右对齐
              onSort: _onSort, // 绑定排序回调
            ),
            const DataColumn(label: Text("订单状态", style: TextStyle(fontWeight: 500))),
            DataColumn(
              label: const Text("创建时间", style: TextStyle(fontWeight: 500)),
              onSort: _onSort,
            ),
          ],
          // 数据源
          source: _orderDataSource,
        ),
      ),
    );
  }
}

// 订单数据源(继承DataTableSource)
class OrderDataSource extends DataTableSource {
  final List<Map<String, dynamic>> _data;

  OrderDataSource(this._data);

  // 获取总行数
  
  int get rowCount => _data.length;

  // 是否可选择行
  
  bool get isRowCountApproximate => false;

  // 每页条数
  
  int get selectedRowCount => 0;

  // 构建表格行
  
  DataRow getRow(int index) {
    final order = _data[index];
    return DataRow(cells: [
      DataCell(Text(order["id"])),
      DataCell(Text(order["user"])),
      DataCell(Text(
        "¥${order["amount"].toStringAsFixed(2)}",
        style: const TextStyle(color: Colors.red, fontWeight: 500),
      )),
      DataCell(Container(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
        decoration: BoxDecoration(
          color: _getStatusColor(order["status"]).withOpacity(0.1),
          borderRadius: BorderRadius.circular(4),
        ),
        child: Text(
          order["status"],
          style: TextStyle(color: _getStatusColor(order["status"])),
        ),
      )),
      DataCell(Text(order["time"])),
    ]);
  }

  // 根据状态获取颜色
  Color _getStatusColor(String status) {
    switch (status) {
      case "已完成":
        return Colors.green;
      case "待付款":
        return Colors.orange;
      case "已取消":
        return Colors.grey;
      case "已发货":
        return Colors.blue;
      default:
        return Colors.black87;
    }
  }
}

四、总结

本文聚焦的 PageView、BottomSheet、DataTable 三类进阶组件,是 Flutter 开发中覆盖“页面切换”“底部交互”“数据展示”核心场景的关键工具:

  • PageView:通过 PageController 实现页面精准控制,结合 TabBar 可快速构建标签页布局,重点关注控制器生命周期和懒加载优化。

  • BottomSheet:区分模态与持久化两种类型,通过 isScrollControlled 实现可滚动布局,适配筛选、分享等轻量交互场景,注意安全区域适配。

  • DataTable:基础表格满足简单数据展示,PaginatedDataTable 实现分页排序,配合 DataSource 可处理大量结构化数据,需注意水平滚动适配。

在这里插入图片描述

掌握这些组件的核心用法后,还需结合实际场景关注“性能优化”(如懒加载、控制器销毁)和“用户体验”(如状态反馈、样式统一)。后续可进一步探索 CustomScrollView 自定义滚动、NestedScrollView 嵌套滚动等高级组件,构建更复杂的交互场景。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐