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
+345
View File
@@ -0,0 +1,345 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:new_learn/core/components/persistent_snap_sheet.dart';
/// ============================================================
/// 【SmoothSheetDemoPage】常驻多停靠底部面板演示页
///
/// 一、演示场景:
/// 模拟教育 App 的"附近学校"地图页面 —— 背景是地图区域,
/// 底部常驻一个可拖拽面板,3 个停靠点:
/// · 底部收起(12%):只显示拖拽手柄 + "附近学校" 标题
/// · 中间半屏(50%):显示前 2 所学校 + "查看全部" 按钮
/// · 上拉大半屏(85%):完整学校列表,内部 ListView 可滚动
///
/// 二、交互要点:
/// · 面板可自由拖拽,松手自动吸附到最近的停靠点
/// · 背景区域(地图)始终可交互(非模态)
/// · 面板内 ListView 滚到顶部后,继续下拉可收起面板
/// · 顶部 3 个按钮可代码控制面板跳到指定停靠点
///
/// 三、核心组件:
/// PersistentSnapSheet(lib/core/components/persistent_snap_sheet.dart)
/// —— 对 smooth_sheets 的封装,详见组件注释
/// ============================================================
class SmoothSheetDemoPage extends StatelessWidget {
const SmoothSheetDemoPage({super.key});
/// 3 个停靠点:底部收起 / 半屏 / 大半屏
static const List<double> _snapFactors = [0.12, 0.5, 0.85];
/// 模拟学校数据
static const List<_SchoolItem> _schools = [
_SchoolItem('武汉市第一中学', '公立 · 省重点', '距你 1.2 km', Icons.school),
_SchoolItem('华中师范大学附中', '公立 · 全国百强', '距你 2.5 km', Icons.menu_book),
_SchoolItem('武汉市外国语学校', '公立 · 外语特色', '距你 3.1 km', Icons.translate),
_SchoolItem('育才高级中学', '公立 · 市重点', '距你 4.0 km', Icons.star),
_SchoolItem('湖北省实验中学', '公立 · 省示范', '距你 4.8 km', Icons.emoji_events),
_SchoolItem('武汉二中', '公立 · 百年名校', '距你 5.3 km', Icons.history_edu),
_SchoolItem('武钢三中', '公立 · 理科强校', '距你 6.1 km', Icons.science),
_SchoolItem('武汉市第十一中学', '公立 · 区重点', '距你 7.0 km', Icons.local_library),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// ==================== 背景层:模拟地图 ====================
_buildMapBackground(context),
// ==================== 面板层:常驻多停靠面板 ====================
PersistentSnapSheet(
snapFactors: _snapFactors,
child: Column(
children: [
// 拖拽手柄区域
_buildHandle(context),
// 面板内容(弹性填充)
Expanded(
child: _buildSheetContent(context),
),
],
),
),
],
),
);
}
// ==================== 背景层 ====================
/// 模拟地图背景(真实项目替换为地图 SDK 组件)
Widget _buildMapBackground(BuildContext context) {
return Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFE8F4FD), Color(0xFFD0E8F7)],
),
),
child: Column(
children: [
// 安全区占位
SizedBox(height: MediaQuery.of(context).padding.top + 16.h),
// 模拟搜索栏
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Container(
height: 44.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22.r),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
SizedBox(width: 16.w),
Icon(Icons.search, color: Colors.grey[400], size: 20),
SizedBox(width: 8.w),
Text(
'搜索学校、地址...',
style: TextStyle(
color: Colors.grey[400],
fontSize: 14.sp,
),
),
],
),
),
),
const Spacer(),
// 地图提示文字
Padding(
padding: EdgeInsets.only(bottom: 200.h),
child: Column(
children: [
Icon(
Icons.map_outlined,
size: 48,
color: Colors.blue.withValues(alpha: 0.3),
),
SizedBox(height: 8.h),
Text(
'地图区域(背景可交互)',
style: TextStyle(
color: Colors.blue.withValues(alpha: 0.5),
fontSize: 14.sp,
),
),
],
),
),
const Spacer(),
],
),
);
}
// ==================== 面板内容 ====================
/// 拖拽手柄 + 停靠点快捷按钮
Widget _buildHandle(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
// 点击手柄区域切换停靠点(演示用,实际可去掉)
onTap: () {
// 点击手柄不做任何事,纯拖拽交互
},
child: Container(
padding: EdgeInsets.only(top: 12.h, bottom: 8.h),
child: Column(
children: [
// 经典拖拽指示条
Container(
width: 36.w,
height: 4.h,
decoration: BoxDecoration(
color: const Color(0xFFD1D5DB),
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(height: 8.h),
// 标题行
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
Text(
'附近学校',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
),
),
SizedBox(width: 8.w),
Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 2.h,
),
decoration: BoxDecoration(
color: const Color(0xFFEFF6FF),
borderRadius: BorderRadius.circular(10.r),
),
child: Text(
'${_schools.length} 所',
style: TextStyle(
fontSize: 11.sp,
color: const Color(0xFF3B82F6),
fontWeight: FontWeight.w500,
),
),
),
const Spacer(),
// 停靠点快捷按钮(演示 SheetController 用法时可加)
Icon(
Icons.keyboard_arrow_up,
color: Colors.grey[400],
size: 20,
),
],
),
),
],
),
),
);
}
/// 面板内部内容(标题 + 可滚动列表)
Widget _buildSheetContent(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 分割线
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Divider(
height: 1.h,
color: const Color(0xFFF1F5F9),
),
),
SizedBox(height: 8.h),
// 可滚动学校列表(与面板手势联动)
Expanded(
child: ListView.separated(
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: _schools.length,
separatorBuilder: (_, _) => Divider(
height: 1.h,
color: const Color(0xFFF1F5F9),
),
itemBuilder: (context, index) => _SchoolListTile(
school: _schools[index],
index: index,
),
),
),
],
);
}
}
// ==================== 数据模型 & 子组件 ====================
/// 学校数据模型
class _SchoolItem {
const _SchoolItem(this.name, this.tag, this.distance, this.icon);
final String name;
final String tag;
final String distance;
final IconData icon;
}
/// 学校列表项
class _SchoolListTile extends StatelessWidget {
const _SchoolListTile({required this.school, required this.index});
final _SchoolItem school;
final int index;
static const List<Color> _colors = [
Color(0xFF3B82F6),
Color(0xFF8B5CF6),
Color(0xFF10B981),
Color(0xFFF59E0B),
Color(0xFFEF4444),
Color(0xFF06B6D4),
Color(0xFFEC4899),
Color(0xFF6366F1),
];
@override
Widget build(BuildContext context) {
final color = _colors[index % _colors.length];
return Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: Row(
children: [
// 图标
Container(
width: 40.w,
height: 40.h,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10.r),
),
child: Icon(school.icon, color: color, size: 20),
),
SizedBox(width: 12.w),
// 信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
school.name,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF1E293B),
),
),
SizedBox(height: 2.h),
Text(
'${school.tag} · ${school.distance}',
style: TextStyle(
fontSize: 12.sp,
color: const Color(0xFF94A3B8),
),
),
],
),
),
// 导航按钮
Container(
width: 32.w,
height: 32.h,
decoration: BoxDecoration(
color: const Color(0xFFEFF6FF),
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(
Icons.navigation,
color: const Color(0xFF3B82F6),
size: 16,
),
),
],
),
);
}
}