view.dart 19 KB

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