history_page.dart 11 KB

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