diff --git a/lib/app/splash_page.dart b/lib/app/splash_page.dart index e4862b1..8162f4c 100644 --- a/lib/app/splash_page.dart +++ b/lib/app/splash_page.dart @@ -77,7 +77,7 @@ class _TransitionViewState extends State { }*/ bool isAggreementAccepted = AppConfigHelper.getAgreementAccepted(); if (isAggreementAccepted) { - pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false); + pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); } else { showDialog( context: context, @@ -91,7 +91,7 @@ class _TransitionViewState extends State { leftTap: () { AppConfigHelper.saveAgreementAccepted(true); pushNamedAndRemoveUntil( - AppRouteName.moduleSelect, (route) => false); + AppRouteName.home, (route) => false); }, rightTap: () { // 退出应用 diff --git a/lib/common/request/apis.dart b/lib/common/request/apis.dart index 71f960e..70e4f71 100644 --- a/lib/common/request/apis.dart +++ b/lib/common/request/apis.dart @@ -53,6 +53,9 @@ class Apis { // 接口地址:https://app.apifox.com/link/project/2684751/apis/api-89897662 static const String courseLesson = 'home/courseLesson'; + // 课程环节列表 + static const String courseSection = 'course/courseLesson'; + /// 磨耳朵 /// GET static const String ears = 'course/grinding/ears'; diff --git a/lib/common/request/dao/home_dao.dart b/lib/common/request/dao/lesson_dao.dart index b6603ed..ba6846d 100644 --- a/lib/common/request/dao/home_dao.dart +++ b/lib/common/request/dao/lesson_dao.dart @@ -1,10 +1,11 @@ import 'package:wow_english/common/request/request_client.dart'; import 'package:wow_english/models/course_entity.dart'; +import 'package:wow_english/models/course_section_entity.dart'; import '../../../models/course_module_entity.dart'; import '../../../models/course_unit_entity.dart'; -class HomeDao { +class LessonDao { ///获取课程模块(列表)信息 static Future?> courseModule() async { var data = await requestClient.get>(Apis.courseModule); @@ -12,20 +13,30 @@ class HomeDao { } ///课程单元列表 - static Future courseUnit(int moduleId) async { + static Future courseUnit(int? moduleId) async { Map mapData = {}; - mapData['moduleId'] = moduleId; + if (moduleId != null) { + mapData['moduleId'] = moduleId; + } var data = await requestClient.get(Apis.courseUnit, queryParameters: mapData); return data; } ///课程列表 - static Future courseLesson({int? moduleId}) async { + static Future courseLesson({int? courseUnitId}) async { Map mapData = {}; - if (moduleId != null) { - mapData['moduleId'] = moduleId; + if (courseUnitId != null) { + mapData['courseUnitId'] = courseUnitId; } var data = await requestClient.get(Apis.courseLesson, queryParameters: mapData); return data; } + + ///课程(单元)列表 + static Future?> courseSection({required int courseUnitId}) async { + Map mapData = {}; + mapData['courseUnitId'] = courseUnitId; + var data = await requestClient.get?>(Apis.courseSection, queryParameters: mapData); + return data; + } } diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart index 9abac24..f673a33 100644 --- a/lib/generated/json/base/json_convert_content.dart +++ b/lib/generated/json/base/json_convert_content.dart @@ -10,6 +10,7 @@ import 'package:wow_english/models/app_version_entity.dart'; import 'package:wow_english/models/course_entity.dart'; import 'package:wow_english/models/course_module_entity.dart'; import 'package:wow_english/models/course_process_entity.dart'; +import 'package:wow_english/models/course_section_entity.dart'; import 'package:wow_english/models/course_unit_entity.dart'; import 'package:wow_english/models/follow_read_entity.dart'; import 'package:wow_english/models/listen_entity.dart'; @@ -198,6 +199,10 @@ class JsonConvert { return data.map((Map e) => CourseProcessVideos.fromJson(e)).toList() as M; } + if ([] is M) { + return data.map((Map e) => + CourseSectionEntity.fromJson(e)).toList() as M; + } if ([] is M) { return data.map((Map e) => CourseUnitEntity.fromJson(e)).toList() as M; @@ -261,6 +266,7 @@ class JsonConvertClassCollection { (CourseProcessTopicsTopicAnswerList) .toString(): CourseProcessTopicsTopicAnswerList.fromJson, (CourseProcessVideos).toString(): CourseProcessVideos.fromJson, + (CourseSectionEntity).toString(): CourseSectionEntity.fromJson, (CourseUnitEntity).toString(): CourseUnitEntity.fromJson, (CourseUnitDetail).toString(): CourseUnitDetail.fromJson, (FollowReadEntity).toString(): FollowReadEntity.fromJson, diff --git a/lib/generated/json/course_section_entity.g.dart b/lib/generated/json/course_section_entity.g.dart new file mode 100644 index 0000000..19945d2 --- /dev/null +++ b/lib/generated/json/course_section_entity.g.dart @@ -0,0 +1,89 @@ +import 'package:wow_english/generated/json/base/json_convert_content.dart'; +import 'package:wow_english/models/course_section_entity.dart'; + +CourseSectionEntity $CourseSectionEntityFromJson(Map json) { + final CourseSectionEntity courseSectionEntity = CourseSectionEntity(); + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + courseSectionEntity.id = id; + } + final int? courseUnitId = jsonConvert.convert(json['courseUnitId']); + if (courseUnitId != null) { + courseSectionEntity.courseUnitId = courseUnitId; + } + final int? courseModuleId = jsonConvert.convert(json['courseModuleId']); + if (courseModuleId != null) { + courseSectionEntity.courseModuleId = courseModuleId; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + courseSectionEntity.name = name; + } + final dynamic des = json['des']; + if (des != null) { + courseSectionEntity.des = des; + } + final int? courseType = jsonConvert.convert(json['courseType']); + if (courseType != null) { + courseSectionEntity.courseType = courseType; + } + final dynamic coverUrl = json['coverUrl']; + if (coverUrl != null) { + courseSectionEntity.coverUrl = coverUrl; + } + final int? sortOrder = jsonConvert.convert(json['sortOrder']); + if (sortOrder != null) { + courseSectionEntity.sortOrder = sortOrder; + } + final int? status = jsonConvert.convert(json['status']); + if (status != null) { + courseSectionEntity.status = status; + } + final bool? lock = jsonConvert.convert(json['lock']); + if (lock != null) { + courseSectionEntity.lock = lock; + } + return courseSectionEntity; +} + +Map $CourseSectionEntityToJson(CourseSectionEntity entity) { + final Map data = {}; + data['id'] = entity.id; + data['courseUnitId'] = entity.courseUnitId; + data['courseModuleId'] = entity.courseModuleId; + data['name'] = entity.name; + data['des'] = entity.des; + data['courseType'] = entity.courseType; + data['coverUrl'] = entity.coverUrl; + data['sortOrder'] = entity.sortOrder; + data['status'] = entity.status; + data['lock'] = entity.lock; + return data; +} + +extension CourseSectionEntityExtension on CourseSectionEntity { + CourseSectionEntity copyWith({ + int? id, + int? courseUnitId, + int? courseModuleId, + String? name, + dynamic des, + int? courseType, + dynamic coverUrl, + int? sortOrder, + int? status, + bool? lock, + }) { + return CourseSectionEntity() + ..id = id ?? this.id + ..courseUnitId = courseUnitId ?? this.courseUnitId + ..courseModuleId = courseModuleId ?? this.courseModuleId + ..name = name ?? this.name + ..des = des ?? this.des + ..courseType = courseType ?? this.courseType + ..coverUrl = coverUrl ?? this.coverUrl + ..sortOrder = sortOrder ?? this.sortOrder + ..status = status ?? this.status + ..lock = lock ?? this.lock; + } +} \ No newline at end of file diff --git a/lib/models/course_module_entity.dart b/lib/models/course_module_entity.dart index c59d3fe..0be37ea 100644 --- a/lib/models/course_module_entity.dart +++ b/lib/models/course_module_entity.dart @@ -5,7 +5,7 @@ import 'package:wow_english/generated/json/course_module_entity.g.dart'; @JsonSerializable() class CourseModuleEntity { - late int id; + int? id; String? code; int? courseModuleThemeId; int? courseTotal; diff --git a/lib/models/course_section_entity.dart b/lib/models/course_section_entity.dart new file mode 100644 index 0000000..a4f1047 --- /dev/null +++ b/lib/models/course_section_entity.dart @@ -0,0 +1,29 @@ +import 'package:wow_english/generated/json/base/json_field.dart'; +import 'package:wow_english/generated/json/course_section_entity.g.dart'; +import 'dart:convert'; +export 'package:wow_english/generated/json/course_section_entity.g.dart'; + +@JsonSerializable() +class CourseSectionEntity { + late int id; + late int courseUnitId; + late int courseModuleId; + late String name; + dynamic des; + late int courseType; + dynamic coverUrl; + late int sortOrder; + late int status; + late bool lock; + + CourseSectionEntity(); + + factory CourseSectionEntity.fromJson(Map json) => $CourseSectionEntityFromJson(json); + + Map toJson() => $CourseSectionEntityToJson(this); + + @override + String toString() { + return jsonEncode(this); + } +} \ No newline at end of file diff --git a/lib/pages/moduleSelect/bloc.dart b/lib/pages/home/bloc.dart index 62e25c7..5279510 100644 --- a/lib/pages/moduleSelect/bloc.dart +++ b/lib/pages/home/bloc.dart @@ -1,7 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; -import 'package:wow_english/models/app_config_entity.dart'; import '../../common/core/app_config_helper.dart'; import '../../common/request/dao/system_dao.dart'; @@ -10,17 +9,17 @@ import '../../utils/log_util.dart'; import 'event.dart'; import 'state.dart'; -class ModuleSelectBloc extends Bloc { - ModuleSelectBloc() : super(ModuleSelectState().init()) { +class ModuleSelectBloc extends Bloc { + ModuleSelectBloc() : super(HomeState().init()) { on(_init); } - void _init(InitEvent event, Emitter emit) async { + void _init(InitEvent event, Emitter emit) async { await _checkUpdate(emit); debugPrint('WQF ModuleSelectBloc _init'); } - Future _checkUpdate(Emitter emit) async { + Future _checkUpdate(Emitter emit) async { if (AppConfigHelper.checkedUpdate) { return; } diff --git a/lib/pages/home/event.dart b/lib/pages/home/event.dart new file mode 100644 index 0000000..e16af4c --- /dev/null +++ b/lib/pages/home/event.dart @@ -0,0 +1,3 @@ +abstract class HomeEvent {} + +class InitEvent extends HomeEvent {} \ No newline at end of file diff --git a/lib/pages/moduleSelect/state.dart b/lib/pages/home/state.dart index 98e2581..45a8985 100644 --- a/lib/pages/moduleSelect/state.dart +++ b/lib/pages/home/state.dart @@ -1,16 +1,16 @@ import 'package:wow_english/models/app_version_entity.dart'; -class ModuleSelectState { - ModuleSelectState init() { - return ModuleSelectState(); +class HomeState { + HomeState init() { + return HomeState(); } - ModuleSelectState clone() { - return ModuleSelectState(); + HomeState clone() { + return HomeState(); } } -class UpdateDialogState extends ModuleSelectState { +class UpdateDialogState extends HomeState { final AppVersionEntity appVersionEntity; diff --git a/lib/pages/moduleSelect/view.dart b/lib/pages/home/view.dart index e191286..af9f5ef 100644 --- a/lib/pages/moduleSelect/view.dart +++ b/lib/pages/home/view.dart @@ -9,8 +9,8 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/models/app_version_entity.dart'; -import 'package:wow_english/pages/moduleSelect/state.dart'; -import 'package:wow_english/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart'; +import 'package:wow_english/pages/home/state.dart'; +import 'package:wow_english/pages/home/widgets/BaseHomeHeaderWidget.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; import '../../common/core/user_util.dart'; @@ -21,8 +21,8 @@ import 'event.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/route/route.dart'; -class ModuleSelectPage extends StatelessWidget { - const ModuleSelectPage({super.key}); +class HomePage extends StatelessWidget { + const HomePage({super.key}); @override Widget build(BuildContext context) { @@ -42,7 +42,7 @@ class _HomePageView extends StatelessWidget { BlocListener(listener: (context, state) { debugPrint('WQF ModuleSelectPage BlocListener state: $state'); }), - BlocListener( + BlocListener( listener: (context, state) { if (state is UpdateDialogState) { _showUpdateDialog(context, state.forceUpdate, state.appVersionEntity); @@ -53,7 +53,7 @@ class _HomePageView extends StatelessWidget { } Widget _homeView() => - BlocBuilder( + BlocBuilder( builder: (context, state) { return Scaffold( body: Container( @@ -69,7 +69,7 @@ class _HomePageView extends StatelessWidget { child: GestureDetector( onTap: () { if (UserUtil.isLogined()) { - pushNamed(AppRouteName.home); + pushNamed(AppRouteName.courseUnit); } else { pushNamed(AppRouteName.login); } diff --git a/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart b/lib/pages/home/widgets/BaseHomeHeaderWidget.dart index 11a2ccf..11a2ccf 100644 --- a/lib/pages/moduleSelect/widgets/BaseHomeHeaderWidget.dart +++ b/lib/pages/home/widgets/BaseHomeHeaderWidget.dart diff --git a/lib/pages/lessons/bloc/lesson_state.dart b/lib/pages/lessons/bloc/lesson_state.dart deleted file mode 100644 index fac0b10..0000000 --- a/lib/pages/lessons/bloc/lesson_state.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of 'lesson_bloc.dart'; - -@immutable -abstract class LessonState {} - -class LessonInitial extends LessonState {} - -class PageIndexChangeState extends LessonState {} - -class LessonDataLoadState extends LessonState {} diff --git a/lib/pages/login/loginpage/login_page.dart b/lib/pages/login/loginpage/login_page.dart index 5ae615d..17e943a 100644 --- a/lib/pages/login/loginpage/login_page.dart +++ b/lib/pages/login/loginpage/login_page.dart @@ -34,7 +34,7 @@ class _LoginPageView extends StatelessWidget { if (state is LoginResultChangeState) { // 调试用 // Navigator.of(context).pushNamed(AppRouteName.home); - pushNamedAndRemoveUntil(AppRouteName.moduleSelect, (route) => false); + pushNamedAndRemoveUntil(AppRouteName.home, (route) => false); } }, child: _buildLoginViewWidget(), diff --git a/lib/pages/lessons/bloc/lesson_bloc.dart b/lib/pages/module/bloc/module_bloc.dart index 5c4077f..102d889 100644 --- a/lib/pages/lessons/bloc/lesson_bloc.dart +++ b/lib/pages/module/bloc/module_bloc.dart @@ -1,15 +1,15 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:wow_english/common/request/dao/home_dao.dart'; +import 'package:wow_english/common/request/dao/lesson_dao.dart'; import 'package:wow_english/common/request/exception.dart'; import 'package:wow_english/models/course_module_entity.dart'; import 'package:wow_english/utils/loading.dart'; import 'package:wow_english/utils/toast_util.dart'; -part 'lesson_event.dart'; -part 'lesson_state.dart'; +part 'module_event.dart'; +part 'module_state.dart'; -class LessonBloc extends Bloc { +class ModuleBloc extends Bloc { final int pageIndex; final PageController pageController; @@ -22,22 +22,22 @@ class LessonBloc extends Bloc { List get listData => _listData; - LessonBloc(this.pageIndex, this.pageController) : super(LessonInitial()) { + ModuleBloc(this.pageIndex, this.pageController) : super(ModuleInitial()) { _currentPageIndex = pageIndex; on(_pageIndexChange); on(_requestData); } - void _pageIndexChange(PageViewChangeIndexEvent event, Emitter emitter) async { + void _pageIndexChange(PageViewChangeIndexEvent event, Emitter emitter) async { _currentPageIndex = event.index; emitter(PageIndexChangeState()); } - void _requestData(RequestDataEvent event, Emitter emitter) async { + void _requestData(RequestDataEvent event, Emitter emitter) async { try { await loading(() async { - _listData = await HomeDao.courseModule() ?? []; - emitter(LessonDataLoadState()); + _listData = await LessonDao.courseModule() ?? []; + emitter(ModuleDataLoadState()); }); } catch (e) { if (e is ApiException) { diff --git a/lib/pages/lessons/bloc/lesson_event.dart b/lib/pages/module/bloc/module_event.dart index 7dd5fda..ab002fa 100644 --- a/lib/pages/lessons/bloc/lesson_event.dart +++ b/lib/pages/module/bloc/module_event.dart @@ -1,11 +1,11 @@ -part of 'lesson_bloc.dart'; +part of 'module_bloc.dart'; @immutable -abstract class LessonEvent {} +abstract class ModuleEvent {} -class PageViewChangeIndexEvent extends LessonEvent { +class PageViewChangeIndexEvent extends ModuleEvent { final int index; PageViewChangeIndexEvent(this.index); } -class RequestDataEvent extends LessonEvent {} +class RequestDataEvent extends ModuleEvent {} diff --git a/lib/pages/module/bloc/module_state.dart b/lib/pages/module/bloc/module_state.dart new file mode 100644 index 0000000..138fe63 --- /dev/null +++ b/lib/pages/module/bloc/module_state.dart @@ -0,0 +1,10 @@ +part of 'module_bloc.dart'; + +@immutable +abstract class ModuleState {} + +class ModuleInitial extends ModuleState {} + +class PageIndexChangeState extends ModuleState {} + +class ModuleDataLoadState extends ModuleState {} diff --git a/lib/pages/lessons/lesson_page.dart b/lib/pages/module/module_page.dart index e3da130..dc29413 100644 --- a/lib/pages/lessons/lesson_page.dart +++ b/lib/pages/module/module_page.dart @@ -6,23 +6,21 @@ import 'package:wow_english/common/widgets/we_app_bar.dart'; import 'package:wow_english/models/course_module_entity.dart'; import 'package:wow_english/route/route.dart'; -import 'bloc/lesson_bloc.dart'; -import 'widgets/lesson_item_widget.dart'; +import 'bloc/module_bloc.dart'; +import 'widgets/module_item_widget.dart'; -class LessonPage extends StatelessWidget { - const LessonPage({super.key, this.starPageIndex}); +// 阶段(模块)列表页 +class ModulePage extends StatelessWidget { + const ModulePage({super.key, this.starPageIndex}); final int? starPageIndex; @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => LessonBloc( - starPageIndex??0, - PageController( - initialPage: starPageIndex??0, - viewportFraction: 0.3 - ), + create: (context) => ModuleBloc( + starPageIndex ?? 0, + PageController(initialPage: starPageIndex ?? 0, viewportFraction: 0.3), )..add(RequestDataEvent()), child: _LessonPageView(), ); @@ -30,27 +28,25 @@ class LessonPage extends StatelessWidget { } class _LessonPageView extends StatelessWidget { - final double _cardHeight = 240.h; final double _scale = 0.8; @override Widget build(BuildContext context) { - return BlocListener( - listener: (context, state){}, + return BlocListener( + listener: (context, state) {}, child: Scaffold( appBar: WEAppBar( leading: IconButton( - onPressed: (){ - popPage(); + onPressed: () { + popPage(); }, icon: Image.asset( 'back'.assetPng, height: 43, width: 43, - ) - ), + )), // actions: [ // IconButton( // icon: Image.asset('shop'.assetPng), @@ -66,9 +62,9 @@ class _LessonPageView extends StatelessWidget { ); } - Widget _lessViewWidget() => BlocBuilder( - builder: (context, state){ - final bloc = BlocProvider.of(context); + Widget _lessViewWidget() => + BlocBuilder(builder: (context, state) { + final bloc = BlocProvider.of(context); return Center( child: SafeArea( child: Column( @@ -81,8 +77,7 @@ class _LessonPageView extends StatelessWidget { onPageChanged: (int index) { bloc.add(PageViewChangeIndexEvent(index)); }, - itemBuilder: (context,index) => _itemTransCard(index) - ), + itemBuilder: (context, index) => _itemTransCard(index)), ), 32.verticalSpace, SizedBox( @@ -91,7 +86,7 @@ class _LessonPageView extends StatelessWidget { child: ListView.builder( itemCount: bloc.listData.length, scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context,int index){ + itemBuilder: (BuildContext context, int index) { return Container( height: 32.h, width: 66.w, @@ -101,13 +96,19 @@ class _LessonPageView extends StatelessWidget { if (index == bloc.currentPageIndex) { return; } - int mill = (index - bloc.currentPageIndex) > 0 ? 100 * (index - bloc.currentPageIndex):100 * (bloc.currentPageIndex-index); - bloc.pageController.animateToPage(index, duration: Duration(milliseconds: mill), curve: Curves.ease); + int mill = (index - bloc.currentPageIndex) > 0 + ? 100 * (index - bloc.currentPageIndex) + : 100 * (bloc.currentPageIndex - index); + bloc.pageController.animateToPage(index, + duration: Duration(milliseconds: mill), + curve: Curves.ease); }, child: Container( - height: bloc.currentPageIndex == index ? 32:20, + height: bloc.currentPageIndex == index ? 32 : 20, decoration: BoxDecoration( - color: bloc.currentPageIndex == index ? Colors.red:Colors.white, + color: bloc.currentPageIndex == index + ? Colors.red + : Colors.white, borderRadius: BorderRadius.circular(5.r), border: Border.all( width: 0.5, @@ -116,10 +117,11 @@ class _LessonPageView extends StatelessWidget { ), alignment: Alignment.center, child: Text( - (index+1).toString(), + (index + 1).toString(), style: TextStyle( - color: bloc.currentPageIndex == index ? Colors.white:Colors.black - ), + color: bloc.currentPageIndex == index + ? Colors.white + : Colors.black), ), ), ), @@ -132,49 +134,54 @@ class _LessonPageView extends StatelessWidget { ); }); - Widget _itemTransCard(int index) => BlocBuilder( - builder: (context, state) { - final bloc = BlocProvider.of(context); - Matrix4 matrix4 = Matrix4.identity(); - if (index == bloc.currentPageIndex.floor()) { - //当前的item - double currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble(); - var currTrans = _cardHeight * (1 - currScale) / 2; + Widget _itemTransCard(int index) => + BlocBuilder(builder: (context, state) { + final bloc = BlocProvider.of(context); + Matrix4 matrix4 = Matrix4.identity(); + if (index == bloc.currentPageIndex.floor()) { + //当前的item + double currScale = + (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble(); + var currTrans = _cardHeight * (1 - currScale) / 2; - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) - ..setTranslationRaw(0.0, currTrans, 0.0); - } else if (index == bloc.currentPageIndex.floor() + 1) { - //右边的item - var currScale = _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale); - var currTrans = _cardHeight * (1 - currScale) / 2; + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) + ..setTranslationRaw(0.0, currTrans, 0.0); + } else if (index == bloc.currentPageIndex.floor() + 1) { + //右边的item + var currScale = + _scale + (bloc.currentPageIndex - index + 1) * (1 - _scale); + var currTrans = _cardHeight * (1 - currScale) / 2; - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) - ..setTranslationRaw(0.0, currTrans, 0.0); - } else if (index == bloc.currentPageIndex - 1) { - //左边 - var currScale = (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble(); - var currTrans = _cardHeight * (1 - currScale) / 2; + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) + ..setTranslationRaw(0.0, currTrans, 0.0); + } else if (index == bloc.currentPageIndex - 1) { + //左边 + var currScale = + (1 - (bloc.currentPageIndex - index) * (1 - _scale)).toDouble(); + var currTrans = _cardHeight * (1 - currScale) / 2; - matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) - ..setTranslationRaw(0.0, currTrans, 0.0); - } else { - //其他,不在屏幕显示的item - matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0) - ..setTranslationRaw(0.0, _cardHeight * (1 - _scale) / 2, 0.0); - } - CourseModuleEntity? model = bloc.listData[index]; - return Transform( - transform: matrix4, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: LessonItemWidget( - model: model, - isSelected: bloc.currentPageIndex == index, - onClickEvent: () { - pushNamed(AppRouteName.unit, arguments: {'courseModuleEntity':model}); - }, + matrix4 = Matrix4.diagonal3Values(1.0, currScale, 1.0) + ..setTranslationRaw(0.0, currTrans, 0.0); + } else { + //其他,不在屏幕显示的item + matrix4 = Matrix4.diagonal3Values(1.0, _scale, 1.0) + ..setTranslationRaw(0.0, _cardHeight * (1 - _scale) / 2, 0.0); + } + CourseModuleEntity? model = bloc.listData[index]; + return Transform( + transform: matrix4, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: ModuleItemWidget( + model: model, + isSelected: bloc.currentPageIndex == index, + onClickEvent: () { + pushNamedAndRemoveUntil( + AppRouteName.courseUnit, (route) => route.isFirst, + arguments: {'courseModuleEntity': model}); + }, + ), ), - ), - ); - }); + ); + }); } diff --git a/lib/pages/lessons/widgets/lesson_item_widget.dart b/lib/pages/module/widgets/module_item_widget.dart index cad8353..ad7e76b 100644 --- a/lib/pages/lessons/widgets/lesson_item_widget.dart +++ b/lib/pages/module/widgets/module_item_widget.dart @@ -4,10 +4,10 @@ import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/ow_image_widget.dart'; import 'package:wow_english/models/course_module_entity.dart'; -import '../../home/courese_module_model.dart'; +import '../../section/courese_module_model.dart'; -class LessonItemWidget extends StatelessWidget { - const LessonItemWidget({super.key, required this.isSelected, this.model, this.onClickEvent}); +class ModuleItemWidget extends StatelessWidget { + const ModuleItemWidget({super.key, required this.isSelected, this.model, this.onClickEvent}); ///是否被选中 final bool isSelected; final CourseModuleEntity? model; diff --git a/lib/pages/moduleSelect/event.dart b/lib/pages/moduleSelect/event.dart deleted file mode 100644 index 5695068..0000000 --- a/lib/pages/moduleSelect/event.dart +++ /dev/null @@ -1,3 +0,0 @@ -abstract class ModuleSelectEvent {} - -class InitEvent extends ModuleSelectEvent {} \ No newline at end of file diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/section/bloc/section_bloc.dart index ef0c7b1..ef3b3f0 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/section/bloc/section_bloc.dart @@ -1,40 +1,49 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:wow_english/common/request/dao/home_dao.dart'; +import 'package:wow_english/common/request/dao/lesson_dao.dart'; import 'package:wow_english/common/request/exception.dart'; -import 'package:wow_english/models/course_entity.dart'; import 'package:wow_english/common/request/dao/listen_dao.dart'; import 'package:wow_english/models/course_process_entity.dart'; import 'package:wow_english/utils/loading.dart'; import 'package:wow_english/utils/toast_util.dart'; -part 'home_event.dart'; -part 'home_state.dart'; +import '../../../models/course_section_entity.dart'; +import '../../../models/course_unit_entity.dart'; -class HomeBloc extends Bloc { - final int? moduleId; +part 'section_event.dart'; +part 'section_state.dart'; - CourseEntity? _modelData; +class SectionBloc extends Bloc { - CourseEntity? get modelData => _modelData; + CourseUnitEntity _courseUnitEntity; + + CourseUnitEntity get courseUnitEntity => _courseUnitEntity; + + CourseUnitDetail _courseUnitDetail; + + CourseUnitDetail get courseUnitDetail => _courseUnitDetail; + + List? _courseSectionDatas; + + List? get courseSectionDatas => _courseSectionDatas; CourseProcessEntity? _processEntity; CourseProcessEntity? get processEntity => _processEntity; - HomeBloc(this.moduleId) : super(HomeInitial()) { + SectionBloc(this._courseUnitEntity, this._courseUnitDetail) : super(LessonInitial()) { on(_requestData); on(_requestExitClass); on(_requestEnterClass); on(_requestVideoLesson); } - void _requestData(RequestDataEvent event, Emitter emitter) async { + void _requestData(RequestDataEvent event, Emitter emitter) async { try { await loading(() async { - _modelData = await HomeDao.courseLesson(moduleId: moduleId); - emitter(HomeDataLoadState()); + _courseSectionDatas = await LessonDao.courseSection(courseUnitId: _courseUnitDetail.id!); + emitter(LessonDataLoadState()); }); } catch (e) { if (e is ApiException) { @@ -43,7 +52,7 @@ class HomeBloc extends Bloc { } } - void _requestVideoLesson(RequestVideoLessonEvent event, Emitter emitter) async { + void _requestVideoLesson(RequestVideoLessonEvent event, Emitter emitter) async { try { await loading(() async { _processEntity = await ListenDao.process(event.courseLessonId); @@ -57,7 +66,7 @@ class HomeBloc extends Bloc { } - void _requestEnterClass(RequestEnterClassEvent event,Emitter emitter) async { + void _requestEnterClass(RequestEnterClassEvent event,Emitter emitter) async { try { await loading(() async { await ListenDao.enterClass(event.courseLessonId); @@ -70,7 +79,7 @@ class HomeBloc extends Bloc { } } - void _requestExitClass(RequestExitClassEvent event,Emitter emitter) async { + void _requestExitClass(RequestExitClassEvent event,Emitter emitter) async { await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime); } } diff --git a/lib/pages/home/bloc/home_event.dart b/lib/pages/section/bloc/section_event.dart index a28e023..cff955d 100644 --- a/lib/pages/home/bloc/home_event.dart +++ b/lib/pages/section/bloc/section_event.dart @@ -1,26 +1,26 @@ -part of 'home_bloc.dart'; +part of 'section_bloc.dart'; @immutable -abstract class HomeEvent {} +abstract class SectionEvent {} -class RequestDataEvent extends HomeEvent {} +class RequestDataEvent extends SectionEvent {} ///获取视频课程内容 -class RequestVideoLessonEvent extends HomeEvent { +class RequestVideoLessonEvent extends SectionEvent { final String courseLessonId; final int courseType; RequestVideoLessonEvent(this.courseLessonId, this.courseType); } ///进入课堂 -class RequestEnterClassEvent extends HomeEvent { +class RequestEnterClassEvent extends SectionEvent { final String courseLessonId; final int courseType; RequestEnterClassEvent(this.courseLessonId,this.courseType); } ///退出课堂 -class RequestExitClassEvent extends HomeEvent { +class RequestExitClassEvent extends SectionEvent { final String courseLessonId; final String currentStep; final String currentTime; diff --git a/lib/pages/home/bloc/home_state.dart b/lib/pages/section/bloc/section_state.dart index a58a828..9c50452 100644 --- a/lib/pages/home/bloc/home_state.dart +++ b/lib/pages/section/bloc/section_state.dart @@ -1,19 +1,19 @@ -part of 'home_bloc.dart'; +part of 'section_bloc.dart'; @immutable -abstract class HomeState {} +abstract class SectionState {} -class HomeInitial extends HomeState {} +class LessonInitial extends SectionState {} -class HomeDataLoadState extends HomeState {} +class LessonDataLoadState extends SectionState {} -class RequestVideoLessonState extends HomeState { +class RequestVideoLessonState extends SectionState { final String courseLessonId; final int type; RequestVideoLessonState(this.courseLessonId,this.type); } -class RequestEnterClassState extends HomeState{ +class RequestEnterClassState extends SectionState{ final String courseLessonId; final int courseType; RequestEnterClassState(this.courseLessonId,this.courseType); diff --git a/lib/pages/home/courese_module_model.dart b/lib/pages/section/courese_module_model.dart index fb3e36c..91db786 100644 --- a/lib/pages/home/courese_module_model.dart +++ b/lib/pages/section/courese_module_model.dart @@ -2,9 +2,10 @@ import 'package:flutter/material.dart'; class CourseModuleModel { Color get color => getCourseColor(); - String get courseModuleTitle => getCourseModuleTitle(); - String get courseModuleLogo => getCoureseImageName(); + String get courseModuleTitle => getCourseModuleTitle(); + + String get courseModuleLogo => getCoureseImageName(); String course; @@ -84,4 +85,4 @@ class CourseModuleModel { } return 'red_positive'; } -} \ No newline at end of file +} diff --git a/lib/pages/home/home_page.dart b/lib/pages/section/section_page.dart index cf0831b..edf88c1 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/section/section_page.dart @@ -3,60 +3,47 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; -import 'package:wow_english/models/course_entity.dart'; -import 'package:wow_english/pages/home/widgets/home_bouns_item.dart'; -import 'package:wow_english/pages/home/widgets/home_tab_header_widget.dart'; -import 'package:wow_english/pages/home/widgets/home_video_item.dart'; +import 'package:wow_english/models/course_unit_entity.dart'; +import 'package:wow_english/pages/section/widgets/home_video_item.dart'; +import 'package:wow_english/pages/section/widgets/section_bouns_item.dart'; +import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; import 'package:wow_english/route/route.dart'; import 'package:wow_english/utils/toast_util.dart'; -import 'bloc/home_bloc.dart'; +import '../../models/course_section_entity.dart'; +import 'bloc/section_bloc.dart'; import 'courese_module_model.dart'; -class HomePage extends StatelessWidget { - const HomePage({super.key, this.moduleId}); +/// 环节列表页 +class SectionPage extends StatelessWidget { + const SectionPage({super.key, required this.courseUnitEntity, required this.courseUnitDetail}); - /// 模块id - final int? moduleId; + final CourseUnitEntity courseUnitEntity; + + /// unitId + final CourseUnitDetail courseUnitDetail; @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => HomeBloc(moduleId)..add(RequestDataEvent()), - child: _HomePageView(context), + create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail)..add(RequestDataEvent()), + child: _SectionPageView(context), ); } } -class _HomePageView extends StatelessWidget { - - const _HomePageView(this.context); +class _SectionPageView extends StatelessWidget { + const _SectionPageView(this.context); final BuildContext context; - void _headerActionEvent(HeaderActionType type) { - if (type == HeaderActionType.video) { - pushNamed(AppRouteName.reAfter); - } else if (type == HeaderActionType.phase) { - pushNamed(AppRouteName.lesson); - } else if (type == HeaderActionType.listen) { - pushNamed(AppRouteName.listen); - } else if (type == HeaderActionType.shop) { - pushNamed(AppRouteName.shop); - } else if (type == HeaderActionType.user) { - pushNamed(AppRouteName.user); - } else if (type == HeaderActionType.home) { - Navigator.pop(context); - } - } - @override Widget build(BuildContext context) { - final bloc = BlocProvider.of(context); - return BlocListener( + final bloc = BlocProvider.of(context); + return BlocListener( listener: (context, state) { if (state is RequestVideoLessonState) { - final videoUrl = bloc.processEntity?.videos?.videoUrl??''; + final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; var title = ''; if (state.type == 1) { title = 'song'; @@ -73,9 +60,13 @@ class _HomePageView extends StatelessWidget { if (videoUrl.isEmpty || !videoUrl.contains('http')) { return; } - pushNamed(AppRouteName.lookVideo,arguments: {'videoUrl':videoUrl,'title':title,'courseLessonId':state.courseLessonId}).then((value) { + pushNamed(AppRouteName.lookVideo, arguments: { + 'videoUrl': videoUrl, + 'title': title, + 'courseLessonId': state.courseLessonId + }).then((value) { if (value != null) { - Map dataMap = value as Map; + Map dataMap = value as Map; bloc.add(RequestExitClassEvent( dataMap['courseLessonId']!, '0', @@ -87,35 +78,37 @@ class _HomePageView extends StatelessWidget { } if (state is RequestEnterClassState) { - if (state.courseType != 3 && state.courseType != 4) {///视频类型 + if (state.courseType != 3 && state.courseType != 4) { + ///视频类型 ///获取视频课程内容 - bloc.add(RequestVideoLessonEvent(state.courseLessonId,state.courseType)); + bloc.add(RequestVideoLessonEvent( + state.courseLessonId, state.courseType)); return; } - if (state.courseType == 4) {//绘本 - pushNamed(AppRouteName.reading, arguments: {'courseLessonId':state.courseLessonId}).then((value) { + if (state.courseType == 4) { + //绘本 + pushNamed(AppRouteName.reading, + arguments: {'courseLessonId': state.courseLessonId}) + .then((value) { if (value != null) { - Map dataMap = value as Map; + Map dataMap = value as Map; bloc.add(RequestExitClassEvent( - dataMap['courseLessonId']!, - dataMap['currentStep']!, - '0' - )); + dataMap['courseLessonId']!, dataMap['currentStep']!, '0')); } }); return; } - if (state.courseType == 3) {//练习 - pushNamed(AppRouteName.topicPic,arguments: {'courseLessonId':state.courseLessonId}).then((value) { + if (state.courseType == 3) { + //练习 + pushNamed(AppRouteName.topicPic, + arguments: {'courseLessonId': state.courseLessonId}) + .then((value) { if (value != null) { - Map dataMap = value as Map; + Map dataMap = value as Map; bloc.add(RequestExitClassEvent( - dataMap['courseLessonId']!, - dataMap['currentStep']!, - '0' - )); + dataMap['courseLessonId']!, dataMap['currentStep']!, '0')); } }); return; @@ -126,9 +119,9 @@ class _HomePageView extends StatelessWidget { ); } - Widget _homeView() => BlocBuilder( - builder: (context, state) { - final bloc = BlocProvider.of(context); + Widget _homeView() => + BlocBuilder(builder: (context, state) { + final bloc = BlocProvider.of(context); return Scaffold( body: Container( color: Colors.white, @@ -136,19 +129,17 @@ class _HomePageView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - HomeTabHeaderWidget( - entity: bloc.modelData, - actionTap: (HeaderActionType type) { - _headerActionEvent(type); - }, - ), + SectionHeaderWidget( + title: bloc.courseUnitDetail.name, + courseModuleCode: bloc.courseUnitEntity.courseModuleCode), Expanded( child: ListView.builder( - itemCount: bloc.modelData?.totalCourseLesson??0, + itemCount: bloc.courseSectionDatas?.length ?? 0, scrollDirection: Axis.horizontal, itemBuilder: (BuildContext context, int index) { - CourseCourseLessons? data = bloc.modelData?.courseLessons?[index]; - if (data?.courseType == 5) { + CourseSectionEntity sectionData = + bloc.courseSectionDatas![index]; + if (sectionData.courseType == 5) { //彩蛋 return GestureDetector( onTap: () { @@ -156,15 +147,17 @@ class _HomePageView extends StatelessWidget { pushNamed(AppRouteName.login); return; } - if (data!.lock!) { + if (sectionData.lock == true) { showToast('当前课程暂未解锁'); return; } + ///进入课堂 - bloc.add(RequestEnterClassEvent(data.id!,data.courseType!)); + bloc.add(RequestEnterClassEvent( + sectionData.id.toString(), sectionData.courseType)); }, - child: HomeBoundsItem( - imageUrl: data?.coverUrl, + child: SectionBoundsItem( + imageUrl: sectionData.coverUrl, ), ); } else { @@ -174,16 +167,18 @@ class _HomePageView extends StatelessWidget { pushNamed(AppRouteName.login); return; } - if (data!.lock!) { + if (sectionData.lock == true) { showToast('当前课程暂未解锁'); return; } + ///进入课堂 - bloc.add(RequestEnterClassEvent(data.id!,data.courseType!)); + bloc.add(RequestEnterClassEvent( + sectionData.id.toString(), sectionData.courseType)); }, - child: HomeVideoItem( - entity: bloc.modelData, - lessons: data, + child: SectionVideoItem( + unitEntity: bloc.courseUnitEntity, + lessons: sectionData, ), ); } @@ -200,17 +195,26 @@ class _HomePageView extends StatelessWidget { ), Container( decoration: BoxDecoration( - color: CourseModuleModel(bloc.modelData?.courseModuleCode??'Phase-1').color, + color: CourseModuleModel( + bloc.courseUnitEntity.courseModuleCode ?? + 'Phase-1') + .color, borderRadius: BorderRadius.circular(14.5.r), ), - padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 24.w), + padding: EdgeInsets.symmetric( + vertical: 8.h, horizontal: 24.w), child: Text( - '${(bloc.modelData?.nowCourseLesson??0)}/${bloc.modelData?.totalCourseLesson??0}', - style: TextStyle(color: Colors.white, fontSize: 12.sp), + '${(bloc.courseUnitEntity.nowStep ?? 0)}/${bloc.courseUnitEntity.total ?? 0}', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), ), ), Image.asset( - CourseModuleModel(bloc.modelData?.courseModuleCode??'Phase-1').courseModuleLogo.assetPng, + CourseModuleModel( + bloc.courseUnitEntity.courseModuleCode ?? + 'Phase-1') + .courseModuleLogo + .assetPng, height: 47.h, width: 80.w, // color: Colors.red, diff --git a/lib/pages/home/widgets/home_video_item.dart b/lib/pages/section/widgets/home_video_item.dart index 5917e54..7542f42 100644 --- a/lib/pages/home/widgets/home_video_item.dart +++ b/lib/pages/section/widgets/home_video_item.dart @@ -2,15 +2,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/ow_image_widget.dart'; -import 'package:wow_english/models/course_entity.dart'; +import '../../../models/course_section_entity.dart'; +import '../../../models/course_unit_entity.dart'; import '../courese_module_model.dart'; -class HomeVideoItem extends StatelessWidget { - const HomeVideoItem({super.key, this.lessons, this.entity}); +class SectionVideoItem extends StatelessWidget { + const SectionVideoItem({super.key, this.lessons, this.unitEntity}); - final CourseEntity? entity; - final CourseCourseLessons? lessons; + final CourseUnitEntity? unitEntity; + final CourseSectionEntity? lessons; @override Widget build(BuildContext context) { @@ -50,7 +51,7 @@ class HomeVideoItem extends StatelessWidget { width: 2, color: const Color(0xFF140C10), ), - color: CourseModuleModel(entity?.courseModuleCode??'Phase-1').color, + color: CourseModuleModel(unitEntity?.courseModuleCode??'Phase-1').color, borderRadius: BorderRadius.circular(6) ), padding: EdgeInsets.symmetric(horizontal: 10.w), diff --git a/lib/pages/home/widgets/home_bouns_item.dart b/lib/pages/section/widgets/section_bouns_item.dart index 27fa3da..f9e779c 100644 --- a/lib/pages/home/widgets/home_bouns_item.dart +++ b/lib/pages/section/widgets/section_bouns_item.dart @@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/widgets/ow_image_widget.dart'; -class HomeBoundsItem extends StatelessWidget { - const HomeBoundsItem({super.key, this.imageUrl}); +class SectionBoundsItem extends StatelessWidget { + const SectionBoundsItem({super.key, this.imageUrl}); final String? imageUrl; diff --git a/lib/pages/unit/widget/course_unit_header_widget.dart b/lib/pages/section/widgets/section_header_widget.dart index 47c3866..91833df 100644 --- a/lib/pages/unit/widget/course_unit_header_widget.dart +++ b/lib/pages/section/widgets/section_header_widget.dart @@ -4,13 +4,14 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; -import '../../../models/course_module_entity.dart'; -import '../../home/courese_module_model.dart'; +import '../courese_module_model.dart'; -class CourseUnitHeaderWidget extends StatelessWidget { - const CourseUnitHeaderWidget({super.key, this.entity}); +class SectionHeaderWidget extends StatelessWidget { + const SectionHeaderWidget({super.key, this.title, this.courseModuleCode}); - final CourseModuleEntity? entity; + final String? title; + + final String? courseModuleCode; @override Widget build(BuildContext context) { @@ -19,8 +20,7 @@ class CourseUnitHeaderWidget extends StatelessWidget { return Container( height: 45, width: double.infinity, - color: - CourseModuleModel(entity?.code ?? 'Phase-1').color, + color: CourseModuleModel(courseModuleCode ?? 'Phase-1').color, padding: EdgeInsets.symmetric(horizontal: 9.5.w), child: Row( children: [ @@ -40,9 +40,10 @@ class CourseUnitHeaderWidget extends StatelessWidget { ), 20.horizontalSpace, Expanded( - child: Text(entity?.name ?? - CourseModuleModel(entity?.code ?? 'Phase-1') - .courseModuleTitle, + child: Text( + title ?? + CourseModuleModel(courseModuleCode ?? 'Phase-1') + .courseModuleTitle, textAlign: TextAlign.left, style: const TextStyle(color: Colors.white, fontSize: 30.0), )), diff --git a/lib/pages/tab/tab_page.dart b/lib/pages/tab/tab_page.dart index 143d8f3..70ad7e7 100644 --- a/lib/pages/tab/tab_page.dart +++ b/lib/pages/tab/tab_page.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:wow_english/pages/home/home_page.dart'; -import 'package:wow_english/pages/lessons/lesson_page.dart'; +import 'package:wow_english/pages/module/module_page.dart'; +import '../unit/view.dart'; import 'blocs/tab_bloc.dart'; class TabPage extends StatelessWidget { const TabPage({super.key}); final _pages =const [ - HomePage(), - LessonPage() + UnitPage(), + ModulePage() ]; final _tabIcons = const [ diff --git a/lib/pages/unit/bloc.dart b/lib/pages/unit/bloc.dart index 767b510..d54cd60 100644 --- a/lib/pages/unit/bloc.dart +++ b/lib/pages/unit/bloc.dart @@ -1,8 +1,11 @@ import 'package:bloc/bloc.dart'; +import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; -import '../../common/request/dao/home_dao.dart'; +import '../../common/request/dao/lesson_dao.dart'; import '../../common/request/exception.dart'; +import '../../models/course_module_entity.dart'; import '../../models/course_unit_entity.dart'; +import '../../route/route.dart'; import '../../utils/loading.dart'; import '../../utils/toast_util.dart'; import 'event.dart'; @@ -10,19 +13,23 @@ import 'state.dart'; class UnitBloc extends Bloc { - CourseUnitEntity? _modelData; + CourseModuleEntity? _moduleEntity; - CourseUnitEntity? get modelData => _modelData; + CourseModuleEntity? get moduleEntity => _moduleEntity; + CourseUnitEntity? _unitData; - UnitBloc() : super(UnitState().init()) { + CourseUnitEntity? get unitData => _unitData; + + + UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) { on(_requestData); } void _requestData(RequestUnitDataEvent event, Emitter emitter) async { try { await loading(() async { - _modelData = await HomeDao.courseUnit(event.moduleId); + _unitData = await LessonDao.courseUnit(event.moduleId); emitter(UnitDataLoadState()); }); } catch (e) { @@ -31,4 +38,22 @@ class UnitBloc extends Bloc { } } } + + String? getCourseModuleCode() { + return _moduleEntity?.code ?? _unitData?.courseModuleCode; + } + + void headerActionEvent(HeaderActionType type) { + if (type == HeaderActionType.video) { + pushNamed(AppRouteName.reAfter); + } else if (type == HeaderActionType.phase) { + pushNamed(AppRouteName.courseModule); + } else if (type == HeaderActionType.listen) { + pushNamed(AppRouteName.listen); + } else if (type == HeaderActionType.shop) { + pushNamed(AppRouteName.shop); + } else if (type == HeaderActionType.user) { + pushNamed(AppRouteName.user); + } + } } diff --git a/lib/pages/unit/event.dart b/lib/pages/unit/event.dart index 6acbed4..53a8b93 100644 --- a/lib/pages/unit/event.dart +++ b/lib/pages/unit/event.dart @@ -2,7 +2,7 @@ abstract class UnitEvent {} // 获取课程单元数据 class RequestUnitDataEvent extends UnitEvent { - final int moduleId; + final int? moduleId; RequestUnitDataEvent(this.moduleId); } diff --git a/lib/pages/unit/view.dart b/lib/pages/unit/view.dart index fb24b78..f61d56b 100644 --- a/lib/pages/unit/view.dart +++ b/lib/pages/unit/view.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/pages/unit/state.dart'; -import 'package:wow_english/pages/unit/widget/course_unit_header_widget.dart'; import 'package:wow_english/pages/unit/widget/course_unit_item.dart'; +import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; import 'package:wow_english/route/route.dart'; import '../../models/course_module_entity.dart'; @@ -12,17 +12,18 @@ import '../../utils/toast_util.dart'; import 'bloc.dart'; import 'event.dart'; +// 课程列表页(多unit,参考口语星球的框或分割标志) class UnitPage extends StatelessWidget { - const UnitPage({super.key, required this.courseEntity}); + const UnitPage({super.key, this.courseModuleEntity}); /// 模块 - final CourseModuleEntity courseEntity; + final CourseModuleEntity? courseModuleEntity; @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => - UnitBloc()..add(RequestUnitDataEvent(courseEntity.id)), + create: (BuildContext context) => UnitBloc(courseModuleEntity) + ..add(RequestUnitDataEvent(courseModuleEntity?.id)), child: Builder(builder: (context) => _buildPage(context)), ); } @@ -37,36 +38,42 @@ class UnitPage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - CourseUnitHeaderWidget(entity: courseEntity), + HomeTabHeaderWidget( + courseModuleCode: bloc.getCourseModuleCode(), + actionTap: (HeaderActionType type) { + bloc.headerActionEvent(type); + }, + ), Expanded( - child: ListView.builder( - itemCount: - bloc.modelData?.courseUnitVOList?.length ?? 0, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - CourseUnitDetail? data = - bloc.modelData?.courseUnitVOList?[index]; - return GestureDetector( - onTap: () { - if (data.lock == true) { - showToast('当前unit暂未解锁'); - return; - } + child: Container( + margin: EdgeInsets.symmetric(horizontal: 12.w), + child: ListView.builder( + itemCount: + bloc.unitData?.courseUnitVOList?.length ?? 0, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + CourseUnitDetail? data = + bloc.unitData?.courseUnitVOList?[index]; + return GestureDetector( + onTap: () { + if (data.lock == true) { + showToast('当前unit暂未解锁'); + return; + } - ///进入课堂 - pushNamedAndRemoveUntil( - AppRouteName.home, (route) => route.isFirst, - arguments: { - 'moduleId': data.courseModuleId, - 'unitId': data.id - }); - }, - child: CourseUnitItem( - unitEntity: bloc.modelData!, - unitLesson: data!, - ), - ); - })), + pushNamed(AppRouteName.courseSection, + arguments: { + 'courseUnitEntity': bloc.unitData, + 'courseUnitDetail': data + }); + }, + child: CourseUnitItem( + unitEntity: bloc.unitData!, + unitLesson: data!, + ), + ); + })), + ), SafeArea( child: Column( children: [ diff --git a/lib/pages/home/widgets/home_tab_header_widget.dart b/lib/pages/unit/widget/home_tab_header_widget.dart index 0c544c4..465f8c9 100644 --- a/lib/pages/home/widgets/home_tab_header_widget.dart +++ b/lib/pages/unit/widget/home_tab_header_widget.dart @@ -5,8 +5,7 @@ import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; import '../../../common/core/app_config_helper.dart'; -import '../../../models/course_entity.dart'; -import '../courese_module_model.dart'; +import '../../section/courese_module_model.dart'; enum HeaderActionType { //视频跟读 @@ -19,14 +18,13 @@ enum HeaderActionType { shop, //个人信息 user, - //返回到(模块选择)首页 - home, } class HomeTabHeaderWidget extends StatelessWidget { - const HomeTabHeaderWidget({super.key, this.entity, this.actionTap}); + const HomeTabHeaderWidget({super.key, this.courseModuleCode, this.actionTap}); + + final String? courseModuleCode; - final CourseEntity? entity; final Function(HeaderActionType type)? actionTap; @override @@ -37,16 +35,14 @@ class HomeTabHeaderWidget extends StatelessWidget { height: 45, width: double.infinity, color: - CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1').color, + CourseModuleModel(courseModuleCode ?? 'Phase-1').color, padding: EdgeInsets.symmetric(horizontal: 9.5.w), child: Row( children: [ ScreenUtil().bottomBarHeight.horizontalSpace, GestureDetector( onTap: () { - if (actionTap != null) { - actionTap!(HeaderActionType.home); - } + Navigator.pop(context); }, child: Container( alignment: Alignment.center, @@ -96,7 +92,7 @@ class HomeTabHeaderWidget extends StatelessWidget { 20.horizontalSpace, Expanded( child: Text( - CourseModuleModel(entity?.courseModuleCode ?? 'Phase-1') + CourseModuleModel(courseModuleCode ?? 'Phase-1') .courseModuleTitle, textAlign: TextAlign.left, style: const TextStyle(color: Colors.white, fontSize: 30.0), diff --git a/lib/route/route.dart b/lib/route/route.dart index 0a987c3..49020a9 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -3,22 +3,21 @@ import 'package:flutter/material.dart'; import 'package:wow_english/app/splash_page.dart'; import 'package:wow_english/common/pages/wow_web_page.dart'; import 'package:wow_english/generated/json/base/json_convert_content.dart'; +import 'package:wow_english/models/course_unit_entity.dart'; import 'package:wow_english/models/product_entity.dart'; import 'package:wow_english/pages/games/view.dart'; -import 'package:wow_english/pages/home/home_page.dart'; -import 'package:wow_english/pages/lessons/lesson_page.dart'; +import 'package:wow_english/pages/home/view.dart'; import 'package:wow_english/pages/listen/listen_page.dart'; import 'package:wow_english/pages/login/forgetpwd/forget_password_home_page.dart'; import 'package:wow_english/pages/login/loginpage/login_page.dart'; import 'package:wow_english/pages/login/setpwd/set_pwd_page.dart'; -import 'package:wow_english/pages/moduleSelect/view.dart'; +import 'package:wow_english/pages/module/module_page.dart'; import 'package:wow_english/pages/practice/topic_picture_page.dart'; import 'package:wow_english/pages/repeatafter/repeat_after_page.dart'; import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart'; import 'package:wow_english/pages/shop/exchane/exchange_lesson_page.dart'; import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.dart'; import 'package:wow_english/pages/shop/home/shop_home_page.dart'; -import 'package:wow_english/pages/tab/tab_page.dart'; import 'package:wow_english/pages/user/information/user_information_page.dart'; import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart'; import 'package:wow_english/pages/user/modify/modify_user_information_page.dart'; @@ -28,7 +27,9 @@ import 'package:wow_english/pages/video/lookvideo/look_video_page.dart'; import '../models/course_module_entity.dart'; import '../pages/reading/reading_page.dart'; +import '../pages/section/section_page.dart'; import '../pages/shopping/view.dart'; +import '../pages/tab/tab_page.dart'; import '../pages/unit/view.dart'; import '../pages/user/setting/delete_account_page.dart'; import '../pages/user/setting/reback_page.dart'; @@ -36,16 +37,16 @@ import '../pages/user/setting/reback_page.dart'; class AppRouteName { static const String splash = 'splash'; static const String login = 'login'; - static const String moduleSelect = 'moduleSelect'; - static const String games = 'games'; static const String home = 'home'; + static const String games = 'games'; static const String fogPwd = 'fogPwd'; /// 设置密码,修改密码;不要自己调用,使用[SetPassWordPage.push]方法,隐藏这种实现 //static const String setPwd = 'setPwd'; static const String webView = 'webView'; - static const String unit = 'courseUnits'; - static const String lesson = 'courseModules'; + static const String courseModule = 'courseModules'; + static const String courseUnit = 'courseUnits'; + static const String courseSection = 'courseSections'; static const String listen = 'listen'; static const String shop = 'shop'; static const String exLesson = 'exLesson'; @@ -57,6 +58,7 @@ class AppRouteName { /// 用户详细信息页 static const String userInformation = 'userInformation'; + /// 修改用户头像 static const String userAvatar = 'userAvatar'; @@ -64,15 +66,19 @@ class AppRouteName { //static const String userModifyInformation = 'userModifyInformation'; ///看视频 static const String lookVideo = 'lookVideo'; + ///绘本 static const String reading = 'reading'; + ///视频跟读详情 static const String readAfterContent = 'readAfterContent'; ///设置 static const String setting = 'setting'; + ///注销账号 static const String deleteAccount = 'deleteAccount'; + ///帮助与反馈 static const String reBack = 'reBack'; @@ -95,31 +101,42 @@ class AppRouter { transitionsBuilder: (_, __, ___, child) => child); case AppRouteName.login: // 是否默认密码登录,修改密码后跳登录页,优先密码登录体验好点 - final bool showPasswordPage = (settings.arguments as Map?)?.getOrNull('showPasswordPage') as bool? ?? false; - return CupertinoPageRoute(builder: (_) => LoginPage(preferencesPasswordLogin: showPasswordPage)); - case AppRouteName.moduleSelect: - return CupertinoPageRoute(builder: (_) => const ModuleSelectPage()); + final bool showPasswordPage = (settings.arguments as Map?) + ?.getOrNull('showPasswordPage') as bool? ?? + false; + return CupertinoPageRoute( + builder: (_) => + LoginPage(preferencesPasswordLogin: showPasswordPage)); + case AppRouteName.home: + return CupertinoPageRoute(builder: (_) => const HomePage()); case AppRouteName.games: return CupertinoPageRoute(builder: (_) => const GamesPage()); - case AppRouteName.home: - int? moduleId; + case AppRouteName.fogPwd: + return CupertinoPageRoute( + builder: (_) => const ForgetPasswordHomePage()); + case AppRouteName.courseModule: + return CupertinoPageRoute(builder: (_) => const ModulePage()); + case AppRouteName.courseUnit: + CourseModuleEntity courseModuleEntity = CourseModuleEntity(); if (settings.arguments != null) { - moduleId = (settings.arguments as Map).getOrNull('moduleId') as int?; + courseModuleEntity = (settings.arguments as Map) + .getOrNull('courseModuleEntity') as CourseModuleEntity; } return CupertinoPageRoute( - builder: (_) => HomePage( - moduleId: moduleId, - )); - case AppRouteName.fogPwd: - return CupertinoPageRoute(builder: (_) => const ForgetPasswordHomePage()); - case AppRouteName.lesson: - return CupertinoPageRoute(builder: (_) => const LessonPage()); - case AppRouteName.unit: - CourseModuleEntity courseEntity = CourseModuleEntity(); + builder: (_) => UnitPage(courseModuleEntity: courseModuleEntity)); + case AppRouteName.courseSection: + CourseUnitEntity courseUnitEntity = CourseUnitEntity(); + CourseUnitDetail courseUnitDetail = CourseUnitDetail(); if (settings.arguments != null) { - courseEntity = (settings.arguments as Map).getOrNull('courseModuleEntity') as CourseModuleEntity; + courseUnitEntity = (settings.arguments as Map) + .getOrNull('courseUnitEntity') as CourseUnitEntity; + courseUnitDetail = (settings.arguments as Map) + .getOrNull('courseUnitDetail') as CourseUnitDetail; } - return CupertinoPageRoute(builder: (_) => UnitPage(courseEntity: courseEntity)); + return CupertinoPageRoute( + builder: (_) => SectionPage( + courseUnitEntity: courseUnitEntity, + courseUnitDetail: courseUnitDetail)); case AppRouteName.listen: return CupertinoPageRoute(builder: (_) => const ListenPage()); case AppRouteName.shop: @@ -129,11 +146,13 @@ class AppRouter { if (settings.arguments != null && settings.arguments is ProductEntity) { productEntity = settings.arguments as ProductEntity; } - return CupertinoPageRoute(builder: (_) => ShoppingPage(productEntity: productEntity)); + return CupertinoPageRoute( + builder: (_) => ShoppingPage(productEntity: productEntity)); case AppRouteName.exLesson: return CupertinoPageRoute(builder: (_) => const ExchangeLessonPage()); case AppRouteName.exList: - return CupertinoPageRoute(builder: (_) => const ExchangeLessonListPage()); + return CupertinoPageRoute( + builder: (_) => const ExchangeLessonListPage()); case AppRouteName.reAfter: return CupertinoPageRoute(builder: (_) => const RepeatAfterPage()); case AppRouteName.user: @@ -161,19 +180,22 @@ class AppRouter { case AppRouteName.topicPic: var courseLessonId = ''; if (settings.arguments != null) { - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String; + courseLessonId = + (settings.arguments as Map)['courseLessonId'] as String; } - return CupertinoPageRoute(builder: (_) => TopicPicturePage(courseLessonId: courseLessonId)); + return CupertinoPageRoute( + builder: (_) => TopicPicturePage(courseLessonId: courseLessonId)); case AppRouteName.lookVideo: final videoUrl = (settings.arguments as Map)['videoUrl'] as String; final title = (settings.arguments as Map)['title'] as String?; - final courseLessonId = (settings.arguments as Map)['courseLessonId'] as String?; + final courseLessonId = + (settings.arguments as Map)['courseLessonId'] as String?; return CupertinoPageRoute( builder: (_) => LookVideoPage( - videoUrl: videoUrl, - typeTitle: title, - courseLessonId: courseLessonId, - )); + videoUrl: videoUrl, + typeTitle: title, + courseLessonId: courseLessonId, + )); /*case AppRouteName.setPwd: case AppRouteName.setPwd: phoneNum: phoneNum, @@ -182,7 +204,8 @@ class AppRouter { ));*/ case AppRouteName.webView: final urlStr = (settings.arguments as Map)['urlStr'] as String; - final webViewTitle = (settings.arguments as Map)['webViewTitle'] as String; + final webViewTitle = + (settings.arguments as Map)['webViewTitle'] as String; return CupertinoPageRoute( builder: (_) => WowWebViewPage( urlStr: urlStr, @@ -191,13 +214,16 @@ class AppRouter { case AppRouteName.readAfterContent: var videoFollowReadId = ''; if (settings.arguments != null) { - videoFollowReadId = (settings.arguments as Map)['videoFollowReadId'] as String; + videoFollowReadId = + (settings.arguments as Map)['videoFollowReadId'] as String; } - return CupertinoPageRoute(builder: (_) => RepeatAfterContentPage(videoFollowReadId: videoFollowReadId)); + return CupertinoPageRoute( + builder: (_) => + RepeatAfterContentPage(videoFollowReadId: videoFollowReadId)); case AppRouteName.setting: - return CupertinoPageRoute(builder: (_) => const SettingPage()); + return CupertinoPageRoute(builder: (_) => const SettingPage()); case AppRouteName.deleteAccount: - return CupertinoPageRoute(builder: (_) => const DeleteAccountPage()); + return CupertinoPageRoute(builder: (_) => const DeleteAccountPage()); case AppRouteName.reBack: return CupertinoPageRoute(builder: (_) => const ReBackPage()); case AppRouteName.tab: @@ -210,26 +236,34 @@ class AppRouter { case AppRouteName.reading: var courseLessonId = ''; if (settings.arguments != null) { - courseLessonId = (settings.arguments as Map)['courseLessonId'] as String; + courseLessonId = + (settings.arguments as Map)['courseLessonId'] as String; } - return CupertinoPageRoute(builder: (_) => ReadingPage(courseLessonId: courseLessonId)); + return CupertinoPageRoute( + builder: (_) => ReadingPage(courseLessonId: courseLessonId)); default: return CupertinoPageRoute( - builder: (_) => Scaffold(body: Center(child: Text('No route defined for ${settings.name}')))); + builder: (_) => Scaffold( + body: Center( + child: Text('No route defined for ${settings.name}')))); } } } Future pushNamed(String routeName, {Object? arguments}) { - return Navigator.of(AppRouter.context).pushNamed(routeName, arguments: arguments).then((value) { + return Navigator.of(AppRouter.context) + .pushNamed(routeName, arguments: arguments) + .then((value) { return value; }); } -Future pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate, {Object? arguments}) { - return Navigator.of(AppRouter.context).pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments); +Future pushNamedAndRemoveUntil(String routeName, RoutePredicate predicate, + {Object? arguments}) { + return Navigator.of(AppRouter.context) + .pushNamedAndRemoveUntil(routeName, predicate, arguments: arguments); } void popPage({dynamic data}) { - Navigator.of(AppRouter.context).pop(data); + Navigator.of(AppRouter.context).pop(data); }