feat: 请求数据和配置

This commit is contained in:
chengsiwen committed 2026-07-06 15:21:29 +08:00
1 parent 557f900187
commit 9b0fe51fa4
21 files changed
+1026 -60

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

-12
View File
@@ -39,17 +39,6 @@ class AppService {
// 添加自定义拦截器 // 添加自定义拦截器
_dio.interceptors.add(NyNetworkIntercept()); _dio.interceptors.add(NyNetworkIntercept());
// 开发环境添加日志
// if (!kReleaseMode) {
// _dio.interceptors.add(TalkerDioLogger(
// settings: TalkerDioLoggerSettings(
// printRequestHeaders: true,
// printRequestData: true,
// printResponseData: true,
// printResponseHeaders: false,
// ),
// ));
// }
} }
/// 更新配置 /// 更新配置
@@ -279,7 +268,6 @@ class ApiRequest {
throw Exception("retry failed (unreachable)"); throw Exception("retry failed (unreachable)");
} }
// ============ 错误处理 ============
static HttpResult<T> _handleError<T>(DioException e) { static HttpResult<T> _handleError<T>(DioException e) {
final Response? resp = e.response; final Response? resp = e.response;
+1 -1
View File
@@ -2,7 +2,7 @@
// https://api-glimzodf.glimzodf.com/glimzodf/ // https://api-glimzodf.glimzodf.com/glimzodf/
class ApiString { class ApiString {
static const String baseUrl = "https://api-glimzodf.glimzodf.com/"; static const String baseUrl = "https://api-glimzodf.glimzodf.com/glimzodf";
static const String appName = 'Novyronst'; static const String appName = 'Novyronst';
+11 -29
View File
@@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart'; import 'package:novyronst/common/help/help_log.dart';
import 'package:novyronst/common/help/help_time.dart'; import 'package:novyronst/common/help/help_time.dart';
import 'package:novyronst/common/help/help_token.dart'; import 'package:novyronst/common/help/help_token.dart';
@@ -11,19 +11,18 @@ import '../help/help_encrypt.dart';
/// 网络请求拦截器 /// 网络请求拦截器
class NyNetworkIntercept extends Interceptor { class NyNetworkIntercept extends Interceptor {
final bool enableLog = !kReleaseMode;
final Logger _logger = Logger();
@override @override
Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async { Future<void> onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
// 添加公共请求头
await _addRequestHeaders(options); await _addRequestHeaders(options);
// 打印请求日志 // 打印请求日志
_logInfo("🔶 REQUEST [${options.method.toUpperCase()}] ${options.uri}"); HelpLog.print("🔶 REQUEST [${options.method.toUpperCase()}] ${options.uri}");
_logDebug("Headers: ${options.headers}"); HelpLog.print("Headers: ${options.headers}");
if (options.data != null) { if (options.data != null) {
_logDebug("Payload: ${options.data}"); HelpLog.print("Payload: ${options.data}");
} }
handler.next(options); handler.next(options);
@@ -31,7 +30,7 @@ class NyNetworkIntercept extends Interceptor {
@override @override
void onResponse(Response response, ResponseInterceptorHandler handler) { void onResponse(Response response, ResponseInterceptorHandler handler) {
_logInfo("🟢 RESPONSE [${response.statusCode}] ${response.requestOptions.uri}"); HelpLog.print("🟢 RESPONSE [${response.statusCode}] ${response.requestOptions.uri}");
// _logDebug("Body: ${response.data}"); // _logDebug("Body: ${response.data}");
// 处理 401 / 402 Token 过期 // 处理 401 / 402 Token 过期
@@ -56,7 +55,7 @@ class NyNetworkIntercept extends Interceptor {
final deStr = HelpEncrypt.deStr(response.data); final deStr = HelpEncrypt.deStr(response.data);
response.data = jsonDecode(deStr); response.data = jsonDecode(deStr);
} catch (e) { } catch (e) {
_logError("解密失败: $e"); HelpLog.print("解密失败: $e");
} }
} }
@@ -65,14 +64,13 @@ class NyNetworkIntercept extends Interceptor {
@override @override
void onError(DioException err, ErrorInterceptorHandler handler) { void onError(DioException err, ErrorInterceptorHandler handler) {
_logError("🔴 Error: ${err.message}"); HelpLog.print("🔴 Error: ${err.message}", tag: 'in onError');
handler.next(err); handler.next(err);
} }
// ============ 私有方法 ============ // ============ 私有方法 ============
Future<void> _addRequestHeaders(RequestOptions options) async { Future<void> _addRequestHeaders(RequestOptions options) async {
// 开发模式添加 security: false
if (!kReleaseMode) { if (!kReleaseMode) {
options.headers['security'] = 'false'; options.headers['security'] = 'false';
} }
@@ -80,7 +78,6 @@ class NyNetworkIntercept extends Interceptor {
// 注入 Token // 注入 Token
final tokenManager = HelpToken(); final tokenManager = HelpToken();
final token = tokenManager.get(); final token = tokenManager.get();
// print('查看token: $token');
if (token.isNotEmpty) { if (token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token'; options.headers['Authorization'] = 'Bearer $token';
} }
@@ -104,7 +101,6 @@ class NyNetworkIntercept extends Interceptor {
'device-gaid': '', 'device-gaid': '',
}); });
// 处理 prevToken(从请求体中提取)
await _handleOldToken(options); await _handleOldToken(options);
} }
@@ -117,25 +113,11 @@ class NyNetworkIntercept extends Interceptor {
options.headers['Authorization'] = prevToken.toString(); options.headers['Authorization'] = prevToken.toString();
} }
} }
} catch (_) { } catch (_) {}
// 非 JSON 请求跳过
}
} }
void _logInfo(String message) {
if (enableLog) _logger.i(message);
}
void _logDebug(String message) {
if (enableLog) _logger.d('💡 $message');
}
void _logError(String message) {
if (enableLog) _logger.e(message);
}
void _handleError(dynamic e, bool error) { void _handleError(dynamic e, bool error) {
_logError("🔴 Error: $e"); HelpLog.print("🔴 Error: $e", tag: 'in ---- _handleError');
// 这里可以根据需要处理错误提示
} }
} }
+62
View File
@@ -2,6 +2,7 @@
import 'package:novyronst/common/api/api_request.dart'; import 'package:novyronst/common/api/api_request.dart';
import 'package:novyronst/common/api/api_string.dart'; import 'package:novyronst/common/api/api_string.dart';
import 'package:novyronst/common/help/help_log.dart'; import 'package:novyronst/common/help/help_log.dart';
import 'package:novyronst/common/help/help_token.dart';
import 'package:novyronst/common/model/novyronst_user.dart'; import 'package:novyronst/common/model/novyronst_user.dart';
import 'package:novyronst/common/model/register_model.dart'; import 'package:novyronst/common/model/register_model.dart';
@@ -38,4 +39,65 @@ class UserRequest {
return null; return null;
}); });
} }
static Future eventCreateHis({
required String shortPlayId,
required String videoId
}) {
// {"short_play_id": shortPlayId, "video_id": videoId}
Map<String, dynamic> params = {"short_play_id": shortPlayId, "video_id": videoId};
return ApiRequest.post(ApiStringUser.eventCreateHistory, data: params).then((value) {
HelpLog.print(value.data, tag: "eventCreateHis");
});
}
static Future eventEnterApp() {
return ApiRequest.post(ApiStringUser.eventUserEnterApp).then((value) {});
}
static Future<bool> eventOnline({String? oldToken}) {
String token = HelpToken().get();
if (token == '') return Future<bool>.value(false);
final body = <String, dynamic>{};
if (oldToken != null) body['token'] = oldToken;
return ApiRequest.post(ApiStringUser.eventUserOnline, data: body).then((value) {
return value.isSuccess;
});
}
static Future<bool> eventOLeaveApp({String? oldToken}) {
String token = HelpToken().get();
if (token == '') return Future<bool>.value(false);
final body = <String, dynamic>{};
if (oldToken != null) body['token'] = oldToken;
return ApiRequest.post(ApiStringUser.eventUserLeaveApp, data: body).then((value) {
return value.isSuccess;
});
}
// 上报播放时长
static Future eventPlaySeconds({
required int shortPlayId,
required int videoId,
required int seconds,
}) {
return ApiRequest.post(ApiStringUser.eventPlaySeconds, data: {
"short_play_id": shortPlayId,
"video_id": videoId,
"play_seconds": seconds,
}).then((value) {});
}
// 上报观看时长
static Future eventWatchTime({
required int shortPlayId,
required int seconds,
}) {
return ApiRequest.post(ApiStringUser.eventWatchTime, data: {
"short_play_id": shortPlayId,
// 时间戳的毫秒数
"request_id": DateTime.now().millisecondsSinceEpoch,
"duration": seconds,
}).then((value) {});
}
} }
+181
View File
@@ -0,0 +1,181 @@
import 'package:novyronst/common/api/api_request.dart';
import 'package:novyronst/common/api/api_string.dart';
import 'package:novyronst/common/help/help_log.dart';
import 'package:novyronst/common/model/ny_category_model.dart';
import 'package:novyronst/common/model/ny_drama_model.dart';
import 'package:novyronst/common/model/ny_home_model.dart';
import '../model/ny_detail_model.dart';
class VideoRequest {
static Future<List<NyHomeModel>> getHomeData() {
return ApiRequest.get(ApiStringVideo.getHomeModules).then((value) {
if (value.isSuccess) {
List? list = value.data['data']['list'];
List<NyHomeModel> homeList = [];
if (list != null) {
homeList = list.map((e) => NyHomeModel.fromJson(e)).toList();
}
return homeList;
}
return [];
});
}
static Future<List<NyCategoryModel>> getCategoriesList() {
return ApiRequest.get(ApiStringVideo.getCategoriesList).then((value) {
// SkyLog.print('vvvvv: ${value.data}');
if (!value.isSuccess) return [];
Map<String, dynamic> data = value.data['data'];
List? list = data['list'];
List<NyCategoryModel> categoryList = [];
if (list != null) {
// id 28 不显示,删除
categoryList = list.map((e) => NyCategoryModel.fromJson(e)).toList();
categoryList.removeWhere((element) => element.id == 28);
categoryList.removeWhere((element) => element.name.length >= 10);
}
return categoryList;
});
}
static Future<List<NyDramaModel>> getCategoriesDetail({
required int categoryId,
int page = 1,
int pageSize = 20,
}) {
return ApiRequest.fetchPage<NyDramaModel>(
ApiStringVideo.getCategoriesDetail,
page: page,
pageSize: pageSize,
extraParams: {'category_id': categoryId},
fromJson: (json) => NyDramaModel.fromJson(json),
);
}
// getSearchHots
static Future<List<NyDramaModel>> getSearchHots() {
return ApiRequest.get(ApiStringVideo.getSearchHots).then((value) {
if (value.isSuccess) {
HelpLog.print('getSearchHots: ${value.data}');
List list = parseDataForList(value.data);
return list.map((e) => NyDramaModel.fromJson(e)).toList();
}
return [];
});
}
// search
static Future<List<NyDramaModel>> search({required String keyword}) {
return ApiRequest.get(ApiStringVideo.getSearch, queryParameters: {
'search': keyword,
}).then((value) {
if (value.isSuccess) {
HelpLog.print('getSearchHots: ${value.data}');
List list = parseDataForList(value.data);
return list.map((e) => NyDramaModel.fromJson(e)).toList();
}
return [];
});
}
//getCollections
static Future<List<NyDramaModel>> getCollections({
int page = 1,
int pageSize = 10,
}) {
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
return ApiRequest.get(
ApiStringVideo.getCollections,
queryParameters: params,
).then((value) {
if (value.isSuccess) {
HelpLog.print('getCollections: ${value.data}');
List list = parseDataForList(value.data);
return list.map((e) => NyDramaModel.fromJson(e)).toList();
}
return [];
});
}
// getHistory
static Future<List<NyDramaModel>> getHistory({
int page = 1,
int pageSize = 10,
}) {
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
return ApiRequest.get(
ApiStringVideo.getHistories,
queryParameters: params,
).then((value) {
if (value.isSuccess) {
HelpLog.print('getHistory: ${value.data}');
List list = parseDataForList(value.data);
return list.map((e) => NyDramaModel.fromJson(e)).toList();
}
return [];
});
}
// getVideoDetail
static Future<NyDetailModel?> getVideoDetail({
required int shortPlayId,
required int shortPlayVideoId,
}) {
Map<String, dynamic> params = {};
params['short_play_id'] = shortPlayId;
params['short_play_video_id'] = shortPlayVideoId;
return ApiRequest.get(
ApiStringVideo.getVideoDetail,
queryParameters: params,
).then((value) {
if (value.isSuccess) {
HelpLog.print('getVideoDetail: ${value.data['data']}');
return NyDetailModel.fromJson(value.data['data']);
}
return null;
});
}
// collect
static Future<bool> collect({
required int shortPlayId,
required int videoId,
}) {
Map<String, dynamic> params = {};
params['short_play_id'] = shortPlayId;
params['video_id'] = videoId;
return ApiRequest.post(ApiStringVideo.collect, data: params).then((value) {
HelpLog.print('collecttttt: ${value.data}');
if (value.isSuccess) {}
return value.isSuccess;
});
}
// cancelCollect
static Future<bool> cancelCollect({required int shortPlayId}) {
Map<String, dynamic> params = {};
params['short_play_id'] = shortPlayId;
return ApiRequest.post(ApiStringVideo.cancelCollect, data: params).then((
value,
) {
HelpLog.print('cancelCollectttt: ${value.data}');
if (value.isSuccess) {}
return value.isSuccess;
});
}
static Map<String, dynamic> _setPage({int page = 1, int pageSize = 10}) {
return {'current_page': page, 'page_size': pageSize};
}
static List parseDataForList(Map<String, dynamic> data) {
Map<String, dynamic> map = data['data'];
List? listData = map['list'];
return listData ?? [];
}
}
+1
View File
@@ -15,6 +15,7 @@ class ConstImage {
static const String btCollectSel = "assets/main/bt_collect_seled.png"; static const String btCollectSel = "assets/main/bt_collect_seled.png";
static const String btMineSel = "assets/main/bt_mine_seled.png"; static const String btMineSel = "assets/main/bt_mine_seled.png";
static const String spaceImg = "assets/main/space_img.png"; static const String spaceImg = "assets/main/space_img.png";
static const String launcher = "assets/main/launcher.png";
/// imgs /// imgs
static const String searchBg = "assets/imgs/search_bg.png"; static const String searchBg = "assets/imgs/search_bg.png";
+35
View File
@@ -0,0 +1,35 @@
import 'package:get/get.dart';
import 'package:novyronst/pages/play/pages/laucher_page.dart';
class HelpRouters {
static const String launcher = '/launcher';
}
class HelpPages {
static const initial = HelpRouters.launcher;
static final List<GetPage> routes = [
routePage(name: HelpRouters.launcher, page: ()=> LauncherPage())
];
}
GetPage routePage({
required String name,
required GetPageBuilder page,
Bindings? binding,
Transition transition = Transition.rightToLeft,
Duration transitionDuration = const Duration(milliseconds: 300),
CustomTransition? customTransition,
bool popGesture = true, // Allow iOS swipe-back gesture by default
}) {
return GetPage(
name: name,
page: page,
binding: binding,
transition: transition,
transitionDuration: transitionDuration,
customTransition: customTransition,
popGesture: popGesture, // Pass through to GetPage
);
}
-9
View File
@@ -65,7 +65,6 @@ class HelpToken {
} }
} }
// ============ 便捷属性 ============
/// 判断 Token 是否为空 /// 判断 Token 是否为空
bool get isEmpty => get().isEmpty; bool get isEmpty => get().isEmpty;
@@ -82,7 +81,6 @@ class HelpToken {
return null; return null;
} }
// ============ 用户信息扩展(原项目没有,但建议加上) ============
/// 保存用户信息 /// 保存用户信息
void saveUserInfo(Map<String, dynamic> userInfo) { void saveUserInfo(Map<String, dynamic> userInfo) {
@@ -107,12 +105,9 @@ class HelpToken {
return null; return null;
} }
// ============ 私有存储方法 ============
String? _getFromStorage(String key) { String? _getFromStorage(String key) {
try { try {
// 使用 SharedPreferences 同步获取(但 SharedPreferences 是异步的)
// 为了保持与原项目一致的同步接口,这里使用异步转同步
// 但更好的方式是在初始化时预加载 // 但更好的方式是在初始化时预加载
return _getFromStorageSync(key); return _getFromStorageSync(key);
} catch (e) { } catch (e) {
@@ -121,13 +116,9 @@ class HelpToken {
} }
} }
// 同步读取(注意:SharedPreferences 本身是异步的,这里用同步方式需要谨慎)
String? _getFromStorageSync(String key) { String? _getFromStorageSync(String key) {
// 由于 SharedPreferences 是异步的,我们使用一个全局缓存 // 由于 SharedPreferences 是异步的,我们使用一个全局缓存
// 实际项目中建议在 main 中调用 init() 预加载
try { try {
// 这里为了简化,直接返回 null,实际应该用异步方式
// 建议使用下面的异步版本
return null; return null;
} catch (_) { } catch (_) {
return null; return null;
+24
View File
@@ -0,0 +1,24 @@
import 'package:novyronst/common/model/parse_model.dart';
class NyCategoryModel {
final int id;
final String name;
const NyCategoryModel({required this.id, required this.name});
factory NyCategoryModel.fromJson(Map<String, dynamic> json) {
return NyCategoryModel(
id: parseInt(json['id']),
name: parseString(json['name']),
);
}
Map<String, dynamic> toJson() {
return {'id': id, 'name': name};
}
NyCategoryModel copyWith({int? id, String? name}) {
return NyCategoryModel(id: id ?? this.id, name: name ?? this.name);
}
}
+113
View File
@@ -0,0 +1,113 @@
import 'package:novyronst/common/model/ny_drama_model.dart';
import 'package:novyronst/common/model/ny_episode_model.dart';
import 'package:novyronst/common/model/parse_model.dart';
class NyDetailModel {
final NyDramaModel? videoInfo;
final NyDramaModel? shortPlayInfo;
final List<NyEpisodeModel> episodeList;
final String? businessModel;
bool? isCollect;
final bool? showShareCoin;
final int? shareCoin;
final int? revolution;
final String? userLevel;
final int? jumpType;
final int? jumpShortPlayId;
final List<int>? checkPoint;
NyDetailModel({
required this.videoInfo,
this.shortPlayInfo,
required this.episodeList,
this.businessModel,
this.isCollect,
this.showShareCoin,
this.shareCoin,
this.revolution,
this.userLevel,
this.jumpType,
this.jumpShortPlayId,
this.checkPoint,
});
factory NyDetailModel.fromJson(Map<String, dynamic> json) {
return NyDetailModel(
videoInfo: parseMap(json['video_info']) == null
? null
: NyDramaModel.fromJson(parseMap(json['video_info'])!),
shortPlayInfo: parseMap(json['shortPlayInfo']) == null
? null
: NyDramaModel.fromJson(parseMap(json['shortPlayInfo'])!),
episodeList: parseModelList(json['episodeList'], NyEpisodeModel.fromJson),
businessModel: json['business_model'] as String?,
isCollect: json['is_collect'] == null
? null
: parseBool(json['is_collect']),
showShareCoin: json['show_share_coin'] == null
? null
: parseBool(json['show_share_coin']),
shareCoin: json['share_coin'] == null
? null
: parseInt(json['share_coin']),
revolution: json['revolution'] == null
? null
: parseInt(json['revolution']),
userLevel: json['user_level'] as String?,
jumpType: json['jump_type'] == null ? null : parseInt(json['jump_type']),
jumpShortPlayId: json['jump_short_play_id'] == null
? null
: parseInt(json['jump_short_play_id']),
checkPoint: parseIntList(json['check_point']),
);
}
Map<String, dynamic> toJson() {
return {
'video_info': videoInfo?.toJson(),
'shortPlayInfo': shortPlayInfo?.toJson(),
'episodeList': episodeList.map((item) => item.toJson()).toList(),
'business_model': businessModel,
'is_collect': isCollect,
'show_share_coin': showShareCoin,
'share_coin': shareCoin,
'revolution': revolution,
'user_level': userLevel,
'jump_type': jumpType,
'jump_short_play_id': jumpShortPlayId,
'check_point': checkPoint,
};
}
NyDetailModel copyWith({
NyDramaModel? videoInfo,
NyDramaModel? shortPlayInfo,
List<NyEpisodeModel>? episodeList,
String? businessModel,
bool? isCollect,
bool? showShareCoin,
int? shareCoin,
int? revolution,
String? userLevel,
int? jumpType,
int? jumpShortPlayId,
List<int>? checkPoint,
}) {
return NyDetailModel(
videoInfo: videoInfo ?? this.videoInfo,
shortPlayInfo: shortPlayInfo ?? this.shortPlayInfo,
episodeList: episodeList ?? this.episodeList,
businessModel: businessModel ?? this.businessModel,
isCollect: isCollect ?? this.isCollect,
showShareCoin: showShareCoin ?? this.showShareCoin,
shareCoin: shareCoin ?? this.shareCoin,
revolution: revolution ?? this.revolution,
userLevel: userLevel ?? this.userLevel,
jumpType: jumpType ?? this.jumpType,
jumpShortPlayId: jumpShortPlayId ?? this.jumpShortPlayId,
checkPoint: checkPoint ?? this.checkPoint,
);
}
}
+185
View File
@@ -0,0 +1,185 @@
import 'package:novyronst/common/model/parse_model.dart';
import 'ny_category_model.dart';
class NyDramaModel {
final int? id;
final int shortId;
int? shortPlayId;
final String name;
final String description;
final String imageUrl;
final String horizontallyImg;
int collectTotal;
final int watchTotal;
final int episodeTotal;
final String? playSeconds;
bool? isCollect;
final String? videoUrl;
final Map<String, dynamic>? videoInfo;
final List<String>? category;
final List<NyCategoryModel>? categoryList;
final String tagType;
int? episode;
final int? process;
final int? buyType;
final int? searchClickTotal;
final int? shortPlayVideoId;
final int? currentEpisode;
int? historyTime;
NyDramaModel({
this.id,
required this.shortId,
this.shortPlayId,
required this.name,
required this.description,
required this.imageUrl,
required this.horizontallyImg,
required this.collectTotal,
required this.watchTotal,
required this.episodeTotal,
this.playSeconds,
this.isCollect,
this.videoUrl,
this.videoInfo,
this.category,
this.categoryList,
required this.tagType,
this.episode,
this.process,
this.buyType,
this.searchClickTotal,
this.shortPlayVideoId,
this.currentEpisode,
this.historyTime,
});
factory NyDramaModel.fromJson(Map<String, dynamic> json) {
return NyDramaModel(
id: json['id'] == null ? null : parseInt(json['id']),
shortId: parseInt(json['short_id']),
shortPlayId: json['short_play_id'] == null
? null
: parseInt(json['short_play_id']),
name: parseString(json['name']),
description: parseString(json['description']),
imageUrl: parseString(json['image_url']),
horizontallyImg: parseString(json['horizontally_img']),
collectTotal: parseInt(json['collect_total']),
watchTotal: parseInt(json['watch_total']),
episodeTotal: parseInt(json['episode_total']),
playSeconds: json['play_seconds'] as String?,
isCollect: json['is_collect'] == null
? null
: parseBool(json['is_collect']),
videoUrl: json['video_url'] as String?,
videoInfo: parseMap(json['video_info']),
category: parseStringList(json['category']),
categoryList: json['categoryList'] == null
? null
: parseModelList(json['categoryList'], NyCategoryModel.fromJson),
tagType: parseString(json['tag_type']),
episode: json['episode'] == null ? null : parseInt(json['episode']),
process: json['process'] == null ? null : parseInt(json['process']),
buyType: json['buy_type'] == null ? null : parseInt(json['buy_type']),
searchClickTotal: json['search_click_total'] == null
? null
: parseInt(json['search_click_total']),
shortPlayVideoId: json['short_play_video_id'] == null
? null
: parseInt(json['short_play_video_id']),
currentEpisode: json['current_episode'] == null
? null
: parseInt(json['current_episode']),
historyTime: json['history_time'] == null
? null
: parseInt(json['history_time']),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'short_id': shortId,
'short_play_id': shortPlayId,
'name': name,
'description': description,
'image_url': imageUrl,
'horizontally_img': horizontallyImg,
'collect_total': collectTotal,
'watch_total': watchTotal,
'episode_total': episodeTotal,
'play_seconds': playSeconds,
'is_collect': isCollect,
'video_url': videoUrl,
'video_info': videoInfo,
'category': category,
'categoryList': categoryList?.map((item) => item.toJson()).toList(),
'tag_type': tagType,
'episode': episode,
'process': process,
'buy_type': buyType,
'search_click_total': searchClickTotal,
'short_play_video_id': shortPlayVideoId,
'current_episode': currentEpisode,
'history_time': historyTime,
};
}
NyDramaModel copyWith({
int? id,
int? shortId,
int? shortPlayId,
String? name,
String? description,
String? imageUrl,
String? horizontallyImg,
int? collectTotal,
int? watchTotal,
int? episodeTotal,
String? playSeconds,
bool? isCollect,
String? videoUrl,
Map<String, dynamic>? videoInfo,
List<String>? category,
List<NyCategoryModel>? categoryList,
String? tagType,
int? episode,
int? process,
int? buyType,
int? searchClickTotal,
int? shortPlayVideoId,
int? currentEpisode,
int? historyTime,
}) {
return NyDramaModel(
id: id ?? this.id,
shortId: shortId ?? this.shortId,
shortPlayId: shortPlayId ?? this.shortPlayId,
name: name ?? this.name,
description: description ?? this.description,
imageUrl: imageUrl ?? this.imageUrl,
horizontallyImg: horizontallyImg ?? this.horizontallyImg,
collectTotal: collectTotal ?? this.collectTotal,
watchTotal: watchTotal ?? this.watchTotal,
episodeTotal: episodeTotal ?? this.episodeTotal,
playSeconds: playSeconds ?? this.playSeconds,
isCollect: isCollect ?? this.isCollect,
videoUrl: videoUrl ?? this.videoUrl,
videoInfo: videoInfo ?? this.videoInfo,
category: category ?? this.category,
categoryList: categoryList ?? this.categoryList,
tagType: tagType ?? this.tagType,
episode: episode ?? this.episode,
process: process ?? this.process,
buyType: buyType ?? this.buyType,
searchClickTotal: searchClickTotal ?? this.searchClickTotal,
shortPlayVideoId: shortPlayVideoId ?? this.shortPlayVideoId,
currentEpisode: currentEpisode ?? this.currentEpisode,
historyTime: historyTime ?? this.historyTime,
);
}
}
+93
View File
@@ -0,0 +1,93 @@
import 'package:novyronst/common/model/parse_model.dart';
class NyEpisodeModel {
final int id;
final int shortId;
final int shortPlayId;
final int shortPlayVideoId;
final int episode;
final String videoUrl;
final int? promiseViewAd;
final int iaaPopUp;
bool isLock;
String playSeconds;
int? viewAdCount;
NyEpisodeModel({
required this.id,
required this.shortId,
required this.shortPlayId,
required this.shortPlayVideoId,
required this.episode,
required this.videoUrl,
required this.playSeconds,
required this.isLock,
this.promiseViewAd,
required this.iaaPopUp,
required this.viewAdCount,
});
factory NyEpisodeModel.fromJson(Map<String, dynamic> json) {
return NyEpisodeModel(
id: parseInt(json['id']),
shortId: parseInt(json['short_id']),
shortPlayId: parseInt(json['short_play_id']),
shortPlayVideoId: parseInt(json['short_play_video_id']),
episode: parseInt(json['episode']),
videoUrl: parseString(json['video_url']),
playSeconds: parseString(json['play_seconds']),
isLock: parseBool(json['is_lock']),
promiseViewAd: json['promise_view_ad'] == null
? null
: parseInt(json['promise_view_ad']),
iaaPopUp: parseInt(json['iaa_pop_up']),
viewAdCount: parseInt(json['view_ad_count']),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'short_id': shortId,
'short_play_id': shortPlayId,
'short_play_video_id': shortPlayVideoId,
'episode': episode,
'video_url': videoUrl,
'promise_view_ad': promiseViewAd,
'iaa_pop_up': iaaPopUp,
'is_lock': isLock,
'play_seconds': playSeconds,
'view_ad_count': viewAdCount,
};
}
NyEpisodeModel copyWith({
int? id,
int? shortId,
int? shortPlayId,
int? shortPlayVideoId,
int? episode,
String? videoUrl,
bool? isLock,
String? playSeconds,
String? durationSeconds,
int? promiseViewAd,
int? viewAdCount,
int? iaaPopUp,
}) {
return NyEpisodeModel(
id: id ?? this.id,
shortId: shortId ?? this.shortId,
shortPlayId: shortPlayId ?? this.shortPlayId,
shortPlayVideoId: shortPlayVideoId ?? this.shortPlayVideoId,
episode: episode ?? this.episode,
videoUrl: videoUrl ?? this.videoUrl,
isLock: isLock ?? this.isLock,
playSeconds: playSeconds ?? this.playSeconds,
promiseViewAd: promiseViewAd ?? this.promiseViewAd,
viewAdCount: viewAdCount ?? this.viewAdCount,
iaaPopUp: iaaPopUp ?? this.iaaPopUp,
);
}
}
+39
View File
@@ -0,0 +1,39 @@
import 'ny_drama_model.dart';
class NyHomeModel {
final String? moduleKey;
final List<NyDramaModel> dataList;
const NyHomeModel({required this.moduleKey, required this.dataList});
factory NyHomeModel.fromJson(Map<String, dynamic> json) {
final data = json['data'];
List<NyDramaModel> videos = [];
if (data is Map<String, dynamic>) {
final list = data['list'];
if (list is List) {
videos = list.map((e) => NyDramaModel.fromJson(e)).toList();
}
} else if (data is List) {
videos = data.map((e) => NyDramaModel.fromJson(e)).toList();
}
return NyHomeModel(
moduleKey: json['module_key'] as String?,
dataList: videos,
);
}
Map<String, dynamic> toJson() {
return {'module_key': moduleKey, 'dataList': dataList};
}
NyHomeModel copyWith({String? moduleKey, dynamic data}) {
return NyHomeModel(
moduleKey: moduleKey ?? this.moduleKey,
dataList: data ?? dataList,
);
}
}
+13 -7
View File
@@ -3,18 +3,26 @@ import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:novyronst/common/help/help_token.dart';
import 'package:novyronst/root/lib_export.dart'; import 'package:novyronst/root/lib_export.dart';
import 'package:novyronst/root/root_page.dart'; import 'package:novyronst/root/root_page.dart';
import 'package:novyronst/tools/util/ny_device_info.dart';
void main() { import 'common/help/help_pages.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await initManage();
runApp(const RootApp()); runApp(const RootApp());
} }
Future<void> initManage() async {
await NyDeviceInfo.init();
await HelpToken().init();
}
class RootApp extends StatefulWidget { class RootApp extends StatefulWidget {
@@ -50,12 +58,11 @@ class _RootAppState extends State<RootApp> with WidgetsBindingObserver {
designSize: Size(375, 812), designSize: Size(375, 812),
child: GetMaterialApp( child: GetMaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
// translations: AppLanguage(),
locale: widget.locale ?? Get.deviceLocale, locale: widget.locale ?? Get.deviceLocale,
fallbackLocale: const Locale('en'), fallbackLocale: const Locale('en'),
initialBinding: AppBindings(), initialBinding: AppBindings(),
// initialRoute: SkyPages.initial, initialRoute: HelpRouters.launcher,
// getPages: SkyPages.routes, getPages: HelpPages.routes,
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: ConstColor.themeColor, seedColor: ConstColor.themeColor,
@@ -75,7 +82,6 @@ class _RootAppState extends State<RootApp> with WidgetsBindingObserver {
); );
}, },
), ),
home: widget.home ?? RootPage(),
onInit: () {}, onInit: () {},
), ),
) )
+48 -1
View File
@@ -1,7 +1,13 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:novyronst/common/api/video_request.dart';
import 'package:novyronst/common/help/app_nav.dart'; import 'package:novyronst/common/help/app_nav.dart';
import 'package:novyronst/common/model/ny_category_model.dart';
import 'package:novyronst/common/model/ny_drama_model.dart';
import 'package:novyronst/common/model/ny_home_model.dart';
import 'package:novyronst/pages/first/pages/search_page.dart'; import 'package:novyronst/pages/first/pages/search_page.dart';
import 'package:novyronst/pages/first/widgets/home_first_view.dart'; import 'package:novyronst/pages/first/widgets/home_first_view.dart';
import 'package:novyronst/pages/first/widgets/home_rank_widget.dart';
import 'package:novyronst/pages/first/widgets/home_remaining_sections.dart'; import 'package:novyronst/pages/first/widgets/home_remaining_sections.dart';
import 'package:novyronst/pages/first/widgets/search_view.dart'; import 'package:novyronst/pages/first/widgets/search_view.dart';
import 'package:novyronst/root/lib_export.dart'; import 'package:novyronst/root/lib_export.dart';
@@ -18,6 +24,41 @@ class FirstPage extends StatefulWidget {
} }
class _RootPageState extends State<FirstPage> { class _RootPageState extends State<FirstPage> {
List<NyCategoryModel> _categories = [];
List<NyHomeModel> _homeDatas = [];
final EasyRefreshController _refreshController = EasyRefreshController(
controlFinishRefresh: true,
);
@override
void initState() {
super.initState();
_getCategoryData();
_getAllData();
}
void _getAllData() {
VideoRequest.getHomeData().then((value) {
setState(() {
_homeDatas = value;
});
for (NyHomeModel element in value) {
print('ele len===: ${element.moduleKey} : ${element.dataList.length}');
}
});
}
void _getCategoryData() {
VideoRequest.getCategoriesList().then((value) {
setState(() {
_categories = value;
});
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppBgPage( return AppBgPage(
@@ -34,11 +75,17 @@ class _RootPageState extends State<FirstPage> {
slivers: [ slivers: [
SliverToBoxAdapter(child: HomeFirstView()), SliverToBoxAdapter(child: HomeFirstView()),
SliverPadding( SliverPadding(
padding: const EdgeInsets.only(left: 16, right: 10, top: 16), padding: const EdgeInsets.only(left: 16, right: 10, top: 16, bottom: 8),
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: HelpImage.asset(ConstImage.homeRank), child: HelpImage.asset(ConstImage.homeRank),
) )
), ),
SliverPadding(
padding: const EdgeInsetsGeometry.symmetric(horizontal: 16),
sliver: SliverToBoxAdapter(
child: HomeRankWidget(),
),
),
const SliverToBoxAdapter(child: HomeRemainingSections()), const SliverToBoxAdapter(child: HomeRemainingSections()),
], ],
), ),
+1 -1
View File
@@ -84,7 +84,7 @@ class _HomeFirstViewState extends State<HomeFirstView> {
); );
} }
_buildVideoItem() { Widget _buildVideoItem() {
return Padding( return Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Column( child: Column(
@@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:novyronst/common/const/const_string.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
import '../../../tools/util/app_screen.dart';
import '../../../tools/widgets/novy_button_bg.dart';
import '../../widgets/cut_corner_clipper.dart';
import '../../widgets/ny_tag_widget.dart';
class HomeRankWidget extends StatelessWidget {
const HomeRankWidget({super.key});
@override
Widget build(BuildContext context) {
return CutCornerClipper(
width: AppScreen.screenWidth - 32,
cutCorner: CutCorner.bottomLeft,
cutSize: 40,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
_buildRankList(),
const SizedBox(height: 5),
Row(
children: [
SizedBox(width: 20.w),
NovyButtonBg(text: 'Keep Watching', width: 308.w,)
],
)
],
),
),
);
}
Row _buildRankList() {
return Row(
children: [
_firstVideoView(),
const SizedBox(width: 8),
_otherVideoView(),
],
);
}
Widget _firstVideoView() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 160.w, height: 240.w,
decoration: BoxDecoration(
color: Colors.red,
)
),
const SizedBox(height: 10),
NovyText('Star of the ocean', fontSize: 15,
color: Colors.black,
fontFamily: ConstFont.montMedium,
),
const SizedBox(height: 5),
NyTagWidget(text: 'Safiray'),
const SizedBox(height: 8),
],
);
}
Widget _otherVideoView() {
return Column(
children: List.generate(3, (index) {
return Container(
margin: const EdgeInsets.only(bottom: 8),
width: 160.w, height: 93.w,
decoration: BoxDecoration(
color: Colors.grey,
)
);
})
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/api/user_request.dart';
import 'package:novyronst/common/help/app_nav.dart';
import 'package:novyronst/common/help/help_image.dart';
import 'package:novyronst/common/help/help_token.dart';
import 'package:novyronst/root/root_page.dart';
class LauncherPage extends StatefulWidget {
const LauncherPage({super.key});
@override
State<StatefulWidget> createState() {
return _LauncherPageState();
}
}
class _LauncherPageState extends State<LauncherPage> {
@override
void initState() {
super.initState();
_userRegister();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: HelpImage.asset(ConstImage.launcher, fit: BoxFit.cover),
);
}
void _userRegister() async {
// 先拿token没有的话去注册
final tm = HelpToken();
String token = tm.get();
if (token.isNotEmpty) {
// 成功后去到主页面
Future.delayed(const Duration(seconds: 1), () {
AppNav.off(RootPage());
});
return;
}
final res = await UserRequest.register();
if (res != null) {
tm.save(res.token);
AppNav.off(RootPage());
}
}
}
+80
View File
@@ -89,6 +89,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.7" version: "3.0.7"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -129,6 +137,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
easy_refresh:
dependency: "direct main"
description:
name: easy_refresh
sha256: "513a5dc82c044f8a9c9a3d600b3d5d3256070575bfd8761940533d8e209ba79b"
url: "https://pub.dev"
source: hosted
version: "3.5.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@@ -280,6 +296,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http: http:
dependency: transitive dependency: transitive
description: description:
@@ -440,6 +464,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path_drawing:
dependency: transitive
description:
name: path_drawing
sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
path_provider: path_provider:
dependency: transitive dependency: transitive
description: description:
@@ -725,6 +765,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
video_player:
dependency: "direct main"
description:
name: video_player
sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f"
url: "https://pub.dev"
source: hosted
version: "2.11.1"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0"
url: "https://pub.dev"
source: hosted
version: "2.9.5"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: "76097729ef0c976937945afa53f1ca3afa9b50c9a95909ba347bcf93270466fd"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
sha256: e4ae5bc934b528e5b95c5e47be2812860186260cd3eed3ac62f5ed380fdd1613
url: "https://pub.dev"
source: hosted
version: "6.8.0"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
+3
View File
@@ -31,6 +31,9 @@ dependencies:
android_id: ^0.5.1 android_id: ^0.5.1
logger: ^2.5.0 logger: ^2.5.0
video_player: ^2.10.0
easy_refresh: ^3.4.0
dev_dependencies: dev_dependencies: