medialocation_view.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:get/get.dart';
  4. import 'package:nomo/config/theme/dark_theme_colors.dart';
  5. import '../../../../config/translations/strings_enum.dart';
  6. import '../../../constants/assets.dart';
  7. import '../../../constants/iconfont/iconfont.dart';
  8. import '../../../widgets/click_opacity.dart';
  9. import '../../../widgets/ix_image.dart';
  10. import '../../../widgets/submit_btn.dart';
  11. import '../controllers/medialocation_controller.dart';
  12. class MedialocationView extends GetView<MedialocationController> {
  13. const MedialocationView({super.key});
  14. @override
  15. Widget build(BuildContext context) {
  16. return Scaffold(
  17. backgroundColor: DarkThemeColors.scaffoldBackgroundColor,
  18. body: Stack(
  19. children: [
  20. // 视频背景层(只显示顶部214高度)
  21. Positioned(
  22. top: 0,
  23. left: 0,
  24. right: 0,
  25. height: 214.w,
  26. child: ClipRect(
  27. child: IXImage(
  28. source: Assets.mediaBg,
  29. width: 375.w,
  30. height: 214.w,
  31. sourceType: ImageSourceType.asset,
  32. ),
  33. ),
  34. ),
  35. // 渐变遮罩层
  36. Positioned(
  37. top: 0,
  38. left: 0,
  39. right: 0,
  40. height: 214.w,
  41. child: Container(
  42. decoration: BoxDecoration(
  43. gradient: LinearGradient(
  44. begin: Alignment.topCenter,
  45. end: Alignment.bottomCenter,
  46. colors: [Colors.black.withValues(alpha: 0.6), Colors.black],
  47. stops: const [0.0, 1.0],
  48. ),
  49. ),
  50. ),
  51. ),
  52. // 内容层
  53. SafeArea(
  54. child: Column(
  55. children: [
  56. // 顶部标题区域
  57. _buildAppBar(),
  58. _buildHeader(),
  59. // 可滚动内容区域
  60. Expanded(
  61. child: SingleChildScrollView(
  62. padding: EdgeInsets.symmetric(horizontal: 16.w),
  63. child: Column(
  64. children: [
  65. 20.verticalSpace,
  66. // 流媒体服务卡片
  67. _buildStreamingServicesCard(),
  68. 10.verticalSpace,
  69. // 功能说明列表
  70. _buildFeaturesList(),
  71. 32.verticalSpace,
  72. ],
  73. ),
  74. ),
  75. ),
  76. // 底部连接按钮
  77. _buildConnectButton(),
  78. ],
  79. ),
  80. ),
  81. ],
  82. ),
  83. );
  84. }
  85. // 顶部标题栏
  86. Widget _buildAppBar() {
  87. return Padding(
  88. padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h),
  89. child: Row(
  90. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  91. children: [
  92. SizedBox(width: 32.w),
  93. GestureDetector(
  94. onTap: () => Get.back(),
  95. child: Container(
  96. width: 24.w,
  97. height: 24.w,
  98. decoration: BoxDecoration(
  99. color: Colors.white.withValues(alpha: 0.1),
  100. shape: BoxShape.circle,
  101. ),
  102. child: Icon(Icons.close_rounded, color: Colors.white, size: 16.w),
  103. ),
  104. ),
  105. ],
  106. ),
  107. );
  108. }
  109. /// 构建顶部标题区域
  110. Widget _buildHeader() {
  111. return Container(
  112. padding: EdgeInsets.symmetric(vertical: 20.w),
  113. child: Column(
  114. children: [
  115. // Movies&TV 图标和标题
  116. Row(
  117. mainAxisAlignment: MainAxisAlignment.center,
  118. children: [
  119. Icon(
  120. IconFont.icon19,
  121. color: DarkThemeColors.errorColor,
  122. size: 32.w,
  123. ),
  124. 4.horizontalSpace,
  125. Text(
  126. 'Movies&TV',
  127. style: TextStyle(
  128. fontSize: 22.sp,
  129. fontWeight: FontWeight.w500,
  130. height: 1.3,
  131. color: Colors.white,
  132. ),
  133. ),
  134. ],
  135. ),
  136. 8.verticalSpace,
  137. // 连接状态
  138. Obx(() {
  139. final text = controller.isConnected.value
  140. ? Strings.connected.tr
  141. : controller.isConnecting.value
  142. ? Strings.connecting.tr
  143. : Strings.disconnected.tr;
  144. final textColor = controller.isConnected.value
  145. ? DarkThemeColors.text1
  146. : controller.isConnecting.value
  147. ? DarkThemeColors.text1
  148. : DarkThemeColors.text1;
  149. final statusImgPath = controller.isConnected.value
  150. ? Assets.connected
  151. : controller.isConnecting.value
  152. ? Assets.connecting
  153. : Assets.disconnected;
  154. return Row(
  155. mainAxisAlignment: MainAxisAlignment.center,
  156. children: [
  157. IXImage(
  158. source: statusImgPath,
  159. sourceType: ImageSourceType.asset,
  160. width: 14.w,
  161. height: 14.w,
  162. ),
  163. 4.horizontalSpace,
  164. Text(
  165. text,
  166. style: TextStyle(
  167. fontSize: 14.sp,
  168. fontWeight: FontWeight.w500,
  169. color: textColor,
  170. ),
  171. ),
  172. ],
  173. );
  174. }),
  175. ],
  176. ),
  177. );
  178. }
  179. /// 构建流媒体服务卡片
  180. Widget _buildStreamingServicesCard() {
  181. return Container(
  182. decoration: BoxDecoration(
  183. color: DarkThemeColors.bg2,
  184. borderRadius: BorderRadius.circular(12.r),
  185. ),
  186. child: ListView.separated(
  187. shrinkWrap: true,
  188. physics: const NeverScrollableScrollPhysics(),
  189. padding: EdgeInsets.zero,
  190. itemCount: controller.streamingServices.length,
  191. separatorBuilder: (context, index) => Divider(
  192. height: 1.w,
  193. thickness: 1.w,
  194. color: DarkThemeColors.strokes1,
  195. ),
  196. itemBuilder: (context, index) {
  197. final service = controller.streamingServices[index];
  198. return _buildStreamingServiceItem(service);
  199. },
  200. ),
  201. );
  202. }
  203. /// 构建单个流媒体服务项
  204. Widget _buildStreamingServiceItem(StreamingService service) {
  205. return ClickOpacity(
  206. onTap: () => controller.openStreamingService(service),
  207. child: Container(
  208. padding: EdgeInsets.all(16.w),
  209. child: Row(
  210. children: [
  211. // Logo
  212. Container(
  213. width: 40.w,
  214. height: 40.w,
  215. decoration: BoxDecoration(
  216. color: _getServiceColor(service.name),
  217. borderRadius: BorderRadius.circular(12.r),
  218. ),
  219. child: Center(
  220. child: Text(
  221. service.name[0].toUpperCase(),
  222. style: TextStyle(
  223. fontSize: 24.sp,
  224. fontWeight: FontWeight.bold,
  225. color: DarkThemeColors.text1,
  226. ),
  227. ),
  228. ),
  229. ),
  230. 10.horizontalSpace,
  231. // 名称和描述
  232. Expanded(
  233. child: Column(
  234. crossAxisAlignment: CrossAxisAlignment.start,
  235. children: [
  236. Text(
  237. service.name,
  238. style: TextStyle(
  239. fontSize: 14.sp,
  240. height: 1.4,
  241. fontWeight: FontWeight.w500,
  242. color: DarkThemeColors.text1,
  243. ),
  244. ),
  245. Text(
  246. service.description,
  247. style: TextStyle(
  248. fontSize: 13.sp,
  249. height: 1.4,
  250. color: DarkThemeColors.text2,
  251. ),
  252. ),
  253. ],
  254. ),
  255. ),
  256. 16.horizontalSpace,
  257. // Open 按钮
  258. Container(
  259. padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
  260. decoration: BoxDecoration(
  261. color: DarkThemeColors.primaryColor,
  262. borderRadius: BorderRadius.circular(20.r),
  263. ),
  264. child: Text(
  265. Strings.open.tr,
  266. style: TextStyle(fontSize: 13.sp, color: DarkThemeColors.text1),
  267. ),
  268. ),
  269. ],
  270. ),
  271. ),
  272. );
  273. }
  274. /// 构建功能说明列表
  275. Widget _buildFeaturesList() {
  276. return Column(
  277. children: [
  278. _buildFeatureItem(
  279. icon: Icons.favorite_outline,
  280. iconColor: Colors.red,
  281. text: 'Access your favorite content',
  282. ),
  283. _buildFeatureItem(
  284. icon: Icons.public,
  285. iconColor: Colors.blue,
  286. text: 'Connection from anywhere',
  287. ),
  288. _buildFeatureItem(
  289. icon: Icons.speed,
  290. iconColor: Colors.purple,
  291. text: 'Ultra fast servers',
  292. ),
  293. ],
  294. );
  295. }
  296. /// 构建单个功能说明项
  297. Widget _buildFeatureItem({
  298. required IconData icon,
  299. required Color iconColor,
  300. required String text,
  301. }) {
  302. return Container(
  303. decoration: BoxDecoration(
  304. color: DarkThemeColors.bg3,
  305. borderRadius: BorderRadius.circular(12.r),
  306. ),
  307. height: 40.w,
  308. margin: EdgeInsets.only(bottom: 10.w),
  309. padding: EdgeInsets.symmetric(horizontal: 14.w),
  310. child: Row(
  311. children: [
  312. Icon(icon, color: iconColor, size: 20.w),
  313. 10.horizontalSpace,
  314. Expanded(
  315. child: Text(
  316. text,
  317. style: TextStyle(fontSize: 13.sp, color: DarkThemeColors.text2),
  318. ),
  319. ),
  320. ],
  321. ),
  322. );
  323. }
  324. /// 构建底部连接按钮
  325. Widget _buildConnectButton() {
  326. return Padding(
  327. padding: EdgeInsets.all(16.w),
  328. child: Obx(() {
  329. return SubmitButton(
  330. text: controller.isConnected.value
  331. ? Strings.disconnect.tr
  332. : controller.isConnecting.value
  333. ? Strings.connecting.tr
  334. : Strings.connect.tr,
  335. onPressed: controller.isConnected.value
  336. ? controller.disconnect
  337. : controller.connect,
  338. isLoading: controller.isConnecting.value,
  339. bgColor: controller.isConnected.value
  340. ? Colors.red
  341. : DarkThemeColors.primaryColor,
  342. );
  343. }),
  344. );
  345. }
  346. /// 获取服务颜色
  347. Color _getServiceColor(String serviceName) {
  348. switch (serviceName.toLowerCase()) {
  349. case 'netflix':
  350. return const Color(0xFFE50914);
  351. case 'youtube':
  352. return const Color(0xFFFF0000);
  353. case 'hulu':
  354. return const Color(0xFF1CE783);
  355. case 'amazon':
  356. return const Color(0xFF00A8E1);
  357. default:
  358. return DarkThemeColors.primaryColor;
  359. }
  360. }
  361. }