Blame view

lib/pages/video/zone/video_zone_page.dart 7.18 KB
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
  
    @override
    Widget build(BuildContext context) {
      return InkWell(
c67962ab   吴启风   feat: 视频卡片点击区域和封面...
201
        borderRadius: BorderRadius.circular(12.r),
383462ef   吴启风   feat: 新增视频专区列表与播放功能
202
203
204
205
        onTap: onTap,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
9475aa49   吴启风   feat: 优化视频专区列表布局与...
206
207
            AspectRatio(
              aspectRatio: 16 / 9,
383462ef   吴启风   feat: 新增视频专区列表与播放功能
208
              child: ClipRRect(
c67962ab   吴启风   feat: 视频卡片点击区域和封面...
209
                borderRadius: BorderRadius.circular(12.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
            ),
          ],
        ),
      );
    }
  }