diff --git a/lib/pages/section/bloc/section_bloc.dart b/lib/pages/section/bloc/section_bloc.dart index ef3b3f0..9943fd2 100644 --- a/lib/pages/section/bloc/section_bloc.dart +++ b/lib/pages/section/bloc/section_bloc.dart @@ -10,12 +10,26 @@ import 'package:wow_english/utils/toast_util.dart'; import '../../../models/course_section_entity.dart'; import '../../../models/course_unit_entity.dart'; +import '../../../utils/list_ext.dart'; part 'section_event.dart'; part 'section_state.dart'; class SectionBloc extends Bloc { + PageController _pageController; + + PageController get pageController => _pageController; + + ///当前页索引 + int _currentPage = 0; + + int get currentPage => _currentPage; + + ScrollController _listController; + + ScrollController get listController => _listController; + CourseUnitEntity _courseUnitEntity; CourseUnitEntity get courseUnitEntity => _courseUnitEntity; @@ -32,11 +46,12 @@ class SectionBloc extends Bloc { CourseProcessEntity? get processEntity => _processEntity; - SectionBloc(this._courseUnitEntity, this._courseUnitDetail) : super(LessonInitial()) { + SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) { on(_requestData); on(_requestExitClass); on(_requestEnterClass); on(_requestVideoLesson); + on(_pageControllerChange); } void _requestData(RequestDataEvent event, Emitter emitter) async { @@ -82,4 +97,15 @@ class SectionBloc extends Bloc { void _requestExitClass(RequestExitClassEvent event,Emitter emitter) async { await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime); } + + void _pageControllerChange(CurrentUnitIndexChangeEvent event, + Emitter emitter) async { + _currentPage = event.unitIndex; + emitter(CurrentPageIndexState()); + } + + // 未锁定的页数 + int unlockPageCount() { + return _courseUnitEntity.courseUnitVOList?.indexWhereOrNull((element) => element.lock == true) ?? 1; + } } diff --git a/lib/pages/section/bloc/section_event.dart b/lib/pages/section/bloc/section_event.dart index cff955d..c21f727 100644 --- a/lib/pages/section/bloc/section_event.dart +++ b/lib/pages/section/bloc/section_event.dart @@ -26,3 +26,9 @@ class RequestExitClassEvent extends SectionEvent { final String currentTime; RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime); } + +///页面切换 +class CurrentUnitIndexChangeEvent extends SectionEvent { + final int unitIndex; + CurrentUnitIndexChangeEvent(this.unitIndex); +} diff --git a/lib/pages/section/bloc/section_state.dart b/lib/pages/section/bloc/section_state.dart index 9c50452..47aec56 100644 --- a/lib/pages/section/bloc/section_state.dart +++ b/lib/pages/section/bloc/section_state.dart @@ -10,11 +10,15 @@ class LessonDataLoadState extends SectionState {} class RequestVideoLessonState extends SectionState { final String courseLessonId; final int type; - RequestVideoLessonState(this.courseLessonId,this.type); + + RequestVideoLessonState(this.courseLessonId, this.type); } -class RequestEnterClassState extends SectionState{ +class RequestEnterClassState extends SectionState { final String courseLessonId; final int courseType; - RequestEnterClassState(this.courseLessonId,this.courseType); + + RequestEnterClassState(this.courseLessonId, this.courseType); } + +class CurrentPageIndexState extends SectionState {} diff --git a/lib/pages/section/section_page.dart b/lib/pages/section/section_page.dart index 3bb055c..d5b9a2e 100644 --- a/lib/pages/section/section_page.dart +++ b/lib/pages/section/section_page.dart @@ -1,6 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:nested_scroll_views/material.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_unit_entity.dart'; @@ -16,7 +18,10 @@ import 'courese_module_model.dart'; /// 环节列表页 class SectionPage extends StatelessWidget { - const SectionPage({super.key, required this.courseUnitEntity, required this.courseUnitDetail}); + const SectionPage( + {super.key, + required this.courseUnitEntity, + required this.courseUnitDetail}); final CourseUnitEntity courseUnitEntity; @@ -26,7 +31,9 @@ class SectionPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail)..add(RequestDataEvent()), + create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail, + PageController(), ScrollController()) + ..add(RequestDataEvent()), child: _SectionPageView(context), ); } @@ -115,11 +122,11 @@ class _SectionPageView extends StatelessWidget { } } }, - child: _homeView(), + child: _sectionView(), ); } - Widget _homeView() => + Widget _sectionView() => BlocBuilder(builder: (context, state) { final bloc = BlocProvider.of(context); return Scaffold( @@ -133,96 +140,65 @@ class _SectionPageView extends StatelessWidget { title: bloc.courseUnitDetail.name, courseModuleCode: bloc.courseUnitEntity.courseModuleCode), Expanded( - child: ListView.builder( - itemCount: bloc.courseSectionDatas?.length ?? 0, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - CourseSectionEntity sectionData = - bloc.courseSectionDatas![index]; - if (sectionData.courseType == 5) { - //彩蛋 - return GestureDetector( - onTap: () { - if (!UserUtil.isLogined()) { - pushNamed(AppRouteName.login); - return; - } - if (sectionData.lock == true) { - showToast('当前课程暂未解锁'); - return; - } - - ///进入课堂 - bloc.add(RequestEnterClassEvent( - sectionData.id.toString(), sectionData.courseType)); - }, - child: SectionBoundsItem( - imageUrl: sectionData.coverUrl, - ), - ); - } else { - return GestureDetector( - onTap: () { - if (!UserUtil.isLogined()) { - pushNamed(AppRouteName.login); - return; - } - if (sectionData.lock == true) { - showToast('当前课程暂未解锁'); - return; - } - - ///进入课堂 - bloc.add(RequestEnterClassEvent( - sectionData.id.toString(), sectionData.courseType)); - }, - child: SectionVideoItem( - unitEntity: bloc.courseUnitEntity, - lessons: sectionData, - ), - ); - } - })), - // SafeArea( - // child: Padding( - // padding: EdgeInsets.symmetric(horizontal: 13.w), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // SizedBox( - // height: 47.h, - // width: 80.w, - // ), - // Container( - // decoration: BoxDecoration( - // color: CourseModuleModel( - // bloc.courseUnitEntity.courseModuleCode ?? - // 'Phase-1') - // .color, - // borderRadius: BorderRadius.circular(14.5.r), - // ), - // padding: EdgeInsets.symmetric( - // vertical: 8.h, horizontal: 24.w), - // child: Text( - // '${(bloc.courseUnitEntity.nowStep ?? 0)}/${bloc.courseUnitEntity.total ?? 0}', - // style: TextStyle( - // color: Colors.white, fontSize: 12.sp), - // ), - // ), - // Image.asset( - // CourseModuleModel( - // bloc.courseUnitEntity.courseModuleCode ?? - // 'Phase-1') - // .courseModuleLogo - // .assetPng, - // height: 47.h, - // width: 80.w, - // // color: Colors.red, - // ), - // ], - // ), - // ), - // ) + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: OverflowBox( + child: NestedPageView.builder( + itemCount: bloc.unlockPageCount(), + controller: bloc.pageController, + onPageChanged: (int index) { + bloc.add(CurrentUnitIndexChangeEvent(index)); + }, + itemBuilder: (context, index) { + return ScrollConfiguration( + ///去掉 Android 上默认的边缘拖拽效果 + behavior: ScrollConfiguration.of(context) + .copyWith(overscroll: false), + child: _itemTransCard(index, context), + ); + }), + ), // 设置外部padding, + )), + SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 13.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + height: 47.h, + width: 80.w, + ), + Container( + decoration: BoxDecoration( + color: CourseModuleModel( + bloc.courseUnitEntity.courseModuleCode ?? + 'Phase-1') + .color, + borderRadius: BorderRadius.circular(14.5.r), + ), + padding: EdgeInsets.symmetric( + vertical: 8.h, horizontal: 24.w), + child: Text( + '${bloc.currentPage + 1}/${bloc.unlockPageCount()}', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), + ), + ), + Image.asset( + CourseModuleModel( + bloc.courseUnitEntity.courseModuleCode ?? + 'Phase-1') + .courseModuleLogo + .assetPng, + height: 47.h, + width: 80.w, + // color: Colors.red, + ), + ], + ), + ), + ) ], ), ), @@ -230,3 +206,56 @@ class _SectionPageView extends StatelessWidget { ); }); } + +Widget _itemTransCard(int index, BuildContext context) { + final bloc = BlocProvider.of(context); + return NestedListView.builder( + itemCount: bloc.courseSectionDatas?.length ?? 0, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + CourseSectionEntity sectionData = bloc.courseSectionDatas![index]; + if (sectionData.courseType == 5) { + //彩蛋 + return GestureDetector( + onTap: () { + if (!UserUtil.isLogined()) { + pushNamed(AppRouteName.login); + return; + } + if (sectionData.lock == true) { + showToast('当前课程暂未解锁'); + return; + } + + ///进入课堂 + bloc.add(RequestEnterClassEvent( + sectionData.id.toString(), sectionData.courseType)); + }, + child: SectionBoundsItem( + imageUrl: sectionData.coverUrl, + ), + ); + } else { + return GestureDetector( + onTap: () { + if (!UserUtil.isLogined()) { + pushNamed(AppRouteName.login); + return; + } + if (sectionData.lock == true) { + showToast('当前课程暂未解锁'); + return; + } + + ///进入课堂 + bloc.add(RequestEnterClassEvent( + sectionData.id.toString(), sectionData.courseType)); + }, + child: SectionVideoItem( + unitEntity: bloc.courseUnitEntity, + lessons: sectionData, + ), + ); + } + }); +} diff --git a/lib/utils/list_ext.dart b/lib/utils/list_ext.dart new file mode 100644 index 0000000..c7ac32a --- /dev/null +++ b/lib/utils/list_ext.dart @@ -0,0 +1,11 @@ + + +extension ListExtension on List { + /// 获取数组中第一个匹配元素的index,没有就返回null + int? indexWhereOrNull(bool Function(E element) test) { + for (int i = 0; i < length; i++) { + if (test(this[i])) return i; + } + return null; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index bff08ce..d4cf9f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -111,6 +111,8 @@ dependencies: umeng_common_sdk: ^1.2.7 # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk umeng_apm_sdk: ^2.2.1 + # 嵌套滚动 https://pub.dev/packages/nested_scroll_views + nested_scroll_views: ^0.0.10 dev_dependencies: build_runner: ^2.4.4