Commit 3ba925a9d0b3386434fe89c90552b3482a056dd5
1 parent
27dad3d1
feat:环节页增加翻页切换单元效果
Showing
6 changed files
with
176 additions
and
98 deletions
lib/pages/section/bloc/section_bloc.dart
@@ -10,12 +10,26 @@ import 'package:wow_english/utils/toast_util.dart'; | @@ -10,12 +10,26 @@ import 'package:wow_english/utils/toast_util.dart'; | ||
10 | 10 | ||
11 | import '../../../models/course_section_entity.dart'; | 11 | import '../../../models/course_section_entity.dart'; |
12 | import '../../../models/course_unit_entity.dart'; | 12 | import '../../../models/course_unit_entity.dart'; |
13 | +import '../../../utils/list_ext.dart'; | ||
13 | 14 | ||
14 | part 'section_event.dart'; | 15 | part 'section_event.dart'; |
15 | part 'section_state.dart'; | 16 | part 'section_state.dart'; |
16 | 17 | ||
17 | class SectionBloc extends Bloc<SectionEvent, SectionState> { | 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 | CourseUnitEntity _courseUnitEntity; | 33 | CourseUnitEntity _courseUnitEntity; |
20 | 34 | ||
21 | CourseUnitEntity get courseUnitEntity => _courseUnitEntity; | 35 | CourseUnitEntity get courseUnitEntity => _courseUnitEntity; |
@@ -32,11 +46,12 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | @@ -32,11 +46,12 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | ||
32 | 46 | ||
33 | CourseProcessEntity? get processEntity => _processEntity; | 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 | on<RequestDataEvent>(_requestData); | 50 | on<RequestDataEvent>(_requestData); |
37 | on<RequestExitClassEvent>(_requestExitClass); | 51 | on<RequestExitClassEvent>(_requestExitClass); |
38 | on<RequestEnterClassEvent>(_requestEnterClass); | 52 | on<RequestEnterClassEvent>(_requestEnterClass); |
39 | on<RequestVideoLessonEvent>(_requestVideoLesson); | 53 | on<RequestVideoLessonEvent>(_requestVideoLesson); |
54 | + on<CurrentUnitIndexChangeEvent>(_pageControllerChange); | ||
40 | } | 55 | } |
41 | 56 | ||
42 | void _requestData(RequestDataEvent event, Emitter<SectionState> emitter) async { | 57 | void _requestData(RequestDataEvent event, Emitter<SectionState> emitter) async { |
@@ -82,4 +97,15 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | @@ -82,4 +97,15 @@ class SectionBloc extends Bloc<SectionEvent, SectionState> { | ||
82 | void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async { | 97 | void _requestExitClass(RequestExitClassEvent event,Emitter<SectionState> emitter) async { |
83 | await ListenDao.exitClass(event.courseLessonId,event.currentStep,event.currentTime); | 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,3 +26,9 @@ class RequestExitClassEvent extends SectionEvent { | ||
26 | final String currentTime; | 26 | final String currentTime; |
27 | RequestExitClassEvent(this.courseLessonId,this.currentStep,this.currentTime); | 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,11 +10,15 @@ class LessonDataLoadState extends SectionState {} | ||
10 | class RequestVideoLessonState extends SectionState { | 10 | class RequestVideoLessonState extends SectionState { |
11 | final String courseLessonId; | 11 | final String courseLessonId; |
12 | final int type; | 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 | final String courseLessonId; | 18 | final String courseLessonId; |
18 | final int courseType; | 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 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
2 | import 'package:flutter_bloc/flutter_bloc.dart'; | 3 | import 'package:flutter_bloc/flutter_bloc.dart'; |
3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; | 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; |
5 | +import 'package:nested_scroll_views/material.dart'; | ||
4 | import 'package:wow_english/common/core/user_util.dart'; | 6 | import 'package:wow_english/common/core/user_util.dart'; |
5 | import 'package:wow_english/common/extension/string_extension.dart'; | 7 | import 'package:wow_english/common/extension/string_extension.dart'; |
6 | import 'package:wow_english/models/course_unit_entity.dart'; | 8 | import 'package:wow_english/models/course_unit_entity.dart'; |
@@ -16,7 +18,10 @@ import 'courese_module_model.dart'; | @@ -16,7 +18,10 @@ import 'courese_module_model.dart'; | ||
16 | 18 | ||
17 | /// 环节列表页 | 19 | /// 环节列表页 |
18 | class SectionPage extends StatelessWidget { | 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 | final CourseUnitEntity courseUnitEntity; | 26 | final CourseUnitEntity courseUnitEntity; |
22 | 27 | ||
@@ -26,7 +31,9 @@ class SectionPage extends StatelessWidget { | @@ -26,7 +31,9 @@ class SectionPage extends StatelessWidget { | ||
26 | @override | 31 | @override |
27 | Widget build(BuildContext context) { | 32 | Widget build(BuildContext context) { |
28 | return BlocProvider( | 33 | return BlocProvider( |
29 | - create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail)..add(RequestDataEvent()), | 34 | + create: (context) => SectionBloc(courseUnitEntity, courseUnitDetail, |
35 | + PageController(), ScrollController()) | ||
36 | + ..add(RequestDataEvent()), | ||
30 | child: _SectionPageView(context), | 37 | child: _SectionPageView(context), |
31 | ); | 38 | ); |
32 | } | 39 | } |
@@ -115,11 +122,11 @@ class _SectionPageView extends StatelessWidget { | @@ -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 | BlocBuilder<SectionBloc, SectionState>(builder: (context, state) { | 130 | BlocBuilder<SectionBloc, SectionState>(builder: (context, state) { |
124 | final bloc = BlocProvider.of<SectionBloc>(context); | 131 | final bloc = BlocProvider.of<SectionBloc>(context); |
125 | return Scaffold( | 132 | return Scaffold( |
@@ -133,96 +140,65 @@ class _SectionPageView extends StatelessWidget { | @@ -133,96 +140,65 @@ class _SectionPageView extends StatelessWidget { | ||
133 | title: bloc.courseUnitDetail.name, | 140 | title: bloc.courseUnitDetail.name, |
134 | courseModuleCode: bloc.courseUnitEntity.courseModuleCode), | 141 | courseModuleCode: bloc.courseUnitEntity.courseModuleCode), |
135 | Expanded( | 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,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
pubspec.yaml
@@ -111,6 +111,8 @@ dependencies: | @@ -111,6 +111,8 @@ dependencies: | ||
111 | umeng_common_sdk: ^1.2.7 | 111 | umeng_common_sdk: ^1.2.7 |
112 | # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk | 112 | # 友盟APM https://pub-web.flutter-io.cn/packages/umeng_apm_sdk |
113 | umeng_apm_sdk: ^2.2.1 | 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 | dev_dependencies: | 117 | dev_dependencies: |
116 | build_runner: ^2.4.4 | 118 | build_runner: ^2.4.4 |