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 _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 _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, ), ), ], ), ); } }