zefyr

asset_picker_quick_builder_delegate.dart 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. part of ct_assets_picker;
  2. class ExtendedAssetPickerQuickBuilderDelegate
  3. extends DefaultAssetPickerBuilderDelegate {
  4. ExtendedAssetPickerQuickBuilderDelegate({
  5. @required ExtendedAssetPickerProvider provider,
  6. int gridCount = 4,
  7. Color themeColor,
  8. AssetsPickerTextDelegate textDelegate,
  9. ThemeData pickerTheme,
  10. SpecialItemPosition specialItemPosition = SpecialItemPosition.none,
  11. WidgetBuilder specialItemBuilder,
  12. bool allowSpecialItemWhenEmpty = false,
  13. List<int> previewThumbSize,
  14. SpecialPickerType specialPickerType,
  15. }) : assert(
  16. provider != null,
  17. 'AssetPickerProvider must be provided and not null.',
  18. ),
  19. assert(
  20. pickerTheme == null || themeColor == null,
  21. 'Theme and theme color cannot be set at the same time.',
  22. ),
  23. super(
  24. provider: provider,
  25. gridCount: gridCount,
  26. themeColor: themeColor,
  27. textDelegate: textDelegate,
  28. pickerTheme: pickerTheme,
  29. specialItemPosition: specialItemPosition,
  30. specialItemBuilder: specialItemBuilder,
  31. allowSpecialItemWhenEmpty: allowSpecialItemWhenEmpty,
  32. previewThumbSize: previewThumbSize,
  33. specialPickerType: specialPickerType,
  34. ) {
  35. Constants.textDelegate = textDelegate ?? DefaultAssetsPickerTextDelegate();
  36. }
  37. @override
  38. bool get isAppleOS => true;
  39. @override
  40. double get appleOSBlurRadius => 0;
  41. /// The main grid view builder for assets.
  42. /// 主要的资源查看列表部件
  43. Widget assetsListBuilder(BuildContext context) {
  44. return ColoredBox(
  45. color: theme.canvasColor,
  46. child: Selector<AssetPickerProvider<AssetEntity, AssetPathEntity>,
  47. List<AssetEntity>>(
  48. selector: (BuildContext _,
  49. AssetPickerProvider<AssetEntity, AssetPathEntity> provider) =>
  50. provider.currentAssets,
  51. builder: (
  52. BuildContext _,
  53. List<AssetEntity> currentAssets,
  54. Widget __,
  55. ) {
  56. return ListView.builder(
  57. scrollDirection: Axis.horizontal,
  58. physics: ClampingScrollPhysics(),
  59. padding: EdgeInsets.zero,
  60. itemCount: assetsGridItemCount(_, currentAssets),
  61. itemBuilder: (BuildContext _, int index) {
  62. return assetGridItemBuilder(_, index, currentAssets);
  63. },
  64. );
  65. },
  66. ),
  67. );
  68. }
  69. @override
  70. Widget assetGridItemBuilder(
  71. BuildContext context, int index, List<AssetEntity> assets) {
  72. return Padding(
  73. padding: EdgeInsets.only(left: index != 0 ? 4 : 0),
  74. child: AspectRatio(
  75. aspectRatio: 0.5,
  76. child: super.assetGridItemBuilder(context, index, assets),
  77. ),
  78. );
  79. }
  80. @override
  81. int assetsGridItemCount(
  82. BuildContext context,
  83. List<AssetEntity> currentAssets,
  84. ) {
  85. final AssetPathEntity currentPathEntity =
  86. Provider.of<DefaultAssetPickerProvider>(
  87. context,
  88. listen: false,
  89. ).currentPathEntity;
  90. if (currentPathEntity == null &&
  91. specialItemPosition != SpecialItemPosition.none) {
  92. return 1;
  93. }
  94. /// Return actual length if current path is all.
  95. /// 如果当前目录是全部内容,则返回实际的内容数量。
  96. if (!(currentPathEntity?.isAll ?? false)) {
  97. return currentAssets?.length ?? 0;
  98. }
  99. int length;
  100. switch (specialItemPosition) {
  101. case SpecialItemPosition.none:
  102. length = currentAssets.length;
  103. break;
  104. case SpecialItemPosition.prepend:
  105. case SpecialItemPosition.append:
  106. length = currentAssets.length + 1;
  107. break;
  108. }
  109. return length;
  110. }
  111. /// It'll pop with [AssetPickerProvider.selectedAssets]
  112. /// when there're any assets chosen.
  113. /// 当有资源已选时,点击按钮将把已选资源通过路由返回。
  114. @override
  115. Widget confirmButton(BuildContext context) {
  116. final zefyrTheme = ZefyrTheme.of(context).toolbarTheme;
  117. final toolbar = ZefyrToolbar.of(context);
  118. return Consumer<DefaultAssetPickerProvider>(
  119. builder: (
  120. BuildContext _,
  121. DefaultAssetPickerProvider provider,
  122. Widget __,
  123. ) {
  124. return FlatButton(
  125. minWidth: provider.isSelectedNotEmpty ? 48.0 : 20.0,
  126. height: appBarItemHeight,
  127. shape: StadiumBorder(),
  128. padding: const EdgeInsets.symmetric(horizontal: 12.0),
  129. color: provider.isSelectedNotEmpty
  130. ? zefyrTheme.toggleColor
  131. : zefyrTheme.disabledIconColor,
  132. child: Text(
  133. provider.isSelectedNotEmpty && !isSingleAssetMode
  134. ? '${Constants.textDelegate.confirm}'
  135. '(${provider.selectedAssets.length}/${provider.maxAssets})'
  136. : Constants.textDelegate.confirm,
  137. style: TextStyle(
  138. color: Colors.white,
  139. fontSize: 16.0,
  140. fontWeight: FontWeight.normal,
  141. ),
  142. ),
  143. onPressed: () async {
  144. if (provider.isSelectedNotEmpty) {
  145. toolbar.editor.closeKeyboard(true);
  146. for (var asset in provider.selectedAssets) {
  147. var file = await asset.originFile;
  148. final image = await toolbar.editor.imageDelegate.picked(
  149. file, (provider as ExtendedAssetPickerProvider).fullImage);
  150. if (image != null) {
  151. toolbar.editor
  152. .formatSelection(NotusAttribute.embed.image(image));
  153. toolbar.editor.closeKeyboard();
  154. }
  155. }
  156. }
  157. },
  158. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  159. );
  160. },
  161. );
  162. }
  163. Widget fullImageButton(BuildContext context) {
  164. final zefyrTheme = ZefyrTheme.of(context).toolbarTheme;
  165. return Selector<DefaultAssetPickerProvider, bool>(
  166. selector: (BuildContext _, DefaultAssetPickerProvider provider) =>
  167. (provider as ExtendedAssetPickerProvider).fullImage,
  168. builder: (BuildContext _, bool fullImage, Widget __) {
  169. return GestureDetector(
  170. onTap: () {
  171. (provider as ExtendedAssetPickerProvider).fullImage = !fullImage;
  172. },
  173. child: Row(
  174. mainAxisSize: MainAxisSize.min,
  175. mainAxisAlignment: MainAxisAlignment.center,
  176. children: [
  177. Container(
  178. // margin: EdgeInsets.all(Screens.width / gridCount / 15.0),
  179. width: 18,
  180. height: 18,
  181. alignment: Alignment.center,
  182. child: AnimatedContainer(
  183. duration: switchingPathDuration,
  184. width: 18,
  185. height: 18,
  186. decoration: BoxDecoration(
  187. border: Border.all(
  188. color: fullImage
  189. ? zefyrTheme.toggleColor
  190. : zefyrTheme.disabledIconColor,
  191. width: 2.0),
  192. color: null,
  193. shape: BoxShape.circle,
  194. ),
  195. child: AnimatedSwitcher(
  196. duration: switchingPathDuration,
  197. reverseDuration: switchingPathDuration,
  198. child: fullImage
  199. ? Container(
  200. width: 10,
  201. height: 10,
  202. decoration: BoxDecoration(
  203. color: zefyrTheme.toggleColor,
  204. shape: BoxShape.circle,
  205. ),
  206. )
  207. : const SizedBox.shrink(),
  208. ),
  209. ),
  210. ),
  211. SizedBox(
  212. width: 8,
  213. ),
  214. Text(
  215. Constants.textDelegate.original,
  216. style: TextStyle(
  217. color: zefyrTheme.iconColor,
  218. fontSize: 16.0,
  219. ),
  220. ),
  221. ],
  222. ),
  223. );
  224. },
  225. );
  226. }
  227. /// Action bar widget aligned to bottom.
  228. /// 底部操作栏部件
  229. @override
  230. Widget bottomActionBar(BuildContext context) {
  231. final zefyrTheme = ZefyrTheme.of(context).toolbarTheme;
  232. return Container(
  233. height: kToolbarHeight + Screens.bottomSafeHeight,
  234. color: zefyrTheme.color,
  235. padding: EdgeInsets.symmetric(horizontal: 20).copyWith(
  236. bottom: Screens.bottomSafeHeight,
  237. ),
  238. child: Row(
  239. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  240. children: [
  241. fullImageButton(context),
  242. confirmButton(context),
  243. ],
  244. ),
  245. );
  246. }
  247. @override
  248. Widget selectIndicator(BuildContext context, AssetEntity asset) {
  249. final zefyrTheme = ZefyrTheme.of(context).toolbarTheme;
  250. if (isSingleAssetMode) {
  251. return Container();
  252. }
  253. return Selector<DefaultAssetPickerProvider, List<AssetEntity>>(
  254. selector: (BuildContext _, DefaultAssetPickerProvider provider) =>
  255. provider.selectedAssets,
  256. builder: (BuildContext _, List<AssetEntity> selectedAssets, Widget __) {
  257. final bool selected = selectedAssets.contains(asset);
  258. final double indicatorSize = Screens.width / gridCount / 3;
  259. return Positioned(
  260. top: 0.0,
  261. right: 0.0,
  262. child: GestureDetector(
  263. behavior: HitTestBehavior.opaque,
  264. onTap: () {
  265. if (selected) {
  266. provider.unSelectAsset(asset);
  267. } else {
  268. if (isSingleAssetMode) {
  269. provider.selectedAssets.clear();
  270. }
  271. provider.selectAsset(asset);
  272. }
  273. },
  274. child: Container(
  275. margin: EdgeInsets.all(Screens.width / gridCount / 15.0),
  276. width: indicatorSize,
  277. height: indicatorSize,
  278. alignment: AlignmentDirectional.topEnd,
  279. child: AnimatedContainer(
  280. duration: switchingPathDuration,
  281. width: indicatorSize / 1.5,
  282. height: indicatorSize / 1.5,
  283. decoration: BoxDecoration(
  284. border: !selected
  285. ? Border.all(color: Colors.white, width: 2.0)
  286. : null,
  287. color: selected ? zefyrTheme.toggleColor : null,
  288. shape: BoxShape.circle,
  289. ),
  290. child: AnimatedSwitcher(
  291. duration: switchingPathDuration,
  292. reverseDuration: switchingPathDuration,
  293. child: selected
  294. ? isSingleAssetMode
  295. ? const Icon(Icons.check, size: 18.0)
  296. : Text(
  297. '${selectedAssets.indexOf(asset) + 1}',
  298. style: TextStyle(
  299. color: selected
  300. ? Colors.white
  301. : null,
  302. fontSize: 14.0,
  303. fontWeight: isAppleOS
  304. ? FontWeight.w600
  305. : FontWeight.bold,
  306. ),
  307. )
  308. : const SizedBox.shrink(),
  309. ),
  310. ),
  311. ),
  312. ),
  313. );
  314. },
  315. );
  316. }
  317. @override
  318. Widget selectedBackdrop(BuildContext context, int index, AssetEntity asset) {
  319. return Selector<DefaultAssetPickerProvider, List<AssetEntity>>(
  320. selector: (BuildContext _, DefaultAssetPickerProvider provider) =>
  321. provider.selectedAssets,
  322. builder: (BuildContext _, List<AssetEntity> selectedAssets, Widget __) {
  323. final bool selected = selectedAssets.contains(asset);
  324. return Positioned.fill(
  325. child: GestureDetector(
  326. onTap: () async {
  327. if (isSingleAssetMode) {
  328. Navigator.of(context).pop(AssetPickerEntity(
  329. assets: [asset],
  330. isFullImage: (provider as ExtendedAssetPickerProvider).fullImage,
  331. ));
  332. return;
  333. }
  334. final List<AssetEntity> result =
  335. await ExtendedAssetPickerViewer.pushToViewer(
  336. context,
  337. currentIndex: index,
  338. previewAssets: provider.currentAssets,
  339. previewThumbSize: previewThumbSize,
  340. selectedAssets: provider.selectedAssets,
  341. selectorProvider: provider as ExtendedAssetPickerProvider,
  342. themeData: theme,
  343. );
  344. if (result != null) {
  345. Navigator.of(context).pop(AssetPickerEntity(
  346. assets: result,
  347. isFullImage: (provider as ExtendedAssetPickerProvider).fullImage,
  348. ));
  349. }
  350. },
  351. child: AnimatedContainer(
  352. duration: switchingPathDuration,
  353. color: selected
  354. ? Colors.black.withOpacity(0.45)
  355. : Colors.black.withOpacity(0.1),
  356. ),
  357. ), // 点击预览同目录下所有资源
  358. );
  359. },
  360. );
  361. }
  362. /// Yes, the build method.
  363. /// 没错,是它是它就是它,我们亲爱的 build 方法~
  364. @override
  365. Widget build(BuildContext context) {
  366. final zefyrTheme = ZefyrTheme.of(context).toolbarTheme;
  367. final toolbar = ZefyrToolbar.of(context);
  368. return AnnotatedRegion<SystemUiOverlayStyle>(
  369. value: overlayStyle,
  370. child: Theme(
  371. data: theme,
  372. child: ChangeNotifierProvider<
  373. AssetPickerProvider<AssetEntity, AssetPathEntity>>.value(
  374. value: provider,
  375. child: Material(
  376. color: theme.canvasColor,
  377. child: Container(
  378. child: Column(
  379. children: <Widget>[
  380. Expanded(
  381. child: Container(
  382. decoration: BoxDecoration(
  383. border: Border(
  384. top: BorderSide(color: theme.dividerColor, width: 1),
  385. bottom:
  386. BorderSide(color: theme.dividerColor, width: 1),
  387. ),
  388. ),
  389. child: Row(
  390. children: [
  391. Container(
  392. width: 58,
  393. child: Column(
  394. children: [
  395. Expanded(
  396. child: FlatButton(
  397. shape: RoundedRectangleBorder(),
  398. color: zefyrTheme.captionColor,
  399. padding: EdgeInsets.zero,
  400. onPressed: () {
  401. ExtendedAssetPicker.getImageFromCamera()
  402. .then((result) {
  403. if (result != null) {
  404. toolbar.closeOverlay();
  405. toolbar.editor.imageDelegate.picked(result, true);
  406. }
  407. });
  408. },
  409. child: Container(
  410. child: Column(
  411. mainAxisAlignment:
  412. MainAxisAlignment.center,
  413. children: [
  414. Icon(
  415. kDefaultButtonIcons[
  416. ZefyrToolbarAction
  417. .cameraImage],
  418. size: 24,
  419. color: Color(0xFFBFBFBF),
  420. ),
  421. Padding(
  422. padding: EdgeInsets.only(top: 6),
  423. child: Text(
  424. (Constants.textDelegate
  425. as ExtendedAssetsPickerTextDelegate)
  426. .camera,
  427. style: TextStyle(
  428. fontSize: 12,
  429. color: Color(0xFF8C8C8C),
  430. ),
  431. ),
  432. )
  433. ],
  434. ),
  435. )),
  436. ),
  437. Expanded(
  438. child: FlatButton(
  439. shape: RoundedRectangleBorder(),
  440. color: zefyrTheme.captionColor,
  441. padding: EdgeInsets.zero,
  442. onPressed: () {
  443. final editor =
  444. ZefyrToolbar.of(context).editor;
  445. // var _selection = toolbar.editor.selection;
  446. editor.closeKeyboard(true);
  447. ExtendedAssetPicker.pickAssets(context,
  448. requestType: RequestType.image)
  449. .then((result) async {
  450. if (result != null) {
  451. for (var asset in result.assets) {
  452. var file = await asset.originFile;
  453. final image = await editor
  454. .imageDelegate
  455. .picked(
  456. file, result.isFullImage);
  457. if (image != null) {
  458. editor.formatSelection(
  459. NotusAttribute.embed
  460. .image(image));
  461. editor.closeKeyboard();
  462. }
  463. }
  464. }
  465. });
  466. },
  467. child: Container(
  468. child: Column(
  469. mainAxisAlignment:
  470. MainAxisAlignment.center,
  471. children: [
  472. Icon(
  473. kDefaultButtonIcons[
  474. ZefyrToolbarAction
  475. .galleryImage],
  476. size: 24,
  477. color: Color(0xFFBFBFBF),
  478. ),
  479. Padding(
  480. padding: EdgeInsets.only(top: 6),
  481. child: Text(
  482. (Constants.textDelegate
  483. as ExtendedAssetsPickerTextDelegate)
  484. .album,
  485. style: TextStyle(
  486. fontSize: 12,
  487. color: Color(0xFF8C8C8C),
  488. ),
  489. ),
  490. )
  491. ],
  492. ),
  493. )),
  494. ),
  495. ],
  496. ),
  497. ),
  498. Expanded(
  499. child: Listener(
  500. behavior: HitTestBehavior.translucent,
  501. onPointerUp: (PointerUpEvent event) {
  502. toolbar.editor.keepOverlay = true;
  503. },
  504. child: assetsListBuilder(context),
  505. ),
  506. ),
  507. ],
  508. ),
  509. ),
  510. ),
  511. bottomActionBar(context),
  512. ],
  513. ),
  514. ),
  515. ),
  516. ),
  517. ),
  518. );
  519. }
  520. }