app_layout.dart 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import 'dart:math' as math;
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_lucide/flutter_lucide.dart';
  4. import 'app_sidebar.dart';
  5. import '../../utils/constants.dart';
  6. import '../../theme/app_colors.dart';
  7. /// 应用布局组件
  8. class AppLayout extends StatelessWidget {
  9. final Widget child;
  10. final bool showSidebar;
  11. final double? maxContentWidth;
  12. final EdgeInsets? contentPadding;
  13. final String? appBarTitle;
  14. const AppLayout({
  15. Key? key,
  16. required this.child,
  17. this.showSidebar = true,
  18. this.maxContentWidth,
  19. this.contentPadding,
  20. this.appBarTitle,
  21. }) : super(key: key);
  22. @override
  23. Widget build(BuildContext context) {
  24. final screenWidth = MediaQuery.of(context).size.width;
  25. final isMobile = screenWidth < AppConstants.mobileBreakpoint;
  26. final isTablet = screenWidth >= AppConstants.mobileBreakpoint &&
  27. screenWidth < AppConstants.tabletBreakpoint;
  28. final isLargeDesktop = screenWidth >= AppConstants.desktopBreakpoint;
  29. // 根据屏幕尺寸确定最大宽度
  30. final effectiveMaxWidth = maxContentWidth ??
  31. (isMobile
  32. ? double.infinity
  33. : isTablet
  34. ? 1320.0
  35. : isLargeDesktop
  36. ? 1760.0
  37. : 1520.0);
  38. // 根据屏幕尺寸确定内边距
  39. final effectivePadding = contentPadding ??
  40. EdgeInsets.symmetric(
  41. horizontal: isMobile
  42. ? 16
  43. : isTablet
  44. ? 32
  45. : isLargeDesktop
  46. ? 56
  47. : 44,
  48. vertical: isMobile
  49. ? 16
  50. : isTablet
  51. ? 28
  52. : isLargeDesktop
  53. ? 40
  54. : 32,
  55. );
  56. // 构建内容包装器
  57. Widget buildContent() {
  58. return LayoutBuilder(
  59. builder: (context, constraints) {
  60. final double targetWidth =
  61. math.min(effectiveMaxWidth, constraints.maxWidth);
  62. final content = Container(
  63. width: targetWidth.isFinite ? targetWidth : null,
  64. padding: effectivePadding,
  65. child: ConstrainedBox(
  66. constraints: BoxConstraints(
  67. minWidth: targetWidth.isFinite ? targetWidth : 0,
  68. maxWidth: effectiveMaxWidth,
  69. ),
  70. child: child,
  71. ),
  72. );
  73. // 对于宽屏需要居中显示内容
  74. if (constraints.maxWidth > effectiveMaxWidth) {
  75. return Align(
  76. alignment: Alignment.topCenter,
  77. child: content,
  78. );
  79. }
  80. return content;
  81. },
  82. );
  83. }
  84. if (!showSidebar) {
  85. return Scaffold(
  86. body: Container(
  87. decoration: const BoxDecoration(
  88. color: AppColors.background,
  89. ),
  90. child: SingleChildScrollView(
  91. child: Align(
  92. alignment: Alignment.topCenter,
  93. child: buildContent(),
  94. ),
  95. ),
  96. ),
  97. );
  98. }
  99. // 移动端使用抽屉式导航
  100. if (isMobile) {
  101. return Scaffold(
  102. appBar: AppBar(
  103. title: Text(appBarTitle ?? AppConstants.appName),
  104. backgroundColor: Colors.white,
  105. foregroundColor: AppColors.textPrimary,
  106. elevation: 0,
  107. leading: Builder(
  108. builder: (context) => IconButton(
  109. icon: const Icon(LucideIcons.menu),
  110. onPressed: () => Scaffold.of(context).openDrawer(),
  111. ),
  112. ),
  113. ),
  114. drawer: const AppSidebar(),
  115. body: Container(
  116. decoration: const BoxDecoration(
  117. color: AppColors.background,
  118. ),
  119. child: SingleChildScrollView(
  120. child: Align(
  121. alignment: Alignment.topCenter,
  122. child: buildContent(),
  123. ),
  124. ),
  125. ),
  126. );
  127. }
  128. // 桌面端使用固定侧边栏
  129. return Scaffold(
  130. body: Row(
  131. children: [
  132. // 侧边栏
  133. const AppSidebar(),
  134. // 主内容区
  135. Expanded(
  136. child: Container(
  137. decoration: const BoxDecoration(
  138. color: AppColors.background,
  139. ),
  140. child: Align(
  141. alignment: Alignment.topCenter,
  142. child: buildContent(),
  143. ),
  144. ),
  145. ),
  146. ],
  147. ),
  148. );
  149. }
  150. }