history_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. import 'dart:io';
  2. import 'package:auto_route/auto_route.dart';
  3. import 'package:eitc_erm_dental_flutter/app_router.gr.dart';
  4. import 'package:eitc_erm_dental_flutter/entity/history_item_info.dart';
  5. import 'package:eitc_erm_dental_flutter/funcs.dart';
  6. import 'package:eitc_erm_dental_flutter/pages/history/vm/history_view_model.dart';
  7. import 'package:eitc_erm_dental_flutter/pages/history/widget/history_item_view.dart';
  8. import 'package:eitc_erm_dental_flutter/widget/main_button.dart';
  9. import 'package:flutter/material.dart';
  10. import 'package:flutter_riverpod/flutter_riverpod.dart';
  11. import 'package:flutter_screenutil/flutter_screenutil.dart';
  12. ///历史记录页面
  13. @RoutePage(name: "historyRoute")
  14. class HistoryPage extends ConsumerStatefulWidget {
  15. const HistoryPage({super.key});
  16. @override
  17. ConsumerState createState() => _HistoryPageState();
  18. }
  19. class _HistoryPageState extends ConsumerState<HistoryPage> {
  20. //照片列表
  21. final List<HistoryItemInfo> _photoList = [];
  22. //视频列表
  23. final List<HistoryItemInfo> _videoList = [];
  24. @override
  25. void initState() {
  26. super.initState();
  27. }
  28. @override
  29. Widget build(BuildContext context) {
  30. return PopScope(
  31. canPop: false,
  32. onPopInvokedWithResult: _onPop,
  33. child: Scaffold(
  34. appBar: _getAppBar(context),
  35. body: Padding(
  36. padding: EdgeInsets.symmetric(horizontal: 16.w),
  37. child: Column(
  38. children: [
  39. _getSelectedCount(context),
  40. Expanded(child: _getListWidget(context)),
  41. _getBottomButtons(context),
  42. SizedBox(
  43. height: 5.h,
  44. )
  45. ],
  46. ),
  47. ),
  48. ));
  49. }
  50. ///pop拦截
  51. void _onPop(bool didPop, _) {
  52. if (didPop) {
  53. return;
  54. }
  55. if (ref.read(historySelectModeProvider)) {
  56. _setSelectMode(false);
  57. return;
  58. }
  59. Navigator.pop(context);
  60. }
  61. ///获取appbar
  62. AppBar _getAppBar(BuildContext context) {
  63. return AppBar(
  64. centerTitle: true,
  65. forceMaterialTransparency: true,
  66. title: Text(getS().history),
  67. leading: IconButton(
  68. onPressed: () {
  69. Navigator.pop(context);
  70. },
  71. icon: Icon(
  72. Platform.isIOS ? Icons.arrow_back_ios_new : Icons.arrow_back)),
  73. actions: [
  74. PopupMenuButton(
  75. icon: const Icon(Icons.menu),
  76. //弹出菜单背景色
  77. color: const Color(0xFF383838),
  78. //背景形状
  79. shape: RoundedRectangleBorder(
  80. borderRadius: BorderRadius.circular(8.r)),
  81. itemBuilder: (ctx) {
  82. return [
  83. PopupMenuItem(
  84. child: SizedBox(
  85. width: double.infinity,
  86. child: Text(
  87. getS().byTime,
  88. textAlign: TextAlign.center,
  89. style: const TextStyle(color: Colors.white),
  90. ),
  91. ),
  92. onTap: () => _updateListType(0),
  93. ),
  94. PopupMenuItem(
  95. child: SizedBox(
  96. width: double.infinity,
  97. child: Text(
  98. getS().byCategory,
  99. textAlign: TextAlign.center,
  100. style: const TextStyle(color: Colors.white),
  101. ),
  102. ),
  103. onTap: () => _updateListType(1),
  104. )
  105. ];
  106. }),
  107. ],
  108. );
  109. }
  110. ///更新列表类型
  111. void _updateListType(int type) {
  112. if (ref.read(historyViewTypeProvider) == type) {
  113. return;
  114. }
  115. ref.read(historyViewTypeProvider.notifier).setType(type);
  116. }
  117. ///获取已选择数量widget
  118. Widget _getSelectedCount(BuildContext context) {
  119. return Consumer(builder: (ctx, ref, _) {
  120. bool isSelectedMode = ref.watch(historySelectModeProvider);
  121. Color primaryColor = Theme.of(context).colorScheme.primary;
  122. //如果不用Visibility的方式,当进入选择模式时,GridView会滚到最上面
  123. return Visibility(
  124. visible: isSelectedMode,
  125. child: Row(
  126. children: [
  127. Consumer(builder: (_, tref, __) {
  128. int count = tref.watch(historySelectedCountProvider);
  129. return Text(getS().hasSelectCountItem(count));
  130. }),
  131. SizedBox(
  132. width: 6.w,
  133. ),
  134. ElevatedButton(
  135. onPressed: () => _setSelectMode(false),
  136. style: ButtonStyle(
  137. minimumSize: const WidgetStatePropertyAll(Size.zero),
  138. padding: WidgetStatePropertyAll(EdgeInsets.symmetric(
  139. horizontal: 15.w, vertical: 4.h)),
  140. elevation: const WidgetStatePropertyAll(0.0),
  141. backgroundColor:
  142. const WidgetStatePropertyAll(Color(0xFFCEDCF0))),
  143. child: Row(
  144. children: [
  145. Text(
  146. getS().cancel,
  147. style: TextStyle(color: primaryColor, fontSize: 12.sp),
  148. ),
  149. SizedBox(
  150. width: 5.w,
  151. ),
  152. Icon(Icons.close, size: 18.r, color: primaryColor)
  153. ],
  154. )),
  155. const Spacer(),
  156. SizedBox(
  157. width: 25.w,
  158. child: Consumer(builder: (_, cref, __) {
  159. int count = cref.watch(historySelectedCountProvider);
  160. return Checkbox(
  161. value: count ==
  162. (cref
  163. .read(historyListProvider)
  164. .value
  165. ?.getMergedList(false)
  166. .length ??
  167. 0),
  168. shape: const CircleBorder(),
  169. onChanged: (bo) => cref
  170. .read(historyListProvider.notifier)
  171. .selectAll(bo ?? false));
  172. })),
  173. Text(getS().selectAll),
  174. SizedBox(
  175. width: 5.w,
  176. ),
  177. ],
  178. ));
  179. });
  180. }
  181. Widget _getLoading() {
  182. return Center(
  183. child: Row(
  184. mainAxisSize: MainAxisSize.min,
  185. crossAxisAlignment: CrossAxisAlignment.center,
  186. children: [
  187. Icon(
  188. Icons.downloading,
  189. size: 35.r,
  190. ),
  191. SizedBox(
  192. width: 10.w,
  193. ),
  194. Text(
  195. getS().loadingHistories,
  196. )
  197. ],
  198. ),
  199. );
  200. }
  201. Widget _getEmpty() {
  202. return Center(
  203. child: Row(
  204. mainAxisSize: MainAxisSize.min,
  205. crossAxisAlignment: CrossAxisAlignment.center,
  206. children: [
  207. Icon(
  208. Icons.sentiment_dissatisfied,
  209. size: 35.r,
  210. ),
  211. SizedBox(
  212. width: 10.w,
  213. ),
  214. Text(
  215. getS().noHistories,
  216. )
  217. ],
  218. ),
  219. );
  220. }
  221. Widget _getListWidget(BuildContext context) {
  222. return Consumer(builder: (ctx, ref, _) {
  223. AsyncValue<HistoryListData> value = ref.watch(historyListProvider);
  224. int viewType = ref.watch(historyViewTypeProvider);
  225. return switch (value) {
  226. AsyncData<HistoryListData>(value: var data) => data.isEmpty
  227. ? _getEmpty()
  228. : (viewType == 0 ? _getTimeList(data) : _getTypeList(ctx, data)),
  229. _ => _getLoading()
  230. };
  231. });
  232. }
  233. ///获取按时间列表
  234. Widget _getTimeList(HistoryListData data) {
  235. return _getGridView(data.getMergedList(true));
  236. }
  237. ///获取按类型列表
  238. Widget _getTypeList(BuildContext context, HistoryListData data) {
  239. return SingleChildScrollView(
  240. child: Column(
  241. crossAxisAlignment: CrossAxisAlignment.start,
  242. children: [
  243. Text(
  244. getS().photo,
  245. style: Theme.of(context).textTheme.titleMedium,
  246. ),
  247. SizedBox(
  248. height: 10.h,
  249. ),
  250. _getGridView(data.photoList, shinkWarp: true, scrollable: false),
  251. SizedBox(
  252. height: 10.h,
  253. ),
  254. Text(
  255. getS().video,
  256. style: Theme.of(context).textTheme.titleMedium,
  257. ),
  258. SizedBox(
  259. height: 10.h,
  260. ),
  261. _getGridView(data.videoList, shinkWarp: true, scrollable: false)
  262. ],
  263. ),
  264. );
  265. }
  266. ///获取GridView
  267. Widget _getGridView(List<HistoryItemInfo> list,
  268. {bool shinkWarp = false, bool scrollable = true}) {
  269. return GridView.builder(
  270. shrinkWrap: shinkWarp,
  271. physics: scrollable
  272. ? const ClampingScrollPhysics()
  273. : const NeverScrollableScrollPhysics(),
  274. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  275. crossAxisCount: 2,
  276. childAspectRatio: 164.0 / 120.0,
  277. mainAxisSpacing: 8.r,
  278. crossAxisSpacing: 15.r),
  279. itemBuilder: (ctx, index) {
  280. HistoryItemInfo item = list[index];
  281. return GestureDetector(
  282. child: HistoryItemView(
  283. index: index,
  284. name: item.name,
  285. path: item.path,
  286. type: item.type,
  287. ),
  288. onTap: () => _onTap(ctx, item),
  289. onLongPress: () => _onLongPress(ctx, item),
  290. );
  291. },
  292. itemCount: list.length,
  293. );
  294. }
  295. ///点击
  296. void _onTap(BuildContext context, HistoryItemInfo info) async {
  297. if (ref.read(historySelectModeProvider)) {
  298. ref.read(historyListProvider.notifier).select(info, !info.isSelected);
  299. return;
  300. }
  301. await context.pushRoute(info.type == 0
  302. ? PhotoViewRoute(info: info)
  303. : VideoPlayerRoute(info: info));
  304. }
  305. ///长按
  306. void _onLongPress(BuildContext context, HistoryItemInfo info) {
  307. if (ref.read(historySelectModeProvider)) {
  308. return;
  309. }
  310. _setSelectMode(true);
  311. ref.read(historyListProvider.notifier).select(info, true);
  312. }
  313. ///设置选择模式
  314. void _setSelectMode(bool isSelectMode) {
  315. ref.read(historySelectModeProvider.notifier).setSelectMode(isSelectMode);
  316. }
  317. ///获取选择的条目
  318. List<HistoryItemInfo> _getSelectedItems() {
  319. return ref.read(historyListProvider.notifier).getSelectedItems();
  320. }
  321. ///获取底部按钮
  322. Widget _getBottomButtons(BuildContext context) {
  323. bool isSelectedMode = ref.watch(historySelectModeProvider);
  324. //如果不用Visibility的方式,当进入选择模式时,GridView会滚到最上面
  325. return Visibility(
  326. visible: isSelectedMode,
  327. child: Row(
  328. children: [
  329. Expanded(
  330. child: MainButton(
  331. text: getS().upload, onPressed: () => _onUpload(context)),
  332. ),
  333. SizedBox(
  334. width: 15.w,
  335. ),
  336. Expanded(
  337. child: MainButton(
  338. text: getS().delete,
  339. onPressed: () => _onDelete(context),
  340. isOutlined: true,
  341. )),
  342. ],
  343. ));
  344. }
  345. ///上传
  346. void _onUpload(BuildContext context) async {
  347. if (!await checkInternetWifi()) {
  348. return;
  349. }
  350. List<HistoryItemInfo> list = _getSelectedItems();
  351. if (list.isEmpty) {
  352. showToast(text: getS().pleaseSelectToUpload);
  353. return;
  354. }
  355. if (context.mounted) {
  356. context.pushRoute(
  357. UploadSelectClinicRoute(uploadList: list, isFromView: false));
  358. }
  359. }
  360. ///删除
  361. void _onDelete(BuildContext context) async {
  362. List<HistoryItemInfo> list = _getSelectedItems();
  363. if (list.isEmpty) {
  364. showToast(text: getS().pleaseSelectToDelete);
  365. return;
  366. }
  367. bool? bo = await showDeleteAlertDialog(
  368. context, getS().deleteMultiHint(list.length));
  369. if (bo == null || !bo) {
  370. return;
  371. }
  372. bool result = await ref.read(historyListProvider.notifier).delete(list);
  373. showToast(text: result ? getS().deleteSuccess : getS().deleteFailed);
  374. }
  375. }