view.dart 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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: Obx(() {
  67. return DefaultTabController(
  68. initialIndex: controller.defaultIndex,
  69. length: controller.tabBeans.length,
  70. child: Stack(
  71. children: [
  72. Obx(() {
  73. return controller.temporaryController != null
  74. ? DWebViewWidget(
  75. controller: controller.temporaryController!)
  76. : const SizedBox.shrink();
  77. }),
  78. _buildTalkContentView(),
  79. buildBottomView()
  80. ],
  81. ),
  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. controller.tabBeans.isEmpty
  137. ? const SizedBox.shrink()
  138. : TabBar(
  139. labelStyle:
  140. TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
  141. unselectedLabelStyle: TextStyle(fontSize: 14.sp),
  142. labelColor: ColorName.primaryTextColor,
  143. unselectedLabelColor: ColorName.secondaryTextColor,
  144. labelPadding: EdgeInsets.only(top: 4.h),
  145. dividerHeight: 0,
  146. splashFactory: NoSplash.splashFactory,
  147. indicator: FixedSizeTabIndicator(
  148. width: 16.w,
  149. height: 3.w,
  150. radius: 3,
  151. color: ColorName.colorPrimary),
  152. tabs: controller.tabBeans
  153. .map((bean) => Tab(text: bean.title))
  154. .toList()),
  155. SizedBox(height: 6.h),
  156. Divider(height: 1, color: '#F2F4F9'.color)
  157. ],
  158. );
  159. } else {
  160. return SizedBox(height: 8.h, width: double.infinity);
  161. }
  162. });
  163. }
  164. @override
  165. bool immersive() {
  166. return true;
  167. }
  168. Widget buildTalkContentView() {
  169. return Obx(() {
  170. if (controller.talkBean.value?.status.value == TalkStatus.notAnalysis &&
  171. controller.isUploading.value != true) {
  172. if (controller.isShowElectricLow.value) {
  173. return buildElectricLowView();
  174. } else {
  175. return buildNotAnalysisView();
  176. }
  177. } else {
  178. return buildTabContentView();
  179. }
  180. });
  181. }
  182. Widget buildTabContentView() {
  183. return Expanded(
  184. child: Obx(() {
  185. return controller.pages.isEmpty
  186. ? const SizedBox.shrink()
  187. : TabBarView(
  188. physics: controller.isEditModel ||
  189. controller.checkTabBean.value?.isDisallowScroll == true
  190. ? const NeverScrollableScrollPhysics()
  191. : null,
  192. children: controller.pages,
  193. );
  194. }),
  195. );
  196. }
  197. Widget buildNotAnalysisView() {
  198. return SizedBox(
  199. width: double.infinity,
  200. child: Column(
  201. children: [
  202. SizedBox(height: 119.h),
  203. SizedBox(
  204. width: 100.w,
  205. height: 100.w,
  206. child: Assets.images.iconTalkSummaryUnanalyzed.image()),
  207. SizedBox(height: 4.h),
  208. Text(StringName.talkUnAnalyzed.tr,
  209. style: TextStyle(
  210. fontSize: 15.sp, color: ColorName.primaryTextColor)),
  211. SizedBox(height: 2.h),
  212. Text(StringName.talkUnAnalyzedTips.tr,
  213. style: TextStyle(
  214. fontSize: 12.sp, color: ColorName.secondaryTextColor)),
  215. SizedBox(height: 24.h),
  216. GestureDetector(
  217. onTap: () {
  218. controller.checkCanAnalyze();
  219. },
  220. child: Container(
  221. decoration: getPrimaryBtnDecoration(8),
  222. width: 240.w,
  223. height: 48.w,
  224. child: Center(
  225. child: Text(
  226. StringName.talkAnalyzedBtnTxt.tr,
  227. style: TextStyle(fontSize: 16.sp, color: ColorName.white),
  228. ),
  229. ),
  230. ),
  231. )
  232. ],
  233. ),
  234. );
  235. }
  236. Widget buildElectricLowView() {
  237. return GestureDetector(
  238. onTap: () {
  239. controller.onGoElectricStore();
  240. },
  241. child: Container(
  242. color: const Color(0xFFDFE4FC),
  243. padding:
  244. EdgeInsets.only(left: 16.w, right: 12.w, top: 8.h, bottom: 8.h),
  245. child: Row(
  246. children: [
  247. SizedBox(
  248. width: 46.w,
  249. height: 56.w,
  250. child: Assets.images.iconTalkElectricLow.image()),
  251. SizedBox(width: 10.w),
  252. IntrinsicHeight(
  253. child: Column(
  254. crossAxisAlignment: CrossAxisAlignment.start,
  255. children: [
  256. SizedBox(
  257. width: 90.w,
  258. height: 21.w,
  259. child: Assets.images.iconTalkElectricLowTxt.image()),
  260. SizedBox(width: 1.w),
  261. Text(StringName.talkElectricLow.tr,
  262. style: TextStyle(
  263. fontSize: 12.sp,
  264. color: ColorName.secondaryTextColor)),
  265. ],
  266. ),
  267. ),
  268. const Spacer(),
  269. Container(
  270. decoration: getPrimaryBtnDecoration(8),
  271. width: 100.w,
  272. height: 36.w,
  273. child: Center(
  274. child: Text(StringName.talkGoStore.tr,
  275. style:
  276. TextStyle(fontSize: 16.sp, color: ColorName.white)),
  277. ))
  278. ],
  279. ),
  280. ),
  281. );
  282. }
  283. Widget buildBottomView() {
  284. return Obx(() {
  285. return Visibility(
  286. key: controller.bottomGlobalKey,
  287. visible: controller.isEditModel == false,
  288. child: AnimatedPositioned(
  289. duration: controller.mindFullDuration,
  290. bottom: controller.isShowMindFullScreen.value
  291. ? controller.getBottomViewHeight()
  292. : 0.h,
  293. left: 0,
  294. right: 0,
  295. child: Align(
  296. alignment: Alignment.bottomCenter,
  297. child: Container(
  298. margin: EdgeInsets.only(bottom: 20.h),
  299. child: IntrinsicHeight(
  300. child: Column(
  301. crossAxisAlignment: CrossAxisAlignment.end,
  302. children: [
  303. _buildAIAnalysisView(),
  304. SizedBox(height: 8.h),
  305. _buildTalkEditView(),
  306. SizedBox(height: 10.h),
  307. buildAudioView()
  308. ],
  309. ),
  310. ),
  311. ),
  312. ),
  313. ),
  314. );
  315. });
  316. }
  317. Widget _buildTalkEditView() {
  318. return Obx(() {
  319. return Visibility(
  320. visible: controller.talkBean.value?.status.value ==
  321. TalkStatus.analysisSuccess &&
  322. controller.checkTabBean.value?.isShowEdit == true,
  323. maintainState: true,
  324. maintainAnimation: true,
  325. maintainSize: true,
  326. child: GestureDetector(
  327. onTap: () => controller.onEditModelClick(),
  328. child: Align(
  329. alignment: Alignment.centerLeft,
  330. child: Container(
  331. margin: EdgeInsets.only(left: 12.w),
  332. padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 6.w),
  333. decoration: BoxDecoration(
  334. color: ColorName.white,
  335. borderRadius: BorderRadius.circular(100),
  336. boxShadow: [
  337. BoxShadow(
  338. color: ColorName.black5.withOpacity(0.08),
  339. spreadRadius: 2,
  340. blurRadius: 12,
  341. offset: const Offset(0, 1),
  342. ),
  343. ]),
  344. child: IntrinsicWidth(
  345. child: Row(
  346. children: [
  347. Assets.images.iconTalkEdit.image(width: 16.w, height: 16.w),
  348. SizedBox(width: 2.w),
  349. Text(StringName.talkUpdateTxt.tr,
  350. style: TextStyle(
  351. height: 1,
  352. fontSize: 14.sp,
  353. color: ColorName.primaryTextColor))
  354. ],
  355. ),
  356. ),
  357. ),
  358. ),
  359. ),
  360. );
  361. });
  362. }
  363. Widget _buildAIAnalysisView() {
  364. return Obx(() {
  365. return Visibility(
  366. visible: controller.talkBean.value?.status.value ==
  367. TalkStatus.analysisSuccess,
  368. child: GestureDetector(
  369. onTap: () {
  370. controller.clickAIAnalysis();
  371. },
  372. child: Container(
  373. margin: EdgeInsets.only(right: 8.w),
  374. width: 64.w,
  375. height: 64.w,
  376. child: Assets.images.iconTalkLogo.image()),
  377. ),
  378. );
  379. });
  380. }
  381. buildAudioView() {
  382. return Container(
  383. decoration: BoxDecoration(
  384. color: Colors.white,
  385. borderRadius: BorderRadius.circular(100),
  386. boxShadow: [
  387. BoxShadow(
  388. color: ColorName.black5.withOpacity(0.1),
  389. spreadRadius: 2,
  390. blurRadius: 12,
  391. offset: const Offset(0, 3),
  392. ),
  393. ],
  394. ),
  395. margin: EdgeInsets.symmetric(horizontal: 12.w),
  396. padding: EdgeInsets.symmetric(vertical: 6.w, horizontal: 9.w),
  397. child: Row(
  398. children: [
  399. GestureDetector(
  400. onTap: () {
  401. controller.clickPlayAudio();
  402. },
  403. child: Obx(() {
  404. return SizedBox(
  405. width: 36.w,
  406. height: 36.w,
  407. child: (controller.isAudioPlaying.value)
  408. ? Assets.images.iconTalkAudioPlaying.image()
  409. : Assets.images.iconTalkAudioPause.image());
  410. }),
  411. ),
  412. SizedBox(width: 15.w),
  413. Builder(builder: (context) {
  414. return Flexible(
  415. child: Obx(() {
  416. return SliderTheme(
  417. data: SliderTheme.of(context).copyWith(
  418. thumbColor: Colors.white,
  419. overlayShape: SliderComponentShape.noOverlay,
  420. trackHeight: 8,
  421. activeTrackColor: "#8A89E9".toColor(),
  422. inactiveTrackColor: "#F6F5F8".toColor(),
  423. trackShape: CustomTrackShape(),
  424. ),
  425. child: Slider(
  426. value: controller.audioProgressValue.value,
  427. min: 0.0,
  428. max: controller.sliderMax,
  429. onChanged: (value) {
  430. controller.updateProgress(value);
  431. },
  432. ),
  433. );
  434. }),
  435. );
  436. }),
  437. SizedBox(width: 11.w),
  438. Obx(() {
  439. return Text(controller.audioDuration.value.toFormattedString(),
  440. style: TextStyle(
  441. fontSize: 10.sp, color: ColorName.secondaryTextColor));
  442. })
  443. ],
  444. ),
  445. );
  446. }
  447. Widget _buildToolbarLeftView() {
  448. return Obx(() {
  449. if (controller.isEditModel) {
  450. return Align(
  451. alignment: Alignment.centerLeft,
  452. child: IconButton(
  453. icon: Assets.images.iconTalkEditCancel
  454. .image(width: 24.w, height: 24.w),
  455. onPressed: () {
  456. controller.onEditCancel();
  457. },
  458. ),
  459. );
  460. } else {
  461. return Row(
  462. children: [
  463. IconButton(
  464. icon: Assets.images.iconTalkBack.image(width: 24.w, height: 24.w),
  465. onPressed: () {
  466. Get.back();
  467. },
  468. ),
  469. SizedBox(width: 6.w),
  470. Obx(() {
  471. return GestureDetector(
  472. onTap: () {
  473. controller.onEditTitleClick();
  474. },
  475. child: Column(
  476. mainAxisAlignment: MainAxisAlignment.center,
  477. crossAxisAlignment: CrossAxisAlignment.start,
  478. children: [
  479. Row(
  480. children: [
  481. ConstrainedBox(
  482. constraints: BoxConstraints(maxWidth: 0.65.sw),
  483. child: Text(
  484. maxLines: 1,
  485. overflow: TextOverflow.ellipsis,
  486. controller.talkBean.value?.title.value ?? '',
  487. style: TextStyle(
  488. fontWeight: FontWeight.bold,
  489. fontSize: 15.sp,
  490. color: ColorName.primaryTextColor,
  491. ),
  492. ),
  493. ),
  494. SizedBox(width: 2.w),
  495. Assets.images.iconTalkEditTitle
  496. .image(width: 16.w, height: 16.w)
  497. ],
  498. ),
  499. SizedBox(height: 2.h),
  500. Text(
  501. controller.talkBean.value?.createTime ?? '',
  502. style: TextStyle(
  503. fontSize: 11.sp, color: ColorName.secondaryTextColor),
  504. )
  505. ],
  506. ),
  507. );
  508. })
  509. ],
  510. );
  511. }
  512. });
  513. }
  514. Widget _buildToolbarRightView() {
  515. return Obx(() {
  516. return Visibility(
  517. visible: controller.talkBean.value?.status.value ==
  518. TalkStatus.analysisSuccess,
  519. child: controller.isEditModel
  520. ? GestureDetector(
  521. onTap: () {
  522. controller.onEditDoneClick();
  523. },
  524. child: Padding(
  525. padding: EdgeInsets.symmetric(horizontal: 12.w),
  526. child: Text(StringName.done.tr,
  527. style: TextStyle(
  528. fontSize: 17.sp, color: ColorName.primaryTextColor)),
  529. ),
  530. )
  531. : Row(
  532. children: [
  533. IconButton(
  534. icon: Assets.images.iconTalkShare
  535. .image(width: 20.w, height: 20.w),
  536. onPressed: () {
  537. controller.onShareClick();
  538. },
  539. ),
  540. ],
  541. ),
  542. );
  543. });
  544. }
  545. }
  546. class CustomTrackShape extends RoundedRectSliderTrackShape {
  547. @override
  548. Rect getPreferredRect({
  549. required RenderBox parentBox,
  550. Offset offset = Offset.zero,
  551. required SliderThemeData sliderTheme,
  552. bool isEnabled = false,
  553. bool isDiscrete = false,
  554. }) {
  555. final trackHeight = sliderTheme.trackHeight;
  556. final trackLeft = offset.dx;
  557. final trackTop = offset.dy + (parentBox.size.height - trackHeight!) / 2;
  558. final trackWidth = parentBox.size.width;
  559. return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
  560. }
  561. }