import 'package:audioplayers/audioplayers.dart'; 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'; import 'package:wow_english/pages/section/section_type.dart'; import 'package:wow_english/pages/section/widgets/section_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/audio_player_util.dart'; import 'package:wow_english/utils/log_util.dart'; import 'package:wow_english/utils/toast_util.dart'; import '../../models/course_section_entity.dart'; import 'bloc/section_bloc.dart'; import 'courese_module_model.dart'; /// 环节列表页 class SectionPage extends StatelessWidget { const SectionPage( {super.key, required this.courseUnitEntity, required this.courseUnitId}); final CourseUnitEntity courseUnitEntity; /// unitId final int courseUnitId; @override Widget build(BuildContext context) { int initialPage = courseUnitEntity.courseUnitVOList ?.indexWhere((element) => element.id == courseUnitId) ?? 0; return BlocProvider( create: (context) => SectionBloc( courseUnitEntity, initialPage, PageController(initialPage: initialPage), ScrollController(), ScrollController()) ..add(InitEvent()), //为了触发指示器进入后计算位置 // ..add(CurrentUnitIndexChangeEvent(initialPage)), child: _SectionPageView(context), ); } } class _SectionPageView extends StatelessWidget { const _SectionPageView(this.context); final BuildContext context; @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return BlocListener( listener: (context, state) async { if (state is RequestVideoLessonState) { final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; var title = ''; if (state.type == SectionType.song.value) { title = 'song'; } if (state.type == SectionType.video.value) { title = 'video'; } if (state.type == SectionType.bouns.value) { title = 'bonus'; } if (videoUrl.isEmpty || !videoUrl.contains('http')) { return; } pushNamed(AppRouteName.lookVideo, arguments: { 'videoUrl': videoUrl, 'title': title, 'courseLessonId': state.courseLessonId, 'isTopic': true }).then((value) { if (value != null) { Map dataMap = value as Map; bloc.add(RequestEndClassEvent( dataMap['courseLessonId']!, dataMap['isCompleted'], currentTime: dataMap['currentTime'], autoNextSection: dataMap['nextSection'])); } AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.countWithMe); }); return; } if (state is RequestEnterClassState) { if (state.courseType != SectionType.practice.value && state.courseType != SectionType.pictureBook.value) { ///视频类型 ///获取视频课程内容 if (state.courseType == 1) { await AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.musicTime); } else { await AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.videoTime); } bloc.add(RequestVideoLessonEvent( state.courseLessonId, state.courseType)); return; } if (state.courseType == SectionType.pictureBook.value) { await AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.readingTime); //绘本 pushNamed(AppRouteName.reading, arguments: {'courseLessonId': state.courseLessonId}) .then((value) { if (value != null) { Map dataMap = value as Map; bloc.add(RequestEndClassEvent( dataMap['courseLessonId']!, dataMap['isCompleted'], currentStep: dataMap['currentStep'], autoNextSection: dataMap['nextSection'], )); AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.countWithMe); } }); return; } if (state.courseType == SectionType.practice.value) { //练习 await AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.quizTime); pushNamed(AppRouteName.topicPic, arguments: {'courseLessonId': state.courseLessonId}) .then((value) { if (value != null) { Map dataMap = value as Map; bloc.add(RequestEndClassEvent( dataMap['courseLessonId']!, dataMap['isCompleted'], currentStep: dataMap['currentStep'], autoNextSection: dataMap['nextSection'])); } AudioPlayerUtil.getInstance() .playAudio(AudioPlayerUtilType.countWithMe); }); return; } } }, child: _sectionView(), ); } Widget _sectionView() => BlocBuilder(builder: (context, state) { final bloc = BlocProvider.of(context); return Scaffold( body: Container( color: Colors.white, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SectionHeaderWidget( title: bloc.getCourseUnitDetail().name, courseModuleCode: bloc.courseUnitEntity.courseModuleCode, onBack: () { popPage(data: { 'needRefresh': bloc.courseUnitEntityChanged, }); }), Expanded( child: Padding( padding: EdgeInsets.symmetric(horizontal: 10.w), 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( bloc.getCourseUnitDetail(pageIndex: index), index, context), ); }), )), SafeArea( child: SizedBox( width: 210.w, height: 40.h, // child: OverflowBox( // maxWidth: 300, // 允许内容超出容器宽度 child: ListView.builder( controller: bloc.indicatorSrollController, scrollDirection: Axis.horizontal, itemCount: bloc.unlockPageCount(), itemBuilder: (context, index) { bool isSelected = index == bloc.currentPage; // 计算透明度,使得当前页的指示器不透明,两侧逐渐透明 double opacity = 0.25 + (1 - ((bloc.currentPage - index).abs() / 2)) * 0.5; // 限制透明度在 0.5 到 1.0 之间 opacity = opacity.clamp(0.25, 1.0); return GestureDetector( onTap: () => bloc.pageController.animateToPage( index, duration: const Duration(milliseconds: 60), curve: Curves.ease), child: SizedBox( width: 30.0.w, height: 30.0.h, child: Padding( padding: EdgeInsets.all(isSelected ? 0.w : 5.w), child: Opacity( opacity: opacity, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: isSelected ? Colors.blue : Colors.grey, shape: BoxShape.circle, ), child: Text("${index + 1}", textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: isSelected ? 12.sp : 8.sp)), ), ), ), ), ); })), ), // ) ], ), ), ), ); }); } Widget _itemTransCard( CourseUnitDetail courseUnitDetail, int pageIndex, BuildContext context) { final bloc = BlocProvider.of(context); List? courseSectionEntities = bloc.courseSectionDatasMap[courseUnitDetail.id]; if (courseSectionEntities == null) { bloc.add(RequestDataEvent(courseUnitDetail.id!)); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( '暂无数据', style: TextStyle(fontSize: 24.0), ), const SizedBox(height: 16.0), // 间距 CircularProgressIndicator( color: CourseModuleModel( bloc.courseUnitEntity.courseModuleCode ?? 'Phase-1') .color), // 加载动画 ], ), ); } else { return NestedListView.builder( itemCount: bloc.courseSectionDatasMap[courseUnitDetail.id]?.length ?? 0, scrollDirection: Axis.horizontal, itemBuilder: (BuildContext context, int index) { CourseSectionEntity sectionData = courseSectionEntities[index]; if (sectionData.courseType == SectionType.bouns.value) { //彩蛋 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: SectionItem( courseModuleId: bloc.courseUnitEntity.courseModuleCode, lessons: sectionData, ), ); } }); } }