Açıklama Yok

photo_preview_page.dart 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import 'dart:async';
  2. import 'dart:typed_data';
  3. import 'package:flutter/material.dart';
  4. import 'package:photo/src/entity/options.dart';
  5. import 'package:photo/src/provider/config_provider.dart';
  6. import 'package:photo/src/provider/selected_provider.dart';
  7. import 'package:photo/src/ui/page/photo_main_page.dart';
  8. import 'package:photo_manager/photo_manager.dart';
  9. class PhotoPreviewPage extends StatefulWidget {
  10. final SelectedProvider selectedProvider;
  11. final List<AssetEntity> list;
  12. final int initIndex;
  13. /// 这个参数是控制在内部点击check后是否实时修改provider状态
  14. final bool changeProviderOnCheckChange;
  15. /// 这里封装了结果
  16. final PhotoPreviewResult result;
  17. const PhotoPreviewPage({
  18. Key key,
  19. @required this.selectedProvider,
  20. @required this.list,
  21. @required this.changeProviderOnCheckChange,
  22. @required this.result,
  23. this.initIndex = 0,
  24. }) : super(key: key);
  25. @override
  26. _PhotoPreviewPageState createState() => _PhotoPreviewPageState();
  27. }
  28. class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
  29. ConfigProvider get config => ConfigProvider.of(context);
  30. Options get options => config.options;
  31. Color get themeColor => options.themeColor;
  32. Color get textColor => options.textColor;
  33. SelectedProvider get selectedProvider => widget.selectedProvider;
  34. List<AssetEntity> get list => widget.list;
  35. StreamController<int> pageChangeController = StreamController.broadcast();
  36. Stream<int> get pageStream => pageChangeController.stream;
  37. bool get changeProviderOnCheckChange => widget.changeProviderOnCheckChange;
  38. PhotoPreviewResult get result => widget.result;
  39. /// 缩略图用的数据
  40. ///
  41. /// 用于与provider数据联动
  42. List<AssetEntity> get previewList {
  43. return selectedProvider.selectedList;
  44. }
  45. /// 选中的数据
  46. List<AssetEntity> _selectedList = [];
  47. List<AssetEntity> get selectedList {
  48. if (changeProviderOnCheckChange) {
  49. return previewList;
  50. }
  51. return _selectedList;
  52. }
  53. PageController pageController;
  54. @override
  55. void initState() {
  56. super.initState();
  57. pageChangeController.add(0);
  58. pageController = PageController(
  59. initialPage: widget.initIndex,
  60. );
  61. _selectedList.clear();
  62. _selectedList.addAll(selectedProvider.selectedList);
  63. result.previewSelectedList = _selectedList;
  64. }
  65. @override
  66. void dispose() {
  67. pageChangeController.close();
  68. super.dispose();
  69. }
  70. @override
  71. Widget build(BuildContext context) {
  72. var textStyle = TextStyle(
  73. color: options.textColor,
  74. fontSize: 14.0,
  75. );
  76. return Theme(
  77. data: Theme.of(context).copyWith(primaryColor: options.themeColor),
  78. child: Scaffold(
  79. appBar: AppBar(
  80. backgroundColor: config.options.themeColor,
  81. title: StreamBuilder(
  82. stream: pageStream,
  83. initialData: widget.initIndex,
  84. builder: (ctx, snap) => Text(
  85. "${snap.data + 1}/${widget.list.length}",
  86. ),
  87. ),
  88. actions: <Widget>[
  89. StreamBuilder(
  90. stream: pageStream,
  91. builder: (ctx, s) => FlatButton(
  92. splashColor: Colors.transparent,
  93. onPressed: selectedList.length == 0 ? null : sure,
  94. child: Text(
  95. config.provider.getSureText(options, selectedList.length),
  96. style: selectedList.length == 0
  97. ? textStyle.copyWith(color: options.disableColor)
  98. : textStyle,
  99. ),
  100. ),
  101. ),
  102. ],
  103. ),
  104. body: PageView.builder(
  105. controller: pageController,
  106. itemBuilder: _buildItem,
  107. itemCount: list.length,
  108. onPageChanged: _onPageChanged,
  109. ),
  110. bottomSheet: _buildThumb(),
  111. bottomNavigationBar: _buildBottom(),
  112. ),
  113. );
  114. }
  115. Widget _buildBottom() {
  116. return Container(
  117. color: themeColor,
  118. child: SafeArea(
  119. child: Container(
  120. height: 52.0,
  121. child: Row(
  122. children: <Widget>[
  123. Expanded(
  124. child: Container(),
  125. ),
  126. _buildCheckbox(),
  127. ],
  128. ),
  129. ),
  130. ),
  131. );
  132. }
  133. Container _buildCheckbox() {
  134. return Container(
  135. constraints: BoxConstraints(
  136. maxWidth: 150.0,
  137. ),
  138. child: StreamBuilder<int>(
  139. builder: (ctx, snapshot) {
  140. var index = snapshot.data;
  141. var data = list[index];
  142. var checked = selectedList.contains(data);
  143. return Stack(
  144. children: <Widget>[
  145. IgnorePointer(
  146. child: _buildCheckboxContent(checked, index),
  147. ),
  148. Positioned(
  149. top: 0.0,
  150. bottom: 0.0,
  151. left: 0.0,
  152. right: 0.0,
  153. child: GestureDetector(
  154. onTap: () => _changeSelected(!checked, index),
  155. behavior: HitTestBehavior.translucent,
  156. child: Container(),
  157. ),
  158. ),
  159. ],
  160. );
  161. },
  162. initialData: widget.initIndex,
  163. stream: pageStream,
  164. ),
  165. );
  166. }
  167. Widget _buildCheckboxContent(bool checked, int index) {
  168. return options.checkBoxBuilderDelegate.buildCheckBox(
  169. context,
  170. checked,
  171. index,
  172. options,
  173. config.provider,
  174. );
  175. }
  176. void _changeSelected(bool isChecked, int index) {
  177. if (changeProviderOnCheckChange) {
  178. _onChangeProvider(isChecked, index);
  179. } else {
  180. _onCheckInOnlyPreview(isChecked, index);
  181. }
  182. }
  183. /// 仅仅修改预览时的状态,在退出时,再更新provider的顺序,这里无论添加与否不修改顺序
  184. void _onCheckInOnlyPreview(bool check, int index) {
  185. var item = list[index];
  186. if (check) {
  187. selectedList.add(item);
  188. } else {
  189. selectedList.remove(item);
  190. }
  191. pageChangeController.add(index);
  192. }
  193. /// 直接修改预览状态,会直接移除item
  194. void _onChangeProvider(bool check, int index) {
  195. var item = list[index];
  196. if (check) {
  197. selectedProvider.addSelectEntity(item);
  198. } else {
  199. selectedProvider.removeSelectEntity(item);
  200. }
  201. pageChangeController.add(index);
  202. }
  203. Widget _buildItem(BuildContext context, int index) {
  204. var data = list[index];
  205. return BigPhotoImage(
  206. assetEntity: data,
  207. loadingWidget: _buildLoadingWidget(data),
  208. );
  209. }
  210. Widget _buildLoadingWidget(AssetEntity entity) {
  211. return options.loadingDelegate
  212. .buildBigImageLoading(context, entity, themeColor);
  213. }
  214. void _onPageChanged(int value) {
  215. pageChangeController.add(value);
  216. }
  217. Widget _buildThumb() {
  218. return StreamBuilder(
  219. builder: (ctx, snapshot) => Container(
  220. height: 80.0,
  221. child: ListView.builder(
  222. itemBuilder: _buildThumbItem,
  223. itemCount: previewList.length,
  224. scrollDirection: Axis.horizontal,
  225. ),
  226. ),
  227. stream: pageStream,
  228. );
  229. }
  230. Widget _buildThumbItem(BuildContext context, int index) {
  231. var item = previewList[index];
  232. return RepaintBoundary(
  233. child: GestureDetector(
  234. onTap: () => changeSelected(item, index),
  235. child: Container(
  236. width: 80.0,
  237. child: Stack(
  238. children: <Widget>[
  239. ImageItem(
  240. themeColor: themeColor,
  241. entity: item,
  242. size: options.thumbSize,
  243. loadingDelegate: options.loadingDelegate,
  244. ),
  245. IgnorePointer(
  246. child: StreamBuilder(
  247. stream: pageStream,
  248. builder: (BuildContext context, AsyncSnapshot snapshot) {
  249. if (selectedList.contains(item)) {
  250. return Container();
  251. }
  252. return Container(
  253. color: Colors.white.withOpacity(0.5),
  254. );
  255. },
  256. ),
  257. ),
  258. ],
  259. ),
  260. ),
  261. ),
  262. );
  263. }
  264. void changeSelected(AssetEntity entity, int index) {
  265. var itemIndex = list.indexOf(entity);
  266. if (itemIndex != -1) pageController.jumpToPage(itemIndex);
  267. }
  268. void sure() {
  269. Navigator.pop(context, selectedList);
  270. }
  271. }
  272. class BigPhotoImage extends StatefulWidget {
  273. final AssetEntity assetEntity;
  274. final Widget loadingWidget;
  275. const BigPhotoImage({
  276. Key key,
  277. this.assetEntity,
  278. this.loadingWidget,
  279. }) : super(key: key);
  280. @override
  281. _BigPhotoImageState createState() => _BigPhotoImageState();
  282. }
  283. class _BigPhotoImageState extends State<BigPhotoImage>
  284. with AutomaticKeepAliveClientMixin {
  285. Widget get loadingWidget {
  286. return widget.loadingWidget ?? Container();
  287. }
  288. @override
  289. Widget build(BuildContext context) {
  290. super.build(context);
  291. var width = MediaQuery.of(context).size.width;
  292. var height = MediaQuery.of(context).size.height;
  293. return FutureBuilder(
  294. future:
  295. widget.assetEntity.thumbDataWithSize(width.floor(), height.floor()),
  296. builder: (BuildContext context, AsyncSnapshot<Uint8List> snapshot) {
  297. var file = snapshot.data;
  298. if (snapshot.connectionState == ConnectionState.done && file != null) {
  299. print(file.length);
  300. return Image.memory(
  301. file,
  302. fit: BoxFit.contain,
  303. width: double.infinity,
  304. height: double.infinity,
  305. );
  306. }
  307. return loadingWidget;
  308. },
  309. );
  310. }
  311. @override
  312. bool get wantKeepAlive => true;
  313. }
  314. class PhotoPreviewResult {
  315. List<AssetEntity> previewSelectedList = [];
  316. }