Files
2026-09-28 20:12:34 +08:00

346 lines
12 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: '841985856751368');
}
});
},
),
];
@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;
}