view.dart 14 KB

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