view.dart 18 KB

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