You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Windows端自定义标题栏:动态Tab栏与Flex布局适配问题

Flutter Windows自定义标题栏:TabBar自适应扩展+滚动实现

核心思路

放弃TabBar与MoveWindow均分空间的布局逻辑,改为TabBar优先占据可用宽度,剩余空间交给MoveWindow;当Tab总宽度超过可用空间时,自动切换为可滚动状态。

具体实现步骤

1. 计算标题栏可用空间

用LayoutBuilder获取标题栏的总宽度,减去左侧「打开文件按钮」和右侧「窗口操作按钮」的固定宽度,得到中间区域的可用空间:

LayoutBuilder(
  builder: (context, constraints) {
    final availableWidth = constraints.maxWidth - openBtnWidth - windowBtnWidth;
    return _buildMiddleArea(availableWidth);
  },
)

2. 动态布局中间区域

根据Tab数量和总宽度,分两种情况处理:

  • 无Tab时:MoveWindow占据全部可用空间
  • 有Tab时:先判断Tab总宽度是否小于等于「可用宽度 - MoveWindow最小拖拽宽度」,满足则让TabBar占对应宽度,剩余空间给MoveWindow;超出则TabBar启用滚动,MoveWindow仅保留最小拖拽宽度(比如40px)

3. 完整代码示例

class CustomTitleBar extends StatelessWidget {
  final List<String> tabs;
  final double openBtnWidth = 80;
  final double windowBtnWidth = 120;
  final double minMoveWindowWidth = 40;

  const CustomTitleBar({super.key, required this.tabs});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      color: Colors.grey[900],
      child: Row(
        children: [
          // 左侧打开文件按钮
          SizedBox(
            width: openBtnWidth,
            child: TextButton(
              onPressed: () {},
              child: const Text('打开文件', style: TextStyle(color: Colors.white)),
            ),
          ),
          // 中间动态区域:TabBar + MoveWindow
          Expanded(
            child: LayoutBuilder(
              builder: (context, constraints) {
                final availableWidth = constraints.maxWidth;
                if (tabs.isEmpty) {
                  // 无Tab时,MoveWindow占满
                  return const MoveWindow();
                }

                // 预估每个Tab的最小宽度(可根据实际UI调整)
                final estimatedTabWidth = 120.0;
                final totalTabWidth = tabs.length * estimatedTabWidth;

                if (totalTabWidth <= availableWidth - minMoveWindowWidth) {
                  // Tab总宽度足够,TabBar占对应宽度,剩余给MoveWindow
                  return Row(
                    children: [
                      SizedBox(
                        width: totalTabWidth,
                        child: TabBar(
                          controller: DefaultTabController.of(context),
                          tabs: tabs.map((t) => Tab(text: t)).toList(),
                          isScrollable: false,
                        ),
                      ),
                      Expanded(child: const MoveWindow()),
                    ],
                  );
                } else {
                  // Tab总宽度超出,TabBar滚动,MoveWindow保留最小宽度
                  return Row(
                    children: [
                      Expanded(
                        child: SingleChildScrollView(
                          scrollDirection: Axis.horizontal,
                          child: SizedBox(
                            width: totalTabWidth,
                            child: TabBar(
                              controller: DefaultTabController.of(context),
                              tabs: tabs.map((t) => Tab(text: t)).toList(),
                              isScrollable: true,
                            ),
                          ),
                        ),
                      ),
                      SizedBox(width: minMoveWindowWidth, child: const MoveWindow()),
                    ],
                  );
                }
              },
            ),
          ),
          // 右侧窗口操作按钮
          SizedBox(
            width: windowBtnWidth,
            child: Row(
              children: [
                IconButton(icon: const Icon(Icons.minimize, color: Colors.white), onPressed: () {}),
                IconButton(icon: const Icon(Icons.maximize, color: Colors.white), onPressed: () {}),
                IconButton(icon: const Icon(Icons.close, color: Colors.white), onPressed: () {}),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

// 自定义MoveWindow拖拽组件示例
class MoveWindow extends StatelessWidget {
  const MoveWindow({super.key});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanStart: (_) {
        // Windows平台窗口拖拽逻辑
        // 可使用window_manager插件或直接调用Win32 API实现
      },
      child: MouseRegion(
        cursor: SystemMouseCursors.move,
        child: Container(color: Colors.transparent),
      ),
    );
  }
}

关键细节说明

  • 精确Tab宽度:如果需要更精准的Tab宽度计算,可以用GlobalKey获取TabBar的实际渲染宽度,替代预估宽度
  • 窗口拖拽实现:Windows平台的窗口拖拽,推荐使用window_manager插件简化操作,避免直接写Win32原生代码
  • 参数调整:可根据实际UI需求修改minMoveWindowWidth、estimatedTabWidth等参数,适配不同设计

内容的提问来源于stack exchange,提问作者Surya Prasad Khanal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:32:05