view.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. import 'package:dsbridge_flutter/dsbridge_flutter.dart';
  2. import 'package:electronic_assistant/base/base_page.dart';
  3. import 'package:electronic_assistant/module/talk/controller.dart';
  4. import 'package:electronic_assistant/resource/colors.gen.dart';
  5. import 'package:electronic_assistant/utils/expand.dart';
  6. import 'package:electronic_assistant/utils/fixed_size_tab_indicator.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:flutter_screenutil/flutter_screenutil.dart';
  10. import 'package:get/get.dart';
  11. import '../../data/bean/talks.dart';
  12. import '../../data/consts/event_report_id.dart';
  13. import '../../resource/assets.gen.dart';
  14. import '../../resource/string.gen.dart';
  15. import '../../router/app_pages.dart';
  16. import '../../utils/common_style.dart';
  17. import 'original/view.dart';
  18. class TalkPage extends BasePage<TalkController> {
  19. final String? talkId;
  20. TalkPage({super.key})
  21. : talkId = Get.arguments[TalkController.argumentTalkId] {
  22. Get.lazyPut<TalkController>(() => TalkController(),
  23. tag: talkId, fenix: true);
  24. }
  25. @override
  26. get controller => Get.find<TalkController>(tag: talkId);
  27. static void start(TalkBean item, {String eventTag = EventId.id_002}) {
  28. if (Get.currentRoute == RoutePath.talkDetail &&
  29. Get.arguments[TalkController.argumentTalkId] == item.id) {
  30. return;
  31. }
  32. Get.toNamed(RoutePath.talkDetail,
  33. arguments: {
  34. TalkController.argumentItem: item,
  35. TalkController.argumentEventTag: eventTag,
  36. TalkController.argumentTalkId: item.id,
  37. },
  38. preventDuplicates: false);
  39. }
  40. static void startById(String talkId, {String eventTag = ''}) {
  41. if (Get.currentRoute == RoutePath.talkDetail &&
  42. Get.arguments[TalkController.argumentTalkId] == talkId) {
  43. return;
  44. }
  45. Get.toNamed(RoutePath.talkDetail,
  46. arguments: {
  47. TalkController.argumentTalkId: talkId,
  48. TalkController.argumentEventTag: eventTag,
  49. },
  50. preventDuplicates: false);
  51. }
  52. @override
  53. Widget buildBody(BuildContext context) {
  54. return WillPopScope(
  55. onWillPop: () async {
  56. if (controller.isEditModel) {
  57. controller.onEditCancel();
  58. return false;
  59. }
  60. if (controller.isShowMindFullScreen.value) {
  61. controller.onExitMindFullScreen();
  62. return false;
  63. }
  64. return true;
  65. },
  66. child: Stack(
  67. children: [
  68. Obx(() {
  69. return controller.temporaryController != null
  70. ? DWebViewWidget(controller: controller.temporaryController!)
  71. : const SizedBox.shrink();
  72. }),
  73. Obx(() {
  74. return controller.isInitializedView.value
  75. ? DefaultTabController(
  76. initialIndex: controller.defaultIndex,
  77. length: controller.tabBeans.length,
  78. child: _buildTalkContentView())
  79. : const SizedBox.shrink();
  80. }),
  81. buildBottomView()
  82. ],
  83. ),
  84. );
  85. }
  86. Widget _buildTalkContentView() {
  87. return Builder(builder: (context) {
  88. final statusBarHeight = MediaQuery.of(context).padding.top;
  89. return Obx(() {
  90. return Column(
  91. children: [
  92. AnimatedContainer(
  93. key: controller.headGlobalKey,
  94. decoration: BoxDecoration(
  95. gradient: LinearGradient(
  96. colors: ['#E1E9FF'.toColor(), '#F9FAFE'.toColor()],
  97. begin: Alignment.topCenter,
  98. end: Alignment.bottomCenter,
  99. stops: const [0, 1.0],
  100. ),
  101. ),
  102. height: controller.isShowMindFullScreen.value
  103. ? 0
  104. : controller.headViewHeight,
  105. duration: controller.mindFullDuration,
  106. child: SingleChildScrollView(
  107. physics: const NeverScrollableScrollPhysics(),
  108. child: Column(children: [
  109. SizedBox(height: statusBarHeight),
  110. Row(
  111. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  112. children: [
  113. _buildToolbarLeftView(),
  114. _buildToolbarRightView(),
  115. ],
  116. ),
  117. buildTabBar(context),
  118. ]),
  119. ),
  120. ),
  121. buildTalkContentView()
  122. ],
  123. );
  124. });
  125. });
  126. }
  127. Widget buildTabBar(BuildContext context) {
  128. TabController tabController = DefaultTabController.of(context);
  129. tabController.addListener(() {
  130. controller.updateTabIndex(tabController.index);
  131. });
  132. return Obx(() {
  133. if (!controller.isEditModel) {
  134. return Column(
  135. children: [
  136. TabBar(
  137. labelStyle:
  138. TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
  139. unselectedLabelStyle: TextStyle(fontSize: 14.sp),
  140. labelColor: ColorName.primaryTextColor,
  141. unselectedLabelColor: ColorName.secondaryTextColor,
  142. labelPadding: EdgeInsets.only(top: 4.h),
  143. dividerHeight: 0,
  144. splashFactory: NoSplash.splashFactory,
  145. indicator: FixedSizeTabIndicator(
  146. width: 16.w,
  147. height: 3.w,
  148. radius: 3,
  149. color: ColorName.colorPrimary),
  150. tabs: controller.tabBeans
  151. .map((bean) => Tab(text: bean.title))
  152. .toList()),
  153. SizedBox(height: 6.h),
  154. Divider(height: 1, color: '#F2F4F9'.color)
  155. ],
  156. );
  157. } else {
  158. return SizedBox(height: 8.h, width: double.infinity);
  159. }
  160. });
  161. }
  162. @override
  163. bool immersive() {
  164. return true;
  165. }
  166. Widget buildTalkContentView() {
  167. return Obx(() {
  168. if (controller.talkBean.value?.status.value == TalkStatus.notAnalysis &&
  169. controller.isUploading.value != true) {
  170. if (controller.isShowElectricLow.value) {
  171. return buildElectricLowView();
  172. } else {
  173. return buildNotAnalysisView();
  174. }
  175. } else {
  176. return buildTabContentView();
  177. }
  178. });
  179. }
  180. Widget buildTabContentView() {
  181. return Expanded(
  182. child: TabBarView(
  183. physics: controller.isEditModel ||
  184. controller.checkTabBean.value?.isDisallowScroll == true
  185. ? const NeverScrollableScrollPhysics()
  186. : null,
  187. children: controller.pages,
  188. ),
  189. );
  190. }
  191. Widget buildNotAnalysisView() {
  192. return SizedBox(
  193. width: double.infinity,
  194. child: Column(
  195. children: [
  196. SizedBox(height: 119.h),
  197. SizedBox(
  198. width: 100.w,
  199. height: 100.w,
  200. child: Assets.images.iconTalkSummaryUnanalyzed.image()),
  201. SizedBox(height: 4.h),
  202. Text(StringName.talkUnAnalyzed.tr,
  203. style: TextStyle(
  204. fontSize: 15.sp, color: ColorName.primaryTextColor)),
  205. SizedBox(height: 2.h),
  206. Text(StringName.talkUnAnalyzedTips.tr,
  207. style: TextStyle(
  208. fontSize: 12.sp, color: ColorName.secondaryTextColor)),
  209. SizedBox(height: 24.h),
  210. GestureDetector(
  211. onTap: () {
  212. controller.checkCanAnalyze();
  213. },
  214. child: Container(
  215. decoration: getPrimaryBtnDecoration(8),
  216. width: 240.w,
  217. height: 48.w,
  218. child: Center(
  219. child: Text(
  220. StringName.talkAnalyzedBtnTxt.tr,
  221. style: TextStyle(fontSize: 16.sp, color: ColorName.white),
  222. ),
  223. ),
  224. ),
  225. )
  226. ],
  227. ),
  228. );
  229. }
  230. Widget buildElectricLowView() {
  231. return GestureDetector(
  232. onTap: () {
  233. controller.onGoElectricStore();
  234. },
  235. child: Container(
  236. color: const Color(0xFFDFE4FC),
  237. padding:
  238. EdgeInsets.only(left: 16.w, right: 12.w, top: 8.h, bottom: 8.h),
  239. child: Row(
  240. children: [
  241. SizedBox(
  242. width: 46.w,
  243. height: 56.w,
  244. child: Assets.images.iconTalkElectricLow.image()),
  245. SizedBox(width: 10.w),
  246. IntrinsicHeight(
  247. child: Column(
  248. crossAxisAlignment: CrossAxisAlignment.start,
  249. children: [
  250. SizedBox(
  251. width: 90.w,
  252. height: 21.w,
  253. child: Assets.images.iconTalkElectricLowTxt.image()),
  254. SizedBox(width: 1.w),
  255. Text(StringName.talkElectricLow.tr,
  256. style: TextStyle(
  257. fontSize: 12.sp,
  258. color: ColorName.secondaryTextColor)),
  259. ],
  260. ),
  261. ),
  262. const Spacer(),
  263. Container(
  264. decoration: getPrimaryBtnDecoration(8),
  265. width: 100.w,
  266. height: 36.w,
  267. child: Center(
  268. child: Text(StringName.talkGoStore.tr,
  269. style:
  270. TextStyle(fontSize: 16.sp, color: ColorName.white)),
  271. ))
  272. ],
  273. ),
  274. ),
  275. );
  276. }
  277. Widget buildBottomView() {
  278. return Obx(() {
  279. return Visibility(
  280. key: controller.bottomGlobalKey,
  281. visible: controller.isEditModel == false,
  282. child: AnimatedPositioned(
  283. duration: controller.mindFullDuration,
  284. bottom: controller.isShowMindFullScreen.value
  285. ? controller.getBottomViewHeight()
  286. : 0.h,
  287. left: 0,
  288. right: 0,
  289. child: Align(
  290. alignment: Alignment.bottomCenter,
  291. child: Container(
  292. margin: EdgeInsets.only(bottom: 20.h),
  293. child: IntrinsicHeight(
  294. child: Column(
  295. crossAxisAlignment: CrossAxisAlignment.end,
  296. children: [
  297. _buildAIAnalysisView(),
  298. SizedBox(height: 8.h),
  299. _buildTalkEditView(),
  300. SizedBox(height: 10.h),
  301. buildAudioView()
  302. ],
  303. ),
  304. ),
  305. ),
  306. ),
  307. ),
  308. );
  309. });
  310. }
  311. Widget _buildTalkEditView() {
  312. return Obx(() {
  313. return Visibility(
  314. visible: controller.talkBean.value?.status.value ==
  315. TalkStatus.analysisSuccess &&
  316. controller.checkTabBean.value?.isShowEdit == true,
  317. maintainState: true,
  318. maintainAnimation: true,
  319. maintainSize: true,
  320. child: GestureDetector(
  321. onTap: () => controller.onEditModelClick(),
  322. child: Align(
  323. alignment: Alignment.centerLeft,
  324. child: Container(
  325. margin: EdgeInsets.only(left: 12.w),
  326. padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 6.w),
  327. decoration: BoxDecoration(
  328. color: ColorName.white,
  329. borderRadius: BorderRadius.circular(100),
  330. boxShadow: [
  331. BoxShadow(
  332. color: ColorName.black5.withOpacity(0.08),
  333. spreadRadius: 2,
  334. blurRadius: 12,
  335. offset: const Offset(0, 1),
  336. ),
  337. ]),
  338. child: IntrinsicWidth(
  339. child: Row(
  340. children: [
  341. Assets.images.iconTalkEdit.image(width: 16.w, height: 16.w),
  342. SizedBox(width: 2.w),
  343. Text(StringName.talkUpdateTxt.tr,
  344. style: TextStyle(
  345. height: 1,
  346. fontSize: 14.sp,
  347. color: ColorName.primaryTextColor))
  348. ],
  349. ),
  350. ),
  351. ),
  352. ),
  353. ),
  354. );
  355. });
  356. }
  357. Widget _buildAIAnalysisView() {
  358. return Obx(() {
  359. return Visibility(
  360. visible: controller.talkBean.value?.status.value ==
  361. TalkStatus.analysisSuccess,
  362. child: GestureDetector(
  363. onTap: () {
  364. controller.clickAIAnalysis();
  365. },
  366. child: Container(
  367. margin: EdgeInsets.only(right: 8.w),
  368. width: 64.w,
  369. height: 64.w,
  370. child: Assets.images.iconTalkLogo.image()),
  371. ),
  372. );
  373. });
  374. }
  375. buildAudioView() {
  376. return Container(
  377. decoration: BoxDecoration(
  378. color: Colors.white,
  379. borderRadius: BorderRadius.circular(100),
  380. boxShadow: [
  381. BoxShadow(
  382. color: ColorName.black5.withOpacity(0.1),
  383. spreadRadius: 2,
  384. blurRadius: 12,
  385. offset: const Offset(0, 3),
  386. ),
  387. ],
  388. ),
  389. margin: EdgeInsets.symmetric(horizontal: 12.w),
  390. padding: EdgeInsets.symmetric(vertical: 6.w, horizontal: 9.w),
  391. child: Row(
  392. children: [
  393. GestureDetector(
  394. onTap: () {
  395. controller.clickPlayAudio();
  396. },
  397. child: Obx(() {
  398. return SizedBox(
  399. width: 36.w,
  400. height: 36.w,
  401. child: (controller.isAudioPlaying.value)
  402. ? Assets.images.iconTalkAudioPlaying.image()
  403. : Assets.images.iconTalkAudioPause.image());
  404. }),
  405. ),
  406. SizedBox(width: 15.w),
  407. Builder(builder: (context) {
  408. return Flexible(
  409. child: Obx(() {
  410. return SliderTheme(
  411. data: SliderTheme.of(context).copyWith(
  412. thumbColor: Colors.white,
  413. overlayShape: SliderComponentShape.noOverlay,
  414. trackHeight: 8,
  415. activeTrackColor: "#8A89E9".toColor(),
  416. inactiveTrackColor: "#F6F5F8".toColor(),
  417. trackShape: CustomTrackShape(),
  418. ),
  419. child: Slider(
  420. value: controller.audioProgressValue.value,
  421. min: 0.0,
  422. max: controller.sliderMax,
  423. onChanged: (value) {
  424. controller.updateProgress(value);
  425. },
  426. ),
  427. );
  428. }),
  429. );
  430. }),
  431. SizedBox(width: 11.w),
  432. Obx(() {
  433. return Text(controller.audioDuration.value.toFormattedString(),
  434. style: TextStyle(
  435. fontSize: 10.sp, color: ColorName.secondaryTextColor));
  436. })
  437. ],
  438. ),
  439. );
  440. }
  441. Widget _buildToolbarLeftView() {
  442. return Obx(() {
  443. if (controller.isEditModel) {
  444. return Align(
  445. alignment: Alignment.centerLeft,
  446. child: IconButton(
  447. icon: Assets.images.iconTalkEditCancel
  448. .image(width: 24.w, height: 24.w),
  449. onPressed: () {
  450. controller.onEditCancel();
  451. },
  452. ),
  453. );
  454. } else {
  455. return Row(
  456. children: [
  457. IconButton(
  458. icon: Assets.images.iconTalkBack.image(width: 24.w, height: 24.w),
  459. onPressed: () {
  460. Get.back();
  461. },
  462. ),
  463. SizedBox(width: 6.w),
  464. Obx(() {
  465. return GestureDetector(
  466. onTap: () {
  467. controller.onEditTitleClick();
  468. },
  469. child: Column(
  470. mainAxisAlignment: MainAxisAlignment.center,
  471. crossAxisAlignment: CrossAxisAlignment.start,
  472. children: [
  473. Row(
  474. children: [
  475. ConstrainedBox(
  476. constraints: BoxConstraints(maxWidth: 0.65.sw),
  477. child: Text(
  478. maxLines: 1,
  479. overflow: TextOverflow.ellipsis,
  480. controller.talkBean.value?.title.value ?? '',
  481. style: TextStyle(
  482. fontWeight: FontWeight.bold,
  483. fontSize: 15.sp,
  484. color: ColorName.primaryTextColor,
  485. ),
  486. ),
  487. ),
  488. SizedBox(width: 2.w),
  489. Assets.images.iconTalkEditTitle
  490. .image(width: 16.w, height: 16.w)
  491. ],
  492. ),
  493. SizedBox(height: 2.h),
  494. Text(
  495. controller.talkBean.value?.createTime ?? '',
  496. style: TextStyle(
  497. fontSize: 11.sp, color: ColorName.secondaryTextColor),
  498. )
  499. ],
  500. ),
  501. );
  502. })
  503. ],
  504. );
  505. }
  506. });
  507. }
  508. Widget _buildToolbarRightView() {
  509. return Obx(() {
  510. return Visibility(
  511. visible: controller.talkBean.value?.status.value ==
  512. TalkStatus.analysisSuccess,
  513. child: controller.isEditModel
  514. ? GestureDetector(
  515. onTap: () {
  516. controller.onEditDoneClick();
  517. },
  518. child: Padding(
  519. padding: EdgeInsets.symmetric(horizontal: 12.w),
  520. child: Text(StringName.done.tr,
  521. style: TextStyle(
  522. fontSize: 17.sp, color: ColorName.primaryTextColor)),
  523. ),
  524. )
  525. : Row(
  526. children: [
  527. IconButton(
  528. icon: Assets.images.iconTalkShare
  529. .image(width: 20.w, height: 20.w),
  530. onPressed: () {
  531. controller.onShareClick();
  532. },
  533. ),
  534. ],
  535. ),
  536. );
  537. });
  538. }
  539. }
  540. class CustomTrackShape extends RoundedRectSliderTrackShape {
  541. @override
  542. Rect getPreferredRect({
  543. required RenderBox parentBox,
  544. Offset offset = Offset.zero,
  545. required SliderThemeData sliderTheme,
  546. bool isEnabled = false,
  547. bool isDiscrete = false,
  548. }) {
  549. final trackHeight = sliderTheme.trackHeight;
  550. final trackLeft = offset.dx;
  551. final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
  552. final trackWidth = parentBox.size.width;
  553. return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
  554. }
  555. }