318 lines
11 KiB
Dart
318 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:new_learn/core/components/video_player_page.dart';
|
||
import 'package:new_learn/features/auth/login_page.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),
|
||
),
|
||
];
|
||
|
||
@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: () {
|
||
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);
|
||
|
||
final String title;
|
||
final String subtitle;
|
||
final IconData icon;
|
||
final WidgetBuilder builder;
|
||
}
|