Commit 3ba925a9d0b3386434fe89c90552b3482a056dd5

Authored by 吴启风
1 parent 27dad3d1

feat:环节页增加翻页切换单元效果

lib/pages/section/bloc/section_bloc.dart
... ... @@ -10,12 +10,26 @@ import 'package:wow_english/utils/toast_util.dart';
10 10  
11 11 import '../../../models/course_section_entity.dart';
12 12 import '../../../models/course_unit_entity.dart';
  13 +import '../../../utils/list_ext.dart';
13 14  
14 15 part 'section_event.dart';
15 16 part 'section_state.dart';
16 17  
17 18 class SectionBloc extends Bloc<SectionEvent, SectionState> {
18 19  
  20 + PageController _pageController;
  21 +
  22 + PageController get pageController => _pageController;
  23 +
  24 + ///当前页索引
  25 + int _currentPage = 0;
  26 +
  27 + int get currentPage => _currentPage;
  28 +
  29 + ScrollController _listController;
  30 +
  31 + ScrollController get listController => _listController;
  32 +
19 33 CourseUnitEntity _courseUnitEntity;
20 34  
21 35 CourseUnitEntity get courseUnitEntity => _courseUnitEntity;
... ... @@ -32,11 +46,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
32 46  
33 47 CourseProcessEntity? get processEntity => _processEntity;
34 48  
35   - SectionBloc(this._courseUnitEntity, this._courseUnitDetail) : super(LessonInitial()) {
  49 + SectionBloc(this._courseUnitEntity, this._courseUnitDetail, this._pageController, this._listController) : super(LessonInitial()) {
36 50 on<RequestDataEvent>(_requestData);
37 51 on<RequestExitClassEvent>(_requestExitClass);
38 52 on<RequestEnterClassEvent>(_requestEnterClass);
39 53 on<RequestVideoLessonEvent>(_requestVideoLesson);
  54 + on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
40 55 }
41 56  
42 57 void _requestData(RequestDataEvent event, Emitter<SectionState> emitter) async {
... ... @@ -82,4 +97,15 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
82 97 void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async {
83 98 await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime);
84 99 }
  100 +
  101 + void _pageControllerChange(CurrentUnitIndexChangeEvent event,
  102 + Emitter<SectionState> emitter) async {
  103 + _currentPage = event.unitIndex;
  104 + emitter(CurrentPageIndexState());
  105 + }
  106 +
  107 + // 未锁定的页数
  108 + int unlockPageCount() {
  109 + return _courseUnitEntity.courseUnitVOList?.indexWhereOrNull((element) => element.lock == true) ?? 1;
  110 + }
85 111 }
... ...
lib/pages/section/bloc/section_event.dart
... ... @@ -26,3 +26,9 @@ class RequestExitClassEvent extends SectionEvent {
26 26 final String currentTime;
27 27 RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime);
28 28 }
  29 +
  30 +///页面切换
  31 +class CurrentUnitIndexChangeEvent extends SectionEvent {
  32 + final int unitIndex;
  33 + CurrentUnitIndexChangeEvent(this.unitIndex);
  34 +}
... ...
lib/pages/section/bloc/section_state.dart
... ... @@ -10,11 +10,15 @@ class LessonDataLoadState extends SectionState {}
10 10 class RequestVideoLessonState extends SectionState {
11 11 final String courseLessonId;
12 12 final int type;
13   - RequestVideoLessonState(this.courseLessonId,this.type);
  13 +
  14 + RequestVideoLessonState(this.courseLessonId, this.type);
14 15 }
15 16  
16   -class RequestEnterClassState extends SectionState{
  17 +class RequestEnterClassState extends SectionState {
17 18 final String courseLessonId;
18 19 final int courseType;
19   - RequestEnterClassState(this.courseLessonId,this.courseType);
  20 +
  21 + RequestEnterClassState(this.courseLessonId, this.courseType);
20 22 }
  23 +
  24 +class CurrentPageIndexState extends SectionState {}
... ...
lib/pages/section/section_page.dart
  1 +import 'package:flutter/cupertino.dart';
1 2 import 'package:flutter/material.dart';
2 3 import 'package:flutter_bloc/flutter_bloc.dart';
3 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
  5 +import 'package:nested_scroll_views/material.dart';
4 6 import 'package:wow_english/common/core/user_util.dart';
5 7 import 'package:wow_english/common/extension/string_extension.dart';
6 8 import 'package:wow_english/models/course_unit_entity.dart';
... ... @@ -16,7 +18,10 @@ import &#39;courese_module_model.dart&#39;;
16 18  
17 19 /// 环节列表页
18 20 class SectionPage extends StatelessWidget {
19   - const SectionPage({super.key, required this.courseUnitEntity, required this.courseUnitDetail});
  21 + const SectionPage(
  22 + {super.key,
  23 + required this.courseUnitEntity,
  24 + required this.courseUnitDetail});
20 25  
21 26 final CourseUnitEntity courseUnitEntity;
22 27  
... ... @@ -26,7 +31,9 @@ class SectionPage extends StatelessWidget {
26 31 @override
27 32 Widget build(BuildContext context) {
28 33 return BlocProvider(
29   - create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail)..add(RequestDataEvent()),
  34 + create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail,
  35 + PageController(), ScrollController())
  36 + ..add(RequestDataEvent()),
30 37 child: _SectionPageView(context),
31 38 );
32 39 }
... ... @@ -115,11 +122,11 @@ class _SectionPageView extends StatelessWidget {
115 122 }
116 123 }
117 124 },
118   - child: _homeView(),
  125 + child: _sectionView(),
119 126 );
120 127 }
121 128  
122   - Widget _homeView() =>
  129 + Widget _sectionView() =>
123 130 BlocBuilder<SectionBloc, SectionState>(builder: (context, state) {
124 131 final bloc = BlocProvider.of<SectionBloc>(context);
125 132 return Scaffold(
... ... @@ -133,96 +140,65 @@ class _SectionPageView extends StatelessWidget {
133 140 title: bloc.courseUnitDetail.name,
134 141 courseModuleCode: bloc.courseUnitEntity.courseModuleCode),
135 142 Expanded(
136   - child: ListView.builder(
137   - itemCount: bloc.courseSectionDatas?.length ?? 0,
138   - scrollDirection: Axis.horizontal,
139   - itemBuilder: (BuildContext context, int index) {
140   - CourseSectionEntity sectionData =
141   - bloc.courseSectionDatas![index];
142   - if (sectionData.courseType == 5) {
143   - //彩蛋
144   - return GestureDetector(
145   - onTap: () {
146   - if (!UserUtil.isLogined()) {
147   - pushNamed(AppRouteName.login);
148   - return;
149   - }
150   - if (sectionData.lock == true) {
151   - showToast('当前课程暂未解锁');
152   - return;
153   - }
154   -
155   - ///进入课堂
156   - bloc.add(RequestEnterClassEvent(
157   - sectionData.id.toString(), sectionData.courseType));
158   - },
159   - child: SectionBoundsItem(
160   - imageUrl: sectionData.coverUrl,
161   - ),
162   - );
163   - } else {
164   - return GestureDetector(
165   - onTap: () {
166   - if (!UserUtil.isLogined()) {
167   - pushNamed(AppRouteName.login);
168   - return;
169   - }
170   - if (sectionData.lock == true) {
171   - showToast('当前课程暂未解锁');
172   - return;
173   - }
174   -
175   - ///进入课堂
176   - bloc.add(RequestEnterClassEvent(
177   - sectionData.id.toString(), sectionData.courseType));
178   - },
179   - child: SectionVideoItem(
180   - unitEntity: bloc.courseUnitEntity,
181   - lessons: sectionData,
182   - ),
183   - );
184   - }
185   - })),
186   - // SafeArea(
187   - // child: Padding(
188   - // padding: EdgeInsets.symmetric(horizontal: 13.w),
189   - // child: Row(
190   - // mainAxisAlignment: MainAxisAlignment.spaceBetween,
191   - // children: [
192   - // SizedBox(
193   - // height: 47.h,
194   - // width: 80.w,
195   - // ),
196   - // Container(
197   - // decoration: BoxDecoration(
198   - // color: CourseModuleModel(
199   - // bloc.courseUnitEntity.courseModuleCode ??
200   - // 'Phase-1')
201   - // .color,
202   - // borderRadius: BorderRadius.circular(14.5.r),
203   - // ),
204   - // padding: EdgeInsets.symmetric(
205   - // vertical: 8.h, horizontal: 24.w),
206   - // child: Text(
207   - // '${(bloc.courseUnitEntity.nowStep ?? 0)}/${bloc.courseUnitEntity.total ?? 0}',
208   - // style: TextStyle(
209   - // color: Colors.white, fontSize: 12.sp),
210   - // ),
211   - // ),
212   - // Image.asset(
213   - // CourseModuleModel(
214   - // bloc.courseUnitEntity.courseModuleCode ??
215   - // 'Phase-1')
216   - // .courseModuleLogo
217   - // .assetPng,
218   - // height: 47.h,
219   - // width: 80.w,
220   - // // color: Colors.red,
221   - // ),
222   - // ],
223   - // ),
224   - // ),
225   - // )
  143 + child: Padding(
  144 + padding: EdgeInsets.symmetric(horizontal: 10.w),
  145 + child: OverflowBox(
  146 + child: NestedPageView.builder(
  147 + itemCount: bloc.unlockPageCount(),
  148 + controller: bloc.pageController,
  149 + onPageChanged: (int index) {
  150 + bloc.add(CurrentUnitIndexChangeEvent(index));
  151 + },
  152 + itemBuilder: (context, index) {
  153 + return ScrollConfiguration(
  154 + ///去掉 Android 上默认的边缘拖拽效果
  155 + behavior: ScrollConfiguration.of(context)
  156 + .copyWith(overscroll: false),
  157 + child: _itemTransCard(index, context),
  158 + );
  159 + }),
  160 + ), // 设置外部padding,
  161 + )),
  162 + SafeArea(
  163 + child: Padding(
  164 + padding: EdgeInsets.symmetric(horizontal: 13.w),
  165 + child: Row(
  166 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  167 + children: [
  168 + SizedBox(
  169 + height: 47.h,
  170 + width: 80.w,
  171 + ),
  172 + Container(
  173 + decoration: BoxDecoration(
  174 + color: CourseModuleModel(
  175 + bloc.courseUnitEntity.courseModuleCode ??
  176 + 'Phase-1')
  177 + .color,
  178 + borderRadius: BorderRadius.circular(14.5.r),
  179 + ),
  180 + padding: EdgeInsets.symmetric(
  181 + vertical: 8.h, horizontal: 24.w),
  182 + child: Text(
  183 + '${bloc.currentPage + 1}/${bloc.unlockPageCount()}',
  184 + style: TextStyle(
  185 + color: Colors.white, fontSize: 12.sp),
  186 + ),
  187 + ),
  188 + Image.asset(
  189 + CourseModuleModel(
  190 + bloc.courseUnitEntity.courseModuleCode ??
  191 + 'Phase-1')
  192 + .courseModuleLogo
  193 + .assetPng,
  194 + height: 47.h,
  195 + width: 80.w,
  196 + // color: Colors.red,
  197 + ),
  198 + ],
  199 + ),
  200 + ),
  201 + )
226 202 ],
227 203 ),
228 204 ),
... ... @@ -230,3 +206,56 @@ class _SectionPageView extends StatelessWidget {
230 206 );
231 207 });
232 208 }
  209 +
  210 +Widget _itemTransCard(int index, BuildContext context) {
  211 + final bloc = BlocProvider.of<SectionBloc>(context);
  212 + return NestedListView.builder(
  213 + itemCount: bloc.courseSectionDatas?.length ?? 0,
  214 + scrollDirection: Axis.horizontal,
  215 + itemBuilder: (BuildContext context, int index) {
  216 + CourseSectionEntity sectionData = bloc.courseSectionDatas![index];
  217 + if (sectionData.courseType == 5) {
  218 + //彩蛋
  219 + return GestureDetector(
  220 + onTap: () {
  221 + if (!UserUtil.isLogined()) {
  222 + pushNamed(AppRouteName.login);
  223 + return;
  224 + }
  225 + if (sectionData.lock == true) {
  226 + showToast('当前课程暂未解锁');
  227 + return;
  228 + }
  229 +
  230 + ///进入课堂
  231 + bloc.add(RequestEnterClassEvent(
  232 + sectionData.id.toString(), sectionData.courseType));
  233 + },
  234 + child: SectionBoundsItem(
  235 + imageUrl: sectionData.coverUrl,
  236 + ),
  237 + );
  238 + } else {
  239 + return GestureDetector(
  240 + onTap: () {
  241 + if (!UserUtil.isLogined()) {
  242 + pushNamed(AppRouteName.login);
  243 + return;
  244 + }
  245 + if (sectionData.lock == true) {
  246 + showToast('当前课程暂未解锁');
  247 + return;
  248 + }
  249 +
  250 + ///进入课堂
  251 + bloc.add(RequestEnterClassEvent(
  252 + sectionData.id.toString(), sectionData.courseType));
  253 + },
  254 + child: SectionVideoItem(
  255 + unitEntity: bloc.courseUnitEntity,
  256 + lessons: sectionData,
  257 + ),
  258 + );
  259 + }
  260 + });
  261 +}
... ...
lib/utils/list_ext.dart 0 → 100644
  1 +
  2 +
  3 +extension ListExtension<E> on List<E> {
  4 + /// 获取数组中第一个匹配元素的index,没有就返回null
  5 + int? indexWhereOrNull(bool Function(E element) test) {
  6 + for (int i = 0; i < length; i++) {
  7 + if (test(this[i])) return i;
  8 + }
  9 + return null;
  10 + }
  11 +}
... ...
pubspec.yaml
... ... @@ -111,6 +111,8 @@ dependencies:
111 111 umeng_common_sdk: ^1.2.7
112 112 # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk
113 113 umeng_apm_sdk: ^2.2.1
  114 + # 嵌套滚动 https://pub.dev/packages/nested_scroll_views
  115 + nested_scroll_views: ^0.0.10
114 116  
115 117 dev_dependencies:
116 118 build_runner: ^2.4.4
... ...