9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
1
2
|
import 'dart:async';
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
3
4
|
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
5
|
import 'package:flutter/services.dart';
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:wow_english/common/dialogs/show_dialog.dart';
import 'package:wow_english/common/request/dao/video_dao.dart';
import 'package:wow_english/common/widgets/we_app_bar.dart';
import 'package:wow_english/route/route.dart';
import 'video_zone_item.dart';
class VideoZonePage extends StatefulWidget {
const VideoZonePage({super.key});
@override
State<VideoZonePage> createState() => _VideoZonePageState();
}
class _VideoZonePageState extends State<VideoZonePage> {
late Future<List<VideoZoneItem>> _itemsFuture;
@override
void initState() {
super.initState();
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
27
28
29
|
unawaited(
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky),
);
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
30
31
32
|
_itemsFuture = VideoDao.list();
}
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
33
34
35
36
37
38
39
40
|
@override
void dispose() {
unawaited(
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky),
);
super.dispose();
}
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
41
|
void _reload() {
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
42
43
44
|
setState(() {
_itemsFuture = VideoDao.list();
});
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
45
46
47
48
|
}
@override
Widget build(BuildContext context) {
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
return MediaQuery.removePadding(
context: context,
removeTop: true,
child: Scaffold(
backgroundColor: Colors.white,
appBar: const WEAppBar(
titleText: '视频专区',
centerTitle: false,
),
body: FutureBuilder<List<VideoZoneItem>>(
future: _itemsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return _VideoLoadError(onRetry: _reload);
}
final items = snapshot.data ?? const <VideoZoneItem>[];
if (items.isEmpty) {
return const Center(
child: Text(
'暂无视频',
style: TextStyle(color: Color(0xFF999999), fontSize: 18),
),
);
}
return RefreshIndicator(
onRefresh: () async {
final future = VideoDao.list();
setState(() => _itemsFuture = future);
await future;
},
child: LayoutBuilder(
builder: (context, constraints) {
final horizontalPadding = 30.w;
final crossAxisSpacing = 12.w;
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
86
87
88
89
90
91
92
|
final titleStyle = _VideoCard.createTitleStyle(context);
final titlePainter = TextPainter(
text: TextSpan(text: '视频标题', style: titleStyle),
maxLines: 1,
textDirection: Directionality.of(context),
textScaler: MediaQuery.textScalerOf(context),
)..layout();
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
93
94
95
96
|
final cardWidth = (constraints.maxWidth -
horizontalPadding * 2 -
crossAxisSpacing * 2) /
3;
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
97
98
|
final cardHeight =
cardWidth * 9 / 16 + 5.h + titlePainter.height;
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
return GridView.builder(
physics: const AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.fromLTRB(
horizontalPadding,
10.h,
horizontalPadding,
16.h,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 14.h,
crossAxisSpacing: crossAxisSpacing,
mainAxisExtent: cardHeight,
),
itemCount: items.length,
itemBuilder: (context, index) => _VideoCard(
item: items[index],
onTap: () => _openVideo(items, index),
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
117
|
titleStyle: titleStyle,
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
118
119
120
|
),
);
},
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
121
122
|
),
);
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
123
124
|
},
),
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
),
);
}
void _openVideo(List<VideoZoneItem> items, int index) {
final item = items[index];
if (!item.accessible || item.videoUrl.isEmpty) {
showTwoActionDialog('提示', '取消', '去购买', '购买课程后即可观看视频。', leftTap: popPage,
rightTap: () {
popPage();
pushNamed(AppRouteName.shop);
});
return;
}
Navigator.of(context).pushNamed(
AppRouteName.videoZonePlayer,
arguments: {
'items': items
.where((video) => video.accessible && video.videoUrl.isNotEmpty)
.toList(),
'initialVideoId': item.id,
},
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
147
148
149
150
151
152
153
|
).then((_) {
if (!mounted) return;
unawaited(
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky),
);
_reload();
});
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
}
}
class _VideoLoadError extends StatelessWidget {
const _VideoLoadError({required this.onRetry});
final VoidCallback onRetry;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'视频加载失败,请稍后重试',
style: TextStyle(color: Color(0xFF999999), fontSize: 18),
),
const SizedBox(height: 12),
FilledButton(onPressed: onRetry, child: const Text('重新加载')),
],
),
);
}
}
class _VideoCard extends StatelessWidget {
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
181
182
183
184
185
|
const _VideoCard({
required this.item,
required this.onTap,
required this.titleStyle,
});
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
186
187
188
|
final VideoZoneItem item;
final VoidCallback onTap;
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
189
190
191
192
193
194
195
196
|
final TextStyle titleStyle;
static TextStyle createTitleStyle(BuildContext context) => TextStyle(
color: const Color(0xFF222222),
fontFamily: null,
fontSize: 17.sp,
fontWeight: FontWeight.w500,
);
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
197
198
199
200
201
202
203
204
205
|
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: BorderRadius.circular(8.r),
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
206
207
|
AspectRatio(
aspectRatio: 16 / 9,
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
208
209
|
child: ClipRRect(
borderRadius: BorderRadius.circular(8.r),
|
9475aa49
吴启风
feat: 优化视频专区列表布局与...
|
210
211
212
213
214
215
216
217
218
219
220
221
|
child: CachedNetworkImage(
imageUrl: item.coverUrl,
fit: BoxFit.cover,
placeholder: (_, __) => const ColoredBox(
color: Color(0xFFF2F2F2),
child: Center(child: CircularProgressIndicator()),
),
errorWidget: (_, __, ___) => const ColoredBox(
color: Color(0xFFF2F2F2),
child: Icon(Icons.broken_image_outlined,
color: Color(0xFFAAAAAA)),
),
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
222
223
224
225
|
),
),
),
SizedBox(height: 5.h),
|
54f62f82
吴启风
fix: 自适应视频标题高度避免文字裁切
|
226
227
228
229
230
|
Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: titleStyle,
|
383462ef
吴启风
feat: 新增视频专区列表与播放功能
|
231
232
233
234
235
236
|
),
],
),
);
}
}
|