view.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. import 'package:electronic_assistant/base/base_page.dart';
  2. import 'package:electronic_assistant/data/bean/talks.dart';
  3. import 'package:electronic_assistant/data/repositories/account_repository.dart';
  4. import 'package:electronic_assistant/dialog/rename_dialog.dart';
  5. import 'package:electronic_assistant/dialog/talk_delete_dialog.dart';
  6. import 'package:electronic_assistant/module/chat/view.dart';
  7. import 'package:electronic_assistant/popup/talk_popup.dart';
  8. import 'package:electronic_assistant/resource/assets.gen.dart';
  9. import 'package:electronic_assistant/resource/colors.gen.dart';
  10. import 'package:electronic_assistant/resource/string.gen.dart';
  11. import 'package:electronic_assistant/utils/expand.dart';
  12. import 'package:electronic_assistant/widget/pull_to_refresh.dart';
  13. import 'package:flutter/gestures.dart';
  14. import 'package:flutter/material.dart';
  15. import 'package:flutter_screenutil/flutter_screenutil.dart';
  16. import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
  17. import 'package:get/get.dart';
  18. import '../../data/bean/agenda.dart';
  19. import '../../router/app_pages.dart';
  20. import '../agenda/detail/view.dart';
  21. import '../agenda/task_item_view.dart';
  22. import '../talk/view.dart';
  23. import 'controller.dart';
  24. class HomePage extends BasePage<HomePageController> {
  25. const HomePage({super.key});
  26. @override
  27. Widget buildBody(BuildContext context) {
  28. return Stack(
  29. children: [
  30. buildBgBox(),
  31. SafeArea(
  32. child: Column(
  33. children: [
  34. Container(
  35. width: 1.sw,
  36. padding: const EdgeInsets.all(12).w,
  37. child: buildOperationBar(),
  38. ),
  39. Expanded(
  40. child: Container(
  41. color: "#F6F5F8".toColor(),
  42. child: PullToRefresh(
  43. enableRefresh: true,
  44. controller: controller.refreshController,
  45. onRefresh: () {
  46. controller.requestHomeData();
  47. },
  48. child: CustomScrollView(
  49. slivers: [
  50. buildHeaderView(),
  51. buildTalkRecordTitle(),
  52. SliverToBoxAdapter(
  53. child: Container(
  54. decoration: BoxDecoration(
  55. gradient: LinearGradient(
  56. begin: Alignment.topCenter,
  57. end: Alignment.bottomCenter,
  58. colors: [
  59. "#FEFEFE".toColor(),
  60. "#FCFBFC".toColor()
  61. ],
  62. ),
  63. ),
  64. height: 0.3472222.sw,
  65. padding: EdgeInsets.only(bottom: 15.h),
  66. child: buildTalkRecord(),
  67. ),
  68. ),
  69. buildTalkTodoTitle(),
  70. buildTalkTodoContent(),
  71. buildSeeMoreView(),
  72. ],
  73. ),
  74. ),
  75. ))
  76. ],
  77. ),
  78. )
  79. ],
  80. );
  81. }
  82. @override
  83. bool immersive() {
  84. return true;
  85. }
  86. Widget buildHeaderView() {
  87. return SliverToBoxAdapter(
  88. child: Container(
  89. decoration: BoxDecoration(
  90. gradient: LinearGradient(
  91. begin: Alignment.topCenter,
  92. end: Alignment.bottomCenter,
  93. colors: [ColorName.white, "#EEEFFB".toColor()],
  94. ),
  95. ),
  96. padding:
  97. EdgeInsets.only(left: 12.w, right: 12.w, top: 8.h, bottom: 16.h),
  98. child: Row(
  99. children: [
  100. Expanded(
  101. child: _buildHeaderCard(
  102. StringName.homeTalkAudio.tr,
  103. StringName.homeTalkQuickAudio.tr,
  104. Assets.images.iconHomeTalkRecordCard.image().image, [
  105. "#1763F9".toColor(),
  106. "#28B2FF".toColor(),
  107. ], onTap: () {
  108. Get.toNamed(RoutePath.record);
  109. }),
  110. ),
  111. SizedBox(width: 8.w),
  112. Expanded(
  113. child: _buildHeaderCard(
  114. StringName.homeTalkImportAudio.tr,
  115. StringName.homeTalkAnalyzeLocalAudio.tr,
  116. Assets.images.iconHomeTalkSelectCard.image().image, [
  117. "#5869ED".toColor(),
  118. "#6E8AF7".toColor(),
  119. ], onTap: () {
  120. controller.onPickerAudioFile();
  121. }),
  122. ),
  123. ],
  124. ),
  125. ),
  126. );
  127. }
  128. Widget _buildHeaderCard(
  129. String title,
  130. String content,
  131. ImageProvider imageProvider,
  132. List<Color> bgColor, {
  133. VoidCallback? onTap,
  134. }) {
  135. return GestureDetector(
  136. onTap: onTap,
  137. child: AspectRatio(
  138. aspectRatio: 164 / 80,
  139. child: Container(
  140. decoration: BoxDecoration(
  141. gradient: LinearGradient(
  142. begin: Alignment.topLeft,
  143. end: Alignment.bottomRight,
  144. colors: bgColor,
  145. ),
  146. borderRadius: BorderRadius.circular(12),
  147. ),
  148. child: Stack(
  149. children: [
  150. Align(
  151. alignment: Alignment.centerRight,
  152. child: AspectRatio(
  153. aspectRatio: 1,
  154. child: SizedBox(
  155. height: double.infinity,
  156. child: Image(image: imageProvider)),
  157. ),
  158. ),
  159. Align(
  160. alignment: Alignment.centerLeft,
  161. child: IntrinsicHeight(
  162. child: Container(
  163. margin: EdgeInsets.only(left: 12.w),
  164. child: Column(
  165. crossAxisAlignment: CrossAxisAlignment.start,
  166. children: [
  167. Text(title,
  168. style: TextStyle(
  169. fontSize: 16.sp,
  170. color: ColorName.white,
  171. fontWeight: FontWeight.bold)),
  172. SizedBox(height: 4.h),
  173. Text(content,
  174. style: TextStyle(
  175. fontSize: 12.sp, color: ColorName.white80)),
  176. ],
  177. ),
  178. ),
  179. ),
  180. )
  181. ],
  182. ),
  183. ),
  184. ),
  185. );
  186. }
  187. SliverToBoxAdapter buildSeeMoreView() {
  188. return SliverToBoxAdapter(
  189. child: Obx(() {
  190. return Visibility(
  191. visible: controller.agendaList.isNotEmpty,
  192. child: Container(
  193. alignment: Alignment.center,
  194. padding: const EdgeInsets.only(top: 12, bottom: 36).w,
  195. child: RichText(
  196. text: TextSpan(
  197. text: StringName.homeTalkTodo1.tr,
  198. style: TextStyle(
  199. color: ColorName.secondaryTextColor, fontSize: 12.sp),
  200. children: <TextSpan>[
  201. TextSpan(
  202. text: StringName.homeTalkTodo2.tr,
  203. style: TextStyle(
  204. color: ColorName.colorPrimary, fontSize: 12.sp),
  205. recognizer: TapGestureRecognizer()
  206. ..onTap = () {
  207. controller.onGoAgendaList();
  208. }),
  209. ],
  210. ),
  211. ),
  212. ),
  213. );
  214. }),
  215. );
  216. }
  217. SliverToBoxAdapter buildTalkTodoTitle() {
  218. return SliverToBoxAdapter(
  219. child: Container(
  220. decoration: BoxDecoration(
  221. gradient: LinearGradient(
  222. begin: Alignment.topCenter,
  223. end: Alignment.bottomCenter,
  224. colors: ["#FCFBFC".toColor(), "#F6F5F8".toColor()],
  225. ),
  226. ),
  227. child: Column(
  228. children: [
  229. SizedBox(height: 12.w),
  230. buildTitle(StringName.homeTalkTodoTitle.tr, () {
  231. controller.onGoAgendaList();
  232. }),
  233. SizedBox(height: 12.w)
  234. ],
  235. ),
  236. ));
  237. }
  238. SliverToBoxAdapter buildTalkRecordTitle() {
  239. return SliverToBoxAdapter(
  240. child: Container(
  241. decoration: BoxDecoration(
  242. gradient: LinearGradient(
  243. begin: Alignment.topCenter,
  244. end: Alignment.bottomCenter,
  245. colors: [ColorName.white, "#FEFEFE".toColor()],
  246. ),
  247. ),
  248. padding: const EdgeInsets.symmetric(vertical: 12).w,
  249. child: buildTitle(StringName.homeTalkRecord.tr, () {
  250. controller.goTalkRecordPage();
  251. }),
  252. ),
  253. );
  254. }
  255. Row buildOperationBar() {
  256. return Row(children: [buildGoLogin(), const Spacer(), buildGoStore()]);
  257. }
  258. GestureDetector buildGoStore() {
  259. return GestureDetector(
  260. child: DecoratedBox(
  261. decoration: BoxDecoration(
  262. color: "#F6F5F8".toColor(),
  263. borderRadius: BorderRadius.circular(16),
  264. ),
  265. child: Padding(
  266. padding:
  267. const EdgeInsets.symmetric(horizontal: 10, vertical: 5).w,
  268. child: Row(
  269. crossAxisAlignment: CrossAxisAlignment.center,
  270. children: [
  271. SizedBox(
  272. width: 20.w,
  273. height: 20.w,
  274. child: Assets.images.iconHomeCharge.image()),
  275. SizedBox(width: 2.w),
  276. Text(StringName.homeChargeTxt.tr,
  277. style: TextStyle(
  278. fontSize: 15.sp, color: ColorName.primaryTextColor)),
  279. ],
  280. ),
  281. )),
  282. onTap: () {
  283. // accountRepository.logout();
  284. // ToastUtil.showToast('GoStore');
  285. Get.toNamed(RoutePath.store);
  286. });
  287. }
  288. Widget buildGoLogin() {
  289. return Obx(() {
  290. return GestureDetector(
  291. onTap: () {
  292. if (controller.isLogin) {
  293. controller.showLoginDrawer();
  294. } else {
  295. Get.toNamed(RoutePath.login);
  296. }
  297. },
  298. child: Row(
  299. children: [
  300. SizedBox(
  301. width: 36.w,
  302. height: 36.w,
  303. child: controller.isLogin
  304. ? Assets.images.iconHomeLogged.image()
  305. : Assets.images.iconHomeNoLogin.image()),
  306. SizedBox(width: 8.w),
  307. Text(controller.loginTxt,
  308. style: TextStyle(
  309. fontWeight: FontWeight.bold,
  310. fontSize: 15.sp,
  311. color: ColorName.primaryTextColor)),
  312. SizedBox(width: 6.w),
  313. controller.isLogin
  314. ? const SizedBox.shrink()
  315. : SizedBox(
  316. height: 16.w, child: Assets.images.iconGoLoginArrow.image())
  317. ],
  318. ),
  319. );
  320. });
  321. }
  322. DecoratedBox buildBgBox() {
  323. return DecoratedBox(
  324. decoration: const BoxDecoration(color: ColorName.white),
  325. child: Container(
  326. height: double.infinity,
  327. ));
  328. }
  329. Widget buildTalkRecord() {
  330. return SizedBox(
  331. width: 1.sw,
  332. child: CustomScrollView(
  333. scrollDirection: Axis.horizontal,
  334. slivers: [
  335. SliverToBoxAdapter(child: SizedBox(width: 12.w)),
  336. Obx(() {
  337. return SliverList.builder(
  338. itemBuilder: _builderTalkItem,
  339. itemCount: controller.talkList.length >= 10
  340. ? 10
  341. : controller.talkList.length);
  342. }),
  343. ],
  344. ),
  345. );
  346. }
  347. Widget _builderTalkItem(BuildContext context, int index) {
  348. return Obx(() {
  349. TalkBean? item = controller.talkList[index];
  350. return _buildTalkView(item, onLongPressStart: (details) {
  351. if (!accountRepository.isLogin.value) {
  352. return;
  353. }
  354. showTalkPopup(details.globalPosition, Alignment.bottomRight,
  355. onRename: () {
  356. showRenameTalkDialog(item);
  357. }, onDelete: () {
  358. showDeleteTalkDialog(item);
  359. });
  360. }, onItemClick: () {
  361. TalkPage.start(item);
  362. });
  363. });
  364. }
  365. Widget _builderAgendaItem(BuildContext context, int index) {
  366. return Obx(() {
  367. Agenda item = controller.agendaList[index];
  368. return GestureDetector(
  369. onTap: () {
  370. AgendaDetailPage.start(item);
  371. },
  372. child: taskItemView(
  373. item,
  374. onThinkingClick: () {
  375. ChatPage.startByTalkId(item.talkId, agenda: item);
  376. },
  377. onCheckClick: () {
  378. controller.agendaComplete(item);
  379. },
  380. ),
  381. );
  382. });
  383. }
  384. SliverToBoxAdapter buildGoRecordView() {
  385. return SliverToBoxAdapter(
  386. child: GestureDetector(
  387. onTap: () => Get.toNamed(RoutePath.record),
  388. child: Container(
  389. margin: EdgeInsets.only(right: 8.w),
  390. decoration: BoxDecoration(
  391. color: Colors.white,
  392. border: Border.all(color: '#EBEBFF'.toColor(), width: 1),
  393. borderRadius: BorderRadius.circular(8.0),
  394. ),
  395. child: SizedBox(
  396. width: 100.w,
  397. height: double.infinity,
  398. child: Stack(
  399. children: [
  400. Positioned(
  401. right: 0,
  402. bottom: 0,
  403. child: SizedBox(
  404. width: 48.w,
  405. child: Assets.images.bgHomeQuickAudio.image(),
  406. ),
  407. ),
  408. Positioned.fill(
  409. child: Align(
  410. alignment: Alignment.center,
  411. child: Column(
  412. children: [
  413. SizedBox(height: 20.h),
  414. SizedBox(
  415. width: 32.w,
  416. height: 32.w,
  417. child: Assets.images.iconAddTalk.image()),
  418. SizedBox(height: 6.h),
  419. Text(StringName.homeTalkAudio.tr,
  420. style: TextStyle(
  421. fontSize: 14.sp,
  422. color: ColorName.colorPrimary,
  423. fontWeight: FontWeight.bold)),
  424. Builder(builder: (context) {
  425. controller.todoTargetContext = context;
  426. return Text(StringName.homeTalkQuickAudio.tr,
  427. style: TextStyle(
  428. fontSize: 10.sp,
  429. color: ColorName.secondaryTextColor));
  430. })
  431. ],
  432. ),
  433. )),
  434. ],
  435. ),
  436. ),
  437. ),
  438. ));
  439. }
  440. Widget _buildTalkView(TalkBean item,
  441. {VoidCallback? onItemClick,
  442. GestureLongPressStartCallback? onLongPressStart}) {
  443. return GestureDetector(
  444. onTap: onItemClick,
  445. onLongPressStart: onLongPressStart,
  446. child: Container(
  447. width: 258.w,
  448. margin: EdgeInsets.only(right: 8.w),
  449. decoration: BoxDecoration(
  450. color: Colors.white,
  451. border: Border.all(color: '#F0F0F0'.toColor(), width: 2),
  452. borderRadius: const BorderRadius.only(
  453. topLeft: Radius.circular(12),
  454. topRight: Radius.circular(24),
  455. bottomRight: Radius.circular(12),
  456. bottomLeft: Radius.circular(12)),
  457. ),
  458. padding: EdgeInsets.only(left: 10.w, right: 16.w),
  459. child: Row(
  460. crossAxisAlignment: CrossAxisAlignment.start,
  461. children: [
  462. Padding(
  463. padding: const EdgeInsets.only(top: 14).h,
  464. child: Stack(
  465. children: [
  466. SizedBox(
  467. width: 35.w,
  468. height: 40.w,
  469. child: Assets.images.iconFilesFile.image()),
  470. Visibility(
  471. visible: item.isExample.isTrue,
  472. child: Container(
  473. margin: const EdgeInsets.only(top: 32).w,
  474. decoration: BoxDecoration(
  475. color: "#B2BAC4".toColor(),
  476. borderRadius: BorderRadius.circular(4)),
  477. padding: const EdgeInsets.symmetric(
  478. horizontal: 5.5, vertical: 2)
  479. .w,
  480. child: Text(StringName.homeTalkExample.tr,
  481. style: TextStyle(
  482. height: 1,
  483. fontSize: 12.sp,
  484. color: ColorName.white)),
  485. ),
  486. ),
  487. ],
  488. ),
  489. ),
  490. SizedBox(width: 8.w),
  491. Expanded(
  492. child: Column(
  493. mainAxisAlignment: MainAxisAlignment.center,
  494. crossAxisAlignment: CrossAxisAlignment.start,
  495. children: [
  496. Row(
  497. children: [
  498. Text(item.title.value.orEmpty,
  499. maxLines: 1,
  500. overflow: TextOverflow.ellipsis,
  501. style: TextStyle(
  502. fontSize: 15.sp,
  503. color: ColorName.primaryTextColor,
  504. fontWeight: FontWeight.bold))
  505. ],
  506. ),
  507. SizedBox(height: 5.h),
  508. Text(
  509. item.summary.value.orEmpty,
  510. style: TextStyle(
  511. fontSize: 12.sp, color: ColorName.secondaryTextColor),
  512. overflow: TextOverflow.ellipsis,
  513. maxLines: 2,
  514. ),
  515. SizedBox(height: 8.h),
  516. Row(
  517. crossAxisAlignment: CrossAxisAlignment.center,
  518. children: [
  519. Text(item.duration.toFormattedDuration(),
  520. style: TextStyle(
  521. fontSize: 12.sp,
  522. color: ColorName.tertiaryTextColor)),
  523. SizedBox(width: 6.w),
  524. Container(
  525. width: 1,
  526. height: 9,
  527. color: ColorName.tertiaryTextColor),
  528. SizedBox(width: 6.w),
  529. Text(item.createTime.orEmpty,
  530. style: TextStyle(
  531. fontSize: 12.sp,
  532. color: ColorName.tertiaryTextColor))
  533. ],
  534. )
  535. ],
  536. ),
  537. )
  538. ],
  539. )),
  540. );
  541. }
  542. Widget buildTitle(String titleName, VoidCallback? onTap) {
  543. return Padding(
  544. padding: const EdgeInsets.symmetric(horizontal: 12).w,
  545. child: Row(
  546. crossAxisAlignment: CrossAxisAlignment.center,
  547. children: [
  548. Text(titleName,
  549. style: TextStyle(
  550. fontWeight: FontWeight.bold,
  551. fontSize: 17.sp,
  552. color: ColorName.primaryTextColor)),
  553. const Spacer(),
  554. Visibility(
  555. visible: onTap == null ? false : true,
  556. child: GestureDetector(
  557. onTap: onTap,
  558. child: Padding(
  559. padding: const EdgeInsets.symmetric(vertical: 6).w,
  560. child: Row(
  561. crossAxisAlignment: CrossAxisAlignment.center,
  562. children: [
  563. Text(
  564. StringName.homeTalkSeeAll.tr,
  565. style: TextStyle(
  566. fontSize: 13.sp, color: ColorName.tertiaryTextColor),
  567. ),
  568. Container(
  569. margin: const EdgeInsets.only(bottom: 1),
  570. width: 16.w,
  571. height: 16.w,
  572. child: Assets.images.iconHomeTalkArrow.image()),
  573. ],
  574. ),
  575. ),
  576. ),
  577. )
  578. ],
  579. ),
  580. );
  581. }
  582. void showUnfinishedRecordPopup() {
  583. SmartDialog.showAttach(
  584. targetContext: controller.todoTargetContext,
  585. alignment: Alignment.bottomRight,
  586. animationType: SmartAnimationType.fade,
  587. clickMaskDismiss: true,
  588. maskColor: Colors.transparent,
  589. bindPage: true,
  590. builder: (_) => Padding(
  591. padding: const EdgeInsets.only(top: 7).h,
  592. child: Stack(
  593. alignment: Alignment.topCenter,
  594. children: [
  595. SizedBox(
  596. width: 146.w,
  597. height: 66.w,
  598. child: Assets.images.bgAudioTodoPopup.image()),
  599. Container(
  600. alignment: Alignment.center,
  601. padding: const EdgeInsets.only(top: 17).w,
  602. width: 146.w,
  603. child: Text(StringName.homePopupTipsTxt.tr,
  604. style: TextStyle(fontSize: 14.sp, color: Colors.white)))
  605. ],
  606. ),
  607. ),
  608. );
  609. }
  610. void showRenameTalkDialog(TalkBean item) {
  611. reNameDialog(StringName.talkRenameTitle.tr, item.title.value,
  612. hintTxt: StringName.talkRenameTitleHint.tr,
  613. maxLength: 15, returnBuilder: (newName) {
  614. controller.requestName(newName, item);
  615. });
  616. }
  617. void showDeleteTalkDialog(TalkBean item) {
  618. talkDeleteDialog(item.id, item.title.value, returnBuilder: () {
  619. controller.requestDelete(item);
  620. });
  621. }
  622. Widget buildTalkTodoContent() {
  623. return Obx(() {
  624. if (controller.agendaList.isEmpty) {
  625. return SliverToBoxAdapter(child: buildAgendaEmptyView(50.h));
  626. } else {
  627. return SliverList.builder(
  628. itemBuilder: _builderAgendaItem,
  629. itemCount: controller.agendaList.length >= 10
  630. ? 10
  631. : controller.agendaList.length);
  632. }
  633. });
  634. }
  635. }
  636. Widget buildAgendaEmptyView(double top, {bool isVisible = true}) {
  637. return Visibility(
  638. visible: isVisible,
  639. child: Container(
  640. width: double.infinity,
  641. padding: EdgeInsets.symmetric(vertical: top),
  642. child: Column(
  643. children: [
  644. SizedBox(
  645. width: 100.w,
  646. height: 100.w,
  647. child: Assets.images.iconNoTask.image()),
  648. SizedBox(height: 4.h),
  649. Text(StringName.agendaNoData.tr,
  650. style: TextStyle(
  651. color: ColorName.secondaryTextColor, fontSize: 14.sp)),
  652. ],
  653. ),
  654. ),
  655. );
  656. }