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/module_cache.dart'; import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/utils/click_with_music_controller.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/toast_util.dart'; import '../../models/course_section_entity.dart'; import '../../utils/log_util.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); final clickController = ClickWithMusicController.instance; return BlocListener( listener: (context, state) async { if (state is RequestEnterClassState) { final courseType = state.courseType; final courseLessonId = state.courseLessonId; if (courseType == SectionType.song.value || courseType == SectionType.video.value) { final title = courseType == SectionType.song.value ? 'song' : 'video'; final AudioPlayerUtilType audioType; ///儿歌/视频类型 if (courseType == SectionType.song.value) { audioType = AudioPlayerUtilType.musicTime; } else { audioType = AudioPlayerUtilType.videoTime; } clickController.playMusicAndPerformAction(context, audioType, () async { ///播放音乐->调进入课程接口->跳转课程页面 await bloc.requestEnterClass(courseLessonId, () { Log.d("WQF request finish"); pushNamed(AppRouteName.lookVideo, arguments: { 'videoUrl': null, 'title': title, 'courseLessonId': 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); }); }, onRequestEnterFailed: (error) { Log.d("WQF requestEnterClass failed $error"); }); }); return; } if (courseType == SectionType.pictureBook.value) { //绘本 clickController.playMusicAndPerformAction( context, AudioPlayerUtilType.readingTime, () async { await bloc.requestEnterClass(courseLessonId, () { pushNamed(AppRouteName.reading, arguments: {'courseLessonId': 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 (courseType == SectionType.practice.value) { //练习 clickController.playMusicAndPerformAction( context, AudioPlayerUtilType.quizTime, () async { await bloc.requestEnterClass(courseLessonId, () { pushNamed(AppRouteName.topicPic, arguments: { 'courseLessonId': courseLessonId, 'moduleColor': ModuleCache.instance.getCurrentThemeColor() }).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, 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: ModuleCache.instance.getCurrentThemeColor()), // 加载动画 ], ), ); } 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( lessons: sectionData, ), ); } }); } }