flutter架 构项目基础

This commit is contained in:
崔宝新
2026-09-04 15:22:20 +08:00
parent 70967229d0
commit 0c5aee59c0
265 changed files with 35484 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// ============================================================
/// Stack + Positioned 层叠布局演示页
///
/// 一、两种使用方式:
/// 1. 直接运行本页面:runApp(const MaterialApp(home: StackDemoPage()))
/// 2. 复制某个 Demo 的 Stack 代码到任意 Scaffold 的 body 中。
///
/// 二、作用:
/// Stack 实现 UI 重叠,类似 CSS 的 position:absolute。
/// Stack = 图层容器(后写的子组件盖在上面)
/// Positioned = 给子组件做绝对定位(只能直接放在 Stack 的 children 里!)
///
/// 三、核心属性:
/// Stack.alignment : 对【没有被 Positioned 包裹】的子组件做默认对齐
/// Stack.clipBehavior: Clip.none 允许子组件绘制超出 Stack 边界
/// (默认 Clip.hardEdge,超出会被裁剪看不见)
/// Positioned.top/bottom/left/right : 距离 Stack 四边的距离
/// Positioned.fill : 填满整个 Stack 容器(相当于做蒙层)
///
/// 四、三大高频坑:
/// 1. 全部子组件都是 Positioned 时 → Stack 尺寸异常!
/// 真实行为:size = 父约束最大值(constraints.biggest),不按内容算。
/// 放在无界约束(如 Column 裸放)→ debug 断言崩溃 "A Stack requires
/// bounded constraints",整个 body 都画不出来;
/// 放在有界约束 → Stack 占满整个可用区域。
/// 修复A:给 Stack 套 SizedBox 固定宽高;
/// 修复B:放一个非 Positioned 的底层组件撑起大小。
/// 2. Positioned 只能直接作为 Stack 的 children,套其他 Widget 中间层会失效。
/// 3. 做悬浮角标务必 clipBehavior: Clip.none,否则负偏移部分被裁剪掉。
/// ============================================================
class StackDemoPage extends StatelessWidget {
const StackDemoPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Stack 层叠布局 Demo')),
body: SingleChildScrollView(
// AlwaysScrollableScrollPhysics() 永远可以滚动,内容很短也能拽动回弹
// BouncingScrollPhysics() 越界回弹(iOS 风格,拉出来空白再弹回)
// NeverScrollableScrollPhysics() ❌完全禁止滚动,就算内容溢出也不能滑动
physics: BouncingScrollPhysics(),
padding: EdgeInsets.all(26.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Demo1:基础重叠 + 右上角角标'),
// 注意:Flutter 3.44 的 Container 已不是 const 构造,外层不能再写 const Center
Center(
child: Stack(
// 未被 Positioned 的子组件默认居中
alignment: Alignment.topLeft,
// 允许内容超出 Stack,角标才能"溢出"到盒子外
clipBehavior: Clip.none, // 允许超出 Stack 边界 (默认 Clip.hardEdge,超出会被裁剪看不见)
children: [
// 底层盒子
Container(
width: 250.w,
height: 250.h,
color: Colors.blue,
),
// 绝对定位:右上角,负偏移让它一半悬在盒子外
Positioned(
top: -10,
right: -10,
child: Container(
padding: EdgeInsets.all(6.w),
color: Colors.red,
child: Text(
"角标",
style: TextStyle(color: Colors.white),
),
),
),
// 普通子组件,受 Stack.alignment 控制居中
Text(
"中间文字",
style: TextStyle(color: Colors.white, fontSize: 20.sp),
),
],
),
),
SizedBox(height: 32.h),
const _SectionTitle('Demo2:Positioned.fill 蒙层'),
Center(
child: Stack(
children: [
// 底层组件撑起 Stack 的尺寸(250x250)
Container(width: 250.w, height: 250.h, color: Colors.grey),
// Positioned.fill 填满整个 Stack,做半透明蒙层
Positioned.fill(
child: Container(
color: Colors.black54,
child: Center(
child: Text(
"蒙层",
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
),
SizedBox(height: 32.h),
const _SectionTitle('坑①:全部 Positioned → Stack 尺寸异常'),
Text(
'全部子组件都是 Positioned 时,Stack 尺寸不由内容决定,'
'而是直接取父约束最大值。套上 SizedBox 有界约束后,'
'Stack 会占满 300x60(而非收缩到内容大小);'
'若裸放在 Column 这种无界高度里,debug 模式会直接断言崩溃,'
'整个 body 都画不出来——这就是之前页面只剩标题的原因。',
style: TextStyle(fontSize: 13.sp, color: Color(0xFF888888)),
),
SizedBox(height: 8.h),
// 有界约束 + 全 Positioned:Stack 占满 SizedBox 给的 300x60
SizedBox(
width: 300.w,
height: 60.h,
child: ColoredBox(
color: Color(0xFFF5F7FA),
child: Stack(
children: [
Positioned(top: 10, left: 10, child: Text("A")),
],
),
),
),
SizedBox(height: 32.h),
const _SectionTitle('坑①修复方案1:SizedBox 固定宽高'),
SizedBox(
width: 300.w,
height: 80.h,
child: ColoredBox(
color: Color(0xFFF5F7FA),
child: Stack(
children: [
Positioned(top: 10, left: 10, child: Text("A")),
],
),
),
),
SizedBox(height: 32.h),
const _SectionTitle('坑①修复方案2:非 Positioned 底层组件撑起尺寸'),
Stack(
children: [
// 非 Positioned 底层组件:由它决定 Stack 尺寸
ColoredBox(
color: Color(0xFFE8E8E8),
child: SizedBox(width: 300.w, height: 80.h),
),
Positioned(top: 10, left: 10, child: Text("A")),
],
),
],
),
),
);
}
}
/// 区块标题
class _SectionTitle extends StatelessWidget {
const _SectionTitle(this.title);
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: Text(
title,
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
);
}
}