view.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import 'package:electronic_assistant/base/base_page.dart';
  2. import 'package:electronic_assistant/module/talk/controller.dart';
  3. import 'package:electronic_assistant/resource/colors.gen.dart';
  4. import 'package:electronic_assistant/utils/expand.dart';
  5. import 'package:electronic_assistant/utils/fixed_size_tab_indicator.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter/services.dart';
  8. import 'package:flutter_screenutil/flutter_screenutil.dart';
  9. import 'package:get/get.dart';
  10. import '../../data/bean/talks.dart';
  11. import '../../resource/assets.gen.dart';
  12. import '../../resource/string.gen.dart';
  13. import '../../router/app_pages.dart';
  14. import '../../utils/common_style.dart';
  15. class TalkPage extends BasePage<TalkController> {
  16. const TalkPage({super.key});
  17. static void start(TalkBean item) {
  18. Get.toNamed(RoutePath.talkDetail, arguments: item);
  19. }
  20. @override
  21. Widget buildBody(BuildContext context) {
  22. return DefaultTabController(
  23. length: controller.tabBeans.length,
  24. child: Stack(
  25. children: [
  26. buildTopGradient(),
  27. Scaffold(
  28. backgroundColor: Colors.transparent,
  29. appBar: AppBar(
  30. systemOverlayStyle: SystemUiOverlayStyle.dark,
  31. backgroundColor: Colors.transparent,
  32. leading: IconButton(
  33. icon: SizedBox(
  34. width: 24.w,
  35. height: 24.w,
  36. child: Assets.images.iconBack.image()),
  37. // Custom icon
  38. onPressed: () {
  39. Get.back();
  40. },
  41. ),
  42. ),
  43. body: Column(
  44. crossAxisAlignment: CrossAxisAlignment.start,
  45. children: [
  46. Padding(
  47. padding: EdgeInsets.symmetric(horizontal: 12.w),
  48. child: Obx(() {
  49. return Column(
  50. crossAxisAlignment: CrossAxisAlignment.start,
  51. children: [
  52. SizedBox(height: 8.h),
  53. Text(controller.talkBean.value?.title ?? '',
  54. style: TextStyle(
  55. fontSize: 22.sp,
  56. fontWeight: FontWeight.bold,
  57. color: ColorName.primaryTextColor)),
  58. SizedBox(height: 4.h),
  59. Text(controller.talkBean.value?.createTime ?? '',
  60. style: TextStyle(
  61. fontSize: 12.sp,
  62. color: ColorName.secondaryTextColor)),
  63. SizedBox(height: 14.h),
  64. ],
  65. );
  66. }),
  67. ),
  68. buildTabBar(),
  69. Divider(
  70. height: 1,
  71. color: const Color(0xFFf6f6f6),
  72. indent: 12.w,
  73. endIndent: 12.w),
  74. SizedBox(height: 8.h),
  75. buildTalkContentView()
  76. ],
  77. ),
  78. ),
  79. buildBottomView(),
  80. buildAIAnalysisView()
  81. ],
  82. ),
  83. );
  84. }
  85. Container buildTabBar() {
  86. return Container(
  87. decoration: const BoxDecoration(
  88. color: Colors.white,
  89. borderRadius: BorderRadius.only(
  90. topLeft: Radius.circular(12),
  91. topRight: Radius.circular(12),
  92. )),
  93. child: TabBar(
  94. labelStyle: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
  95. unselectedLabelStyle: TextStyle(fontSize: 14.sp),
  96. labelColor: ColorName.primaryTextColor,
  97. unselectedLabelColor: ColorName.secondaryTextColor,
  98. labelPadding: EdgeInsets.only(top: 4.h),
  99. dividerHeight: 0,
  100. indicator: FixedSizeTabIndicator(
  101. width: 16.w,
  102. height: 3.w,
  103. radius: 3,
  104. color: ColorName.colorPrimary),
  105. tabs: controller.tabBeans.map((txt) => Tab(text: txt)).toList()),
  106. );
  107. }
  108. @override
  109. bool immersive() {
  110. return true;
  111. }
  112. Container buildTopGradient() {
  113. return Container(
  114. width: 1.sw,
  115. height: 180.h,
  116. decoration: BoxDecoration(
  117. gradient: LinearGradient(
  118. colors: ['#E1E9FF'.toColor(), '#F9FAFE'.toColor()],
  119. begin: Alignment.topCenter,
  120. end: Alignment.bottomCenter,
  121. stops: const [0.3, 1.0],
  122. ),
  123. ));
  124. }
  125. Widget buildTalkContentView() {
  126. return Obx(() {
  127. if (controller.talkBean.value?.status == TalkStatus.notAnalysis) {
  128. if (controller.isShowElectricLow.value) {
  129. return buildElectricLowView();
  130. } else {
  131. return buildNotAnalysisView();
  132. }
  133. } else {
  134. return buildTabContentView();
  135. }
  136. });
  137. }
  138. Widget buildTabContentView() {
  139. return Expanded(
  140. child: TabBarView(
  141. children: controller.pages,
  142. ),
  143. );
  144. }
  145. Widget buildNotAnalysisView() {
  146. return SizedBox(
  147. width: double.infinity,
  148. child: Column(
  149. children: [
  150. SizedBox(height: 119.h),
  151. SizedBox(
  152. width: 100.w,
  153. height: 100.w,
  154. child: Assets.images.iconTalkSummaryUnanalyzed.image()),
  155. SizedBox(height: 4.h),
  156. Text(StringName.talkUnAnalyzed.tr,
  157. style: TextStyle(
  158. fontSize: 15.sp, color: ColorName.primaryTextColor)),
  159. SizedBox(height: 2.h),
  160. Text(StringName.talkUnAnalyzedTips.tr,
  161. style: TextStyle(
  162. fontSize: 12.sp, color: ColorName.secondaryTextColor)),
  163. SizedBox(height: 24.h),
  164. GestureDetector(
  165. onTap: () {
  166. controller.checkCanAnalyze();
  167. },
  168. child: Container(
  169. decoration: getPrimaryBtnDecoration(8),
  170. width: 240.w,
  171. height: 48.w,
  172. child: Center(
  173. child: Text(
  174. StringName.talkAnalyzedBtnTxt.tr,
  175. style: TextStyle(fontSize: 16.sp, color: ColorName.white),
  176. ),
  177. ),
  178. ),
  179. )
  180. ],
  181. ),
  182. );
  183. }
  184. Widget buildElectricLowView() {
  185. return Container(
  186. color: const Color(0xFFDFE4FC),
  187. padding: EdgeInsets.only(left: 16.w, right: 12.w, top: 8.h, bottom: 8.h),
  188. child: Row(
  189. children: [
  190. SizedBox(
  191. width: 46.w,
  192. height: 56.w,
  193. child: Assets.images.iconTalkElectricLow.image()),
  194. SizedBox(width: 10.w),
  195. IntrinsicHeight(
  196. child: Column(
  197. crossAxisAlignment: CrossAxisAlignment.start,
  198. children: [
  199. SizedBox(
  200. width: 90.w,
  201. height: 21.w,
  202. child: Assets.images.iconTalkElectricLowTxt.image()),
  203. SizedBox(width: 1.w),
  204. Text(StringName.talkElectricLow.tr,
  205. style: TextStyle(
  206. fontSize: 12.sp, color: ColorName.secondaryTextColor)),
  207. ],
  208. ),
  209. ),
  210. const Spacer(),
  211. GestureDetector(
  212. onTap: () {
  213. controller.goElectricStore();
  214. },
  215. child: Container(
  216. decoration: getPrimaryBtnDecoration(8),
  217. width: 100.w,
  218. height: 36.w,
  219. child: Center(
  220. child: Text(StringName.talkGoStore.tr,
  221. style:
  222. TextStyle(fontSize: 16.sp, color: ColorName.white)),
  223. )),
  224. )
  225. ],
  226. ),
  227. );
  228. }
  229. Widget buildBottomView() {
  230. return Align(
  231. alignment: Alignment.bottomCenter,
  232. child: Container(
  233. margin: EdgeInsets.only(bottom: 20.h), // 设置底部偏移距离
  234. child: IntrinsicHeight(
  235. child: Column(
  236. crossAxisAlignment: CrossAxisAlignment.end,
  237. children: [
  238. Container(
  239. margin: EdgeInsets.only(right: 8.w),
  240. width: 64.w,
  241. height: 64.w,
  242. child: Assets.images.iconTalkLogo.image()),
  243. SizedBox(height: 24.h),
  244. buildAudioView()
  245. ],
  246. ),
  247. ),
  248. ),
  249. );
  250. }
  251. buildAIAnalysisView() {
  252. return Container();
  253. }
  254. buildAudioView() {
  255. return Container(
  256. decoration: BoxDecoration(
  257. color: Colors.white,
  258. borderRadius: BorderRadius.circular(100),
  259. boxShadow: [
  260. BoxShadow(
  261. color: ColorName.black5.withOpacity(0.1),
  262. spreadRadius: 2,
  263. blurRadius: 12,
  264. offset: const Offset(0, 3),
  265. ),
  266. ],
  267. ),
  268. margin: EdgeInsets.symmetric(horizontal: 12.w),
  269. padding: EdgeInsets.symmetric(vertical: 6.w),
  270. child: Row(
  271. children: [
  272. SizedBox(width: 9.w),
  273. GestureDetector(
  274. onTap: () {
  275. controller.clickPlayAudio();
  276. },
  277. child: Obx(() {
  278. return SizedBox(
  279. width: 36.w,
  280. height: 36.w,
  281. child: (controller.isAudioPlaying.value)
  282. ? Assets.images.iconTalkAudioPlaying.image()
  283. : Assets.images.iconTalkAudioPause.image());
  284. }),
  285. ),
  286. SizedBox(width: 15.w),
  287. Builder(builder: (context) {
  288. return SizedBox(
  289. width: 226.w,
  290. height: 18.w,
  291. child: Obx(() {
  292. return SliderTheme(
  293. data: SliderTheme.of(context).copyWith(
  294. thumbColor: Colors.white,
  295. overlayShape: SliderComponentShape.noOverlay,
  296. trackHeight: 8,
  297. activeTrackColor: "#8A89E9".toColor(),
  298. inactiveTrackColor: "#F6F5F8".toColor(),
  299. trackShape: CustomTrackShape(),
  300. ),
  301. child: Slider(
  302. value: controller.audioProgressValue.value,
  303. min: 0.0,
  304. max: controller.sliderMax,
  305. onChanged: (value) {
  306. controller.updateProgress(value);
  307. },
  308. ),
  309. );
  310. }),
  311. );
  312. }),
  313. SizedBox(width: 11.w),
  314. Obx(() {
  315. return Text(controller.audioDuration.value.toFormattedString(),
  316. style: TextStyle(
  317. fontSize: 10.sp, color: ColorName.secondaryTextColor));
  318. })
  319. ],
  320. ),
  321. );
  322. }
  323. }
  324. class CustomTrackShape extends RoundedRectSliderTrackShape {
  325. @override
  326. Rect getPreferredRect({
  327. required RenderBox parentBox,
  328. Offset offset = Offset.zero,
  329. required SliderThemeData sliderTheme,
  330. bool isEnabled = false,
  331. bool isDiscrete = false,
  332. }) {
  333. final trackHeight = sliderTheme.trackHeight;
  334. final trackLeft = offset.dx;
  335. final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
  336. final trackWidth = parentBox.size.width;
  337. return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
  338. }
  339. }
  340. // class EnhancedShadowSliderThumbShape extends RoundSliderThumbShape {
  341. // final double thumbRadius;
  342. // final double elevation;
  343. // final double shadowOffsetY;
  344. //
  345. // EnhancedShadowSliderThumbShape({
  346. // required this.thumbRadius,
  347. // this.elevation = 8.0,
  348. // this.shadowOffsetY = 0, // 阴影向下偏移量
  349. // });
  350. //
  351. // @override
  352. // void paint(
  353. // PaintingContext context,
  354. // Offset center, {
  355. // required Animation<double> activationAnimation,
  356. // required Animation<double> enableAnimation,
  357. // required bool isDiscrete,
  358. // required TextPainter labelPainter,
  359. // required RenderBox parentBox,
  360. // required SliderThemeData sliderTheme,
  361. // required TextDirection textDirection,
  362. // required double value,
  363. // required double textScaleFactor,
  364. // required Size sizeWithOverflow,
  365. // }) {
  366. // final Canvas canvas = context.canvas;
  367. // final Paint shadowPaint = Paint()
  368. // ..color = Colors.black.withOpacity(0.25)
  369. // ..maskFilter = MaskFilter.blur(BlurStyle.normal, elevation);
  370. //
  371. // final Paint thumbPaint = Paint()
  372. // ..color = sliderTheme.thumbColor!
  373. // ..style = PaintingStyle.fill;
  374. //
  375. // // 调整阴影的绘制位置,使其向下偏移
  376. // canvas.drawCircle(center.translate(0, shadowOffsetY), thumbRadius + 2, shadowPaint);
  377. // canvas.drawCircle(center, thumbRadius, thumbPaint);
  378. // }
  379. // }