import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'package:new_learn/core/components/video_player_page.dart'; import 'package:new_learn/features/auth/login_page.dart'; import 'package:new_learn/features/conquerPackage/captureList/index.dart'; import 'package:new_learn/pages/demo/align_demo.dart'; import 'package:new_learn/pages/demo/animation_demo.dart'; import 'package:new_learn/pages/demo/form_demo.dart'; import 'package:new_learn/pages/demo/grid_demo.dart'; import 'package:new_learn/pages/demo/image_demo.dart'; import 'package:new_learn/pages/demo/input_demo.dart'; import 'package:new_learn/pages/demo/isar_watch_demo.dart'; import 'package:new_learn/pages/demo/list_demo.dart'; import 'package:new_learn/pages/demo/note_picker_demo.dart'; import 'package:new_learn/pages/demo/oss_upload_demo.dart'; import 'package:new_learn/pages/demo/audio_player_demo.dart'; import 'package:new_learn/pages/demo/rich_text_demo.dart'; import 'package:new_learn/pages/demo/dio_demo.dart'; import 'package:new_learn/pages/demo/dio_pro_demo.dart'; import 'package:new_learn/pages/demo/responsive_demo.dart'; import 'package:new_learn/pages/demo/share_demo.dart'; import 'package:new_learn/pages/demo/seed_palette_demo.dart'; import 'package:new_learn/pages/demo/sliver_demo.dart'; import 'package:new_learn/pages/demo/stack_demo.dart'; import 'package:new_learn/pages/demo/theme_demo.dart'; import 'package:new_learn/pages/demo/text_demo.dart'; import 'package:new_learn/pages/demo/wrong_flow_demo.dart'; import 'package:new_learn/pages/demo/smooth_sheet_demo.dart'; import 'package:new_learn/pages/demo/wrap_demo.dart'; import 'package:new_learn/pages/demo/exam_demo.dart'; /// ============================================================ /// 【TaskPage】「任务」tab 页 —— 学习 Demo 汇总入口 /// /// 一、本页作用: /// 把之前学的所有组件 Demo 收进一个列表,点击每一项通过 /// Navigator.push 路由跳转到对应页面 —— 这就是真实 App 里 /// 「列表 → 详情」的跳转模型。 /// /// 二、路由跳转的两种写法(都可用): /// 1. MaterialPageRoute:无需注册路由表,即写即用(本页采用); /// 2. 命名路由 pushNamed:需在 MaterialApp.routes 里先注册。 /// 推荐本页方式:页面少时最简单;页面多了再统一用命名路由。 /// /// 三、结构化知识点: /// _DemoItem 是纯数据模型(标题/副标题/图标/构造器), /// 列表只用数据驱动生成 UI,新增一个 Demo 只需加一行数据。 /// ============================================================ class TaskPage extends StatelessWidget { const TaskPage({super.key}); /// 全部学习 Demo 的数据清单(新增 Demo 只需在这里加一行) /// /// 每行四项:标题 / 副标题 / 图标 / 页面构造器(用 _page 包装)。 /// 写页面构造器的三种姿势(任选,见 _page 函数注释): /// - _page(LoginPage.new) :构造函数 tear-off(最简洁); /// - 需要给页面传参就写闭包, /// 如 _page(() => VideoPlayerPage(url: '...')); /// - 页面能 const 就写 _page(() => const Xxx()),享 const 优化。 /// _page 内部是惰性创建:点击列表项时才 new 页面,不点不实例化。 static final List<_DemoItem> _demos = [ _DemoItem( '登录页(真实接口)', '账号/密码 + 协议 + POST /api/v1/User/Login', Icons.login, _page(LoginPage.new), ), _DemoItem( 'Align 对齐布局', 'Alignment 定位 + SizedBox 尺寸', Icons.align_horizontal_left, _page(AlignDemoPage.new), ), _DemoItem( 'Wrap 自动换行 + Chip 标签', '流式布局、ActionChip/FilterChip', Icons.wrap_text, _page(WrapDemoPage.new), ), _DemoItem( 'Stack 层叠布局', 'Positioned 定位 + 角标', Icons.layers, _page(StackDemoPage.new), ), _DemoItem( 'TextField 输入框', 'controller + dispose + 密码可见', Icons.text_fields, _page(InputDemoPage.new), ), _DemoItem( '表单校验 Form', 'validator 规则 + 即时校验 + 两段式提交', Icons.rule, _page(FormDemoPage.new), ), _DemoItem( '动画体系', '隐式动画 + 答题反馈 + Hero + 自绘进度环', Icons.auto_awesome, _page(AnimationDemoPage.new), ), _DemoItem( '品牌换肤(两级主题)', '一级品牌色板 + 二级页面细节(SkyBlue/Red)', Icons.palette_outlined, _page(ThemeDemoPage.new), ), _DemoItem( 'Seed 色板(主题生效演示)', 'Material 组件颜色全部来自 colorSchemeSeed 自动生成', Icons.palette, _page(SeedPaletteDemoPage.new), ), _DemoItem( 'Isar watch 响应式', '错题本:数据库变化自动刷新(仅真机)', Icons.sync, _page(IsarWatchDemoPage.new), ), _DemoItem( '接口+Isar+watch 链路', '答题→接口上报→错题本自动刷新(仅真机)', Icons.sync_alt, _page(WrongFlowDemoPage.new), ), _DemoItem( 'Text 文本组件', '样式/截断/富文本/手势', Icons.title, _page(TextDemoPage.new), ), _DemoItem( 'Image 图片组件', '网络图/本地图/圆角/圆形头像', Icons.image, _page(ImageDemoPage.new), ), _DemoItem( 'ListView 列表', '下拉刷新 + 上拉加载 + 回到顶部', Icons.view_list, _page(ListDemoPage.new), ), _DemoItem( 'GridView 网格', 'count/builder/extent 三种构造', Icons.grid_view, _page(GridDemoPage.new), ), _DemoItem( 'CustomScrollView 混排', 'Sliver 系列 + 可折叠头部', Icons.view_day, _page(SliverDemoPage.new), ), _DemoItem( 'Dio 网络请求封装', '拦截器 + 错误映射 + 模型', Icons.cloud_download, _page(DioDemoPage.new), ), _DemoItem( 'Dio 生产级深化', '取消请求 + 进度回调 + 失败重试', Icons.downloading, _page(DioProDemoPage.new), ), _DemoItem( '笔记图片选择', '相册多选/拍照 + 缩略图 + 上传占位', Icons.photo_library_outlined, _page(NotePickerPage.new), ), _DemoItem( 'OSS 直传(V4 签名)', '客户端签名 + PUT 直传阿里云 OSS + 进度条', Icons.cloud_upload_outlined, _page(OssUploadDemoPage.new), ), _DemoItem( '视频播放', 'video_player + chewie 在线点播', Icons.play_circle_outline, // Flutter 官方文档示例视频(bee.mp4),国内网络可直连; // 以后接真实课程视频时把 url 换成后端地址即可 _page( () => const VideoPlayerPage( url: 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', title: '视频播放', ), ), ), _DemoItem( '音频播放', 'just_audio 控制条(播放/进度/倍速/循环)', Icons.music_note, _page(AudioPlayerDemoPage.new), ), _DemoItem( '富文本渲染', '题目/解析 HTML → widget(audio 自动换组件)', Icons.text_snippet_outlined, _page(RichTextDemoPage.new), ), _DemoItem( '响应式布局 手机 vs Pad', '断点判断 + 单列/多列切换', Icons.phone_iphone, _page(ResponsiveDemoPage.new), ), _DemoItem( '分享功能', '文本/图片/Widget截图分享(海报)', Icons.share_outlined, _page(ShareDemoPage.new), ), _DemoItem( '常驻多停靠底部面板', 'smooth_sheets · 3 停靠点 · 内部 ListView 联动', Icons.pan_tool_outlined, _page(SmoothSheetDemoPage.new), ), _DemoItem( '试卷练习(大数据滑动)', 'PageView.builder 100题 + 题型分发 + 续答持久化', Icons.description_outlined, _page(ExamDemoPage.new), ), _DemoItem( '攻克列表', '课程攻克包列表 + 进度条 + 展开详情(go_router 跳转 + extra 传参)', Icons.emoji_events_outlined, _page(() => const Scaffold()), onTap: (context) { // 延时到当前帧结束后再导航,避免 build 过程中触发 Router rebuild WidgetsBinding.instance.addPostFrameCallback((_) { if (context.mounted) { context.push('/conquerPackage/captureList', extra: '841985856751366'); } }); }, ), ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('任务'), centerTitle: true, ), body: ListView.separated( padding: EdgeInsets.all(16.w), itemCount: _demos.length, separatorBuilder: (_, _) => SizedBox(height: 10.h), itemBuilder: (context, index) { final demo = _demos[index]; return Card( elevation: 0, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), side: const BorderSide(color: Color(0xFFEEF1F5)), ), child: ListTile( leading: Container( width: 40.w, height: 40.h, decoration: BoxDecoration( color: const Color(0xFFE8F0FE), borderRadius: BorderRadius.circular(10.r), ), child: Icon(demo.icon, color: const Color(0xFF3B82F6)), ), title: Text( demo.title, style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w600, color: const Color(0xFF333333), ), ), subtitle: Text( demo.subtitle, style: TextStyle(fontSize: 12.sp, color: const Color(0xFF999999)), ), trailing: const Icon( Icons.chevron_right, color: Color(0xFFC0C6CF), ), // 点击 → 路由跳转到对应 Demo 页面 onTap: () { if (demo.onTap != null) { demo.onTap!(context); } else { Navigator.of(context).push( MaterialPageRoute(builder: demo.builder), ); } }, ), ); }, ), ); } } /// 把一个页面构造函数包装成 WidgetBuilder(惰性创建,点击才实例化) /// /// 原理拆解(对照 _DemoItem.builder 的类型 WidgetBuilder): /// - WidgetBuilder = Widget Function(BuildContext context),带 context 参数; /// - 列表项只需要"无参创建页面",用不到 context → _page 把它吞掉; /// - _page(Xxx.new) 里的 .new 是【构造函数 tear-off】(Dart 2.15+ 语法), /// 类型为 `Xxx Function()`,等价手写 `() => Xxx()`。 /// /// 三种等价写法(效果相同): /// _page(Xxx.new) // tear-off,最简洁 /// _page(() => const Xxx()) // 显式闭包 + const 构造(享 const 优化) /// (_) => const Xxx() // 不用 _page,直接就是 WidgetBuilder WidgetBuilder _page(Widget Function() create) { return (_) => create(); } /// Demo 列表项数据模型 class _DemoItem { const _DemoItem( this.title, this.subtitle, this.icon, this.builder, { this.onTap, }); final String title; final String subtitle; final IconData icon; final WidgetBuilder builder; final void Function(BuildContext context)? onTap; }