medialocation_view.dart 11 KB

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