Преглед изворни кода

Merge branch 'master' into fix-number

CaiJingLong пре 6 година
родитељ
комит
7a844b2091
No account linked to committer's email address

+ 13
- 0
.vscode/launch.json Прегледај датотеку

@@ -0,0 +1,13 @@
1
+{
2
+    // 使用 IntelliSense 了解相关属性。 
3
+    // 悬停以查看现有属性的描述。
4
+    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5
+    "version": "0.2.0",
6
+    "configurations": [
7
+        {
8
+            "name": "Flutter",
9
+            "request": "launch",
10
+            "type": "dart"
11
+        }
12
+    ]
13
+}

+ 1
- 1
README.md Прегледај датотеку

@@ -44,7 +44,7 @@ import 'package:photo_manager/photo_manager.dart';
44 44
 
45 45
 ```dart
46 46
 void _pickImage() async {
47
-    List<AssetEntity> imgList = await PhotoPicker.pickImage(
47
+    List<AssetEntity> imgList = await PhotoPicker.pickAsset(
48 48
       context: context,
49 49
       // BuildContext requied
50 50
 

+ 9
- 6
example/lib/main.dart Прегледај датотеку

@@ -31,9 +31,9 @@ class _MyHomePageState extends State<MyHomePage> with LoadingDelegate {
31 31
   String currentSelected = "";
32 32
 
33 33
   void _pickImage() async {
34
-    List<AssetEntity> imgList = await PhotoPicker.pickImage(
34
+    List<AssetEntity> imgList = await PhotoPicker.pickAsset(
35
+      // BuildContext required
35 36
       context: context,
36
-      // BuildContext requied
37 37
 
38 38
       /// The following are optional parameters.
39 39
       themeColor: Colors.green,
@@ -50,7 +50,7 @@ class _MyHomePageState extends State<MyHomePage> with LoadingDelegate {
50 50
       // max picker image count
51 51
       provider: I18nProvider.chinese,
52 52
       // i18n provider ,default is chinese. , you can custom I18nProvider or use ENProvider()
53
-      rowCount: 5,
53
+      rowCount: 3,
54 54
       // item row count
55 55
       textColor: Colors.white,
56 56
       // text color
@@ -61,10 +61,13 @@ class _MyHomePageState extends State<MyHomePage> with LoadingDelegate {
61 61
       checkBoxBuilderDelegate: DefaultCheckBoxBuilderDelegate(
62 62
         activeColor: Colors.white,
63 63
         unselectedColor: Colors.white,
64
-      ), // default is DefaultCheckBoxBuilderDelegate ,or you make custom delegate to create checkbox
64
+      ),
65
+      // default is DefaultCheckBoxBuilderDelegate ,or you make custom delegate to create checkbox
66
+
67
+      loadingDelegate: this,
68
+      // if you want to build custom loading widget,extends LoadingDelegate, [see example/lib/main.dart]
65 69
 
66
-      loadingDelegate:
67
-          this, // if you want to build custom loading widget,extends LoadingDelegate, [see example/lib/main.dart]
70
+      badgeDelegate: const DurationBadgeDelegate(),
68 71
     );
69 72
 
70 73
     if (imgList == null) {

+ 50
- 4
lib/photo.dart Прегледај датотеку

@@ -3,6 +3,7 @@ library photo;
3 3
 import 'dart:async';
4 4
 
5 5
 import 'package:flutter/material.dart';
6
+import 'package:photo/src/delegate/badge_delegate.dart';
6 7
 import 'package:photo/src/delegate/checkbox_builder_delegate.dart';
7 8
 import 'package:photo/src/delegate/loading_delegate.dart';
8 9
 import 'package:photo/src/delegate/sort_delegate.dart';
@@ -17,6 +18,7 @@ export 'package:photo/src/delegate/loading_delegate.dart';
17 18
 export 'package:photo/src/delegate/sort_delegate.dart';
18 19
 export 'package:photo/src/provider/i18n_provider.dart'
19 20
     show I18NCustomProvider, I18nProvider, CNProvider, ENProvider;
21
+export 'package:photo/src/delegate/badge_delegate.dart';
20 22
 
21 23
 class PhotoPicker {
22 24
   static PhotoPicker _instance;
@@ -30,25 +32,67 @@ class PhotoPicker {
30 32
 
31 33
   static const String rootRouteName = "photo_picker_image";
32 34
 
35
+  /// use new method [pickAsset]
36
+  ///
37
+  /// This method will be removed in the future.
38
+  @deprecated
39
+  static Future<List<AssetEntity>> pickImage({
40
+    @required BuildContext context,
41
+    int rowCount = 4,
42
+    int maxSelected = 9,
43
+    double padding = 0.5,
44
+    double itemRadio = 1.0,
45
+    Color themeColor,
46
+    Color dividerColor,
47
+    Color textColor,
48
+    Color disableColor,
49
+    int thumbSize = 64,
50
+    I18nProvider provider = I18nProvider.chinese,
51
+    SortDelegate sortDelegate,
52
+    CheckBoxBuilderDelegate checkBoxBuilderDelegate,
53
+    LoadingDelegate loadingDelegate,
54
+  }) {
55
+    return pickAsset(
56
+      context: context,
57
+      rowCount: rowCount,
58
+      maxSelected: maxSelected,
59
+      padding: padding,
60
+      itemRadio: itemRadio,
61
+      themeColor: themeColor,
62
+      dividerColor: dividerColor,
63
+      textColor: textColor,
64
+      disableColor: disableColor,
65
+      thumbSize: thumbSize,
66
+      provider: provider,
67
+      sortDelegate: sortDelegate,
68
+      checkBoxBuilderDelegate: checkBoxBuilderDelegate,
69
+      loadingDelegate: loadingDelegate,
70
+    );
71
+  }
72
+
33 73
   /// 没有授予权限的时候,会开启一个dialog去帮助用户去应用设置页面开启权限
34
-  /// 确定开启设置页面,取消关闭弹窗
74
+  /// 确定开启设置页面,取消关闭弹窗,无论选择什么,返回值都是null
35 75
   ///
36 76
   ///
37 77
   /// 当用户给予权限后
38 78
   ///
39 79
   ///   当用户确定时,返回一个图片[AssetEntity]列表
40 80
   ///
41
-  ///   当用户取消时
81
+  ///   当用户取消时返回一个空数组
82
+  ///
83
+  /// 关于参数可以查看readme文档介绍
42 84
   ///
43 85
   /// if user not grand permission, then return null and show a dialog to help user open setting.
44
-  /// sure is open setting cancel ,cancel to dismiss dialog
86
+  /// sure is open setting cancel ,cancel to dismiss dialog, return null
45 87
   ///
46 88
   /// when user give permission.
47 89
   ///
48 90
   ///   when user sure , return a [AssetEntity] of [List]
49 91
   ///
50 92
   ///   when user cancel selected,result is empty list
51
-  static Future<List<AssetEntity>> pickImage({
93
+  ///
94
+  /// params see readme.md
95
+  static Future<List<AssetEntity>> pickAsset({
52 96
     @required BuildContext context,
53 97
     int rowCount = 4,
54 98
     int maxSelected = 9,
@@ -63,6 +107,7 @@ class PhotoPicker {
63 107
     SortDelegate sortDelegate,
64 108
     CheckBoxBuilderDelegate checkBoxBuilderDelegate,
65 109
     LoadingDelegate loadingDelegate,
110
+    BadgeDelegate badgeDelegate = const DefaultBadgeDelegate(),
66 111
   }) {
67 112
     assert(provider != null, "provider must be not null");
68 113
     assert(context != null, "context must be not null");
@@ -90,6 +135,7 @@ class PhotoPicker {
90 135
       sortDelegate: sortDelegate,
91 136
       checkBoxBuilderDelegate: checkBoxBuilderDelegate,
92 137
       loadingDelegate: loadingDelegate,
138
+      badgeDelegate: badgeDelegate,
93 139
     );
94 140
 
95 141
     return PhotoPicker()._pickImage(

+ 83
- 0
lib/src/delegate/badge_delegate.dart Прегледај датотеку

@@ -0,0 +1,83 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:photo_manager/photo_manager.dart';
3
+
4
+abstract class BadgeDelegate {
5
+  const BadgeDelegate();
6
+
7
+  Widget buildBadge(BuildContext context, AssetType type, Duration duration);
8
+}
9
+
10
+class DefaultBadgeDelegate extends BadgeDelegate {
11
+  final AlignmentGeometry alignment;
12
+
13
+  const DefaultBadgeDelegate({
14
+    this.alignment = Alignment.topLeft,
15
+  });
16
+
17
+  @override
18
+  Widget buildBadge(BuildContext context, AssetType type, Duration duration) {
19
+    if (type == AssetType.video) {
20
+      return Padding(
21
+        padding: const EdgeInsets.all(2.0),
22
+        child: Align(
23
+          alignment: alignment,
24
+          child: Container(
25
+            decoration: BoxDecoration(
26
+              color: Theme.of(context).primaryColor,
27
+              borderRadius: BorderRadius.circular(3.0),
28
+            ),
29
+            child: Text(
30
+              "video",
31
+              style: const TextStyle(
32
+                fontSize: 12.0,
33
+                color: Colors.white,
34
+              ),
35
+            ),
36
+            padding: const EdgeInsets.all(4.0),
37
+          ),
38
+        ),
39
+      );
40
+    }
41
+
42
+    return Container();
43
+  }
44
+}
45
+
46
+class DurationBadgeDelegate extends BadgeDelegate {
47
+  final AlignmentGeometry alignment;
48
+  const DurationBadgeDelegate({this.alignment = Alignment.bottomRight});
49
+
50
+  @override
51
+  Widget buildBadge(BuildContext context, AssetType type, Duration duration) {
52
+    if (type == AssetType.video) {
53
+      var s = duration.inSeconds;
54
+      var m = duration.inMinutes;
55
+      var h = duration.inHours;
56
+
57
+      String text =
58
+          "$h:${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}";
59
+
60
+      return Padding(
61
+        padding: const EdgeInsets.all(2.0),
62
+        child: Align(
63
+          alignment: alignment,
64
+          child: Container(
65
+            decoration: BoxDecoration(
66
+              color: Theme.of(context).primaryColor.withOpacity(0.65),
67
+            ),
68
+            child: Text(
69
+              text,
70
+              style: const TextStyle(
71
+                fontSize: 12.0,
72
+                color: Colors.white,
73
+              ),
74
+            ),
75
+            padding: const EdgeInsets.all(4.0),
76
+          ),
77
+        ),
78
+      );
79
+    }
80
+
81
+    return Container();
82
+  }
83
+}

+ 4
- 0
lib/src/entity/options.dart Прегледај датотеку

@@ -1,4 +1,5 @@
1 1
 import 'package:flutter/material.dart';
2
+import 'package:photo/src/delegate/badge_delegate.dart';
2 3
 import 'package:photo/src/delegate/checkbox_builder_delegate.dart';
3 4
 import 'package:photo/src/delegate/loading_delegate.dart';
4 5
 import 'package:photo/src/delegate/sort_delegate.dart';
@@ -28,6 +29,8 @@ class Options {
28 29
 
29 30
   final LoadingDelegate loadingDelegate;
30 31
 
32
+  final BadgeDelegate badgeDelegate;
33
+
31 34
   const Options({
32 35
     this.rowCount,
33 36
     this.maxSelected,
@@ -41,5 +44,6 @@ class Options {
41 44
     this.sortDelegate,
42 45
     this.checkBoxBuilderDelegate,
43 46
     this.loadingDelegate,
47
+    this.badgeDelegate,
44 48
   });
45 49
 }

+ 55
- 19
lib/src/ui/page/photo_main_page.dart Прегледај датотеку

@@ -1,6 +1,7 @@
1 1
 import 'dart:typed_data';
2 2
 
3 3
 import 'package:flutter/material.dart';
4
+import 'package:photo/src/delegate/badge_delegate.dart';
4 5
 import 'package:photo/src/delegate/loading_delegate.dart';
5 6
 import 'package:photo/src/engine/lru_cache.dart';
6 7
 import 'package:photo/src/entity/options.dart';
@@ -190,19 +191,22 @@ class _PhotoMainPageState extends State<PhotoMainPage>
190 191
 
191 192
   Widget _buildItem(BuildContext context, int index) {
192 193
     var data = list[index];
193
-    return GestureDetector(
194
-      onTap: () => _onItemClick(data, index),
195
-      child: Stack(
196
-        children: <Widget>[
197
-          ImageItem(
198
-            entity: data,
199
-            themeColor: themeColor,
200
-            size: options.thumbSize,
201
-            loadingDelegate: options.loadingDelegate,
202
-          ),
203
-          _buildMask(containsEntity(data)),
204
-          _buildSelected(data),
205
-        ],
194
+    return RepaintBoundary(
195
+      child: GestureDetector(
196
+        onTap: () => _onItemClick(data, index),
197
+        child: Stack(
198
+          children: <Widget>[
199
+            ImageItem(
200
+              entity: data,
201
+              themeColor: themeColor,
202
+              size: options.thumbSize,
203
+              loadingDelegate: options.loadingDelegate,
204
+              badgeDelegate: options.badgeDelegate,
205
+            ),
206
+            _buildMask(containsEntity(data)),
207
+            _buildSelected(data),
208
+          ],
209
+        ),
206 210
       ),
207 211
     );
208 212
   }
@@ -491,19 +495,22 @@ class ImageItem extends StatelessWidget {
491 495
 
492 496
   final LoadingDelegate loadingDelegate;
493 497
 
498
+  final BadgeDelegate badgeDelegate;
499
+
494 500
   const ImageItem({
495 501
     Key key,
496 502
     this.entity,
497 503
     this.themeColor,
498 504
     this.size = 64,
499 505
     this.loadingDelegate,
506
+    this.badgeDelegate,
500 507
   }) : super(key: key);
501 508
 
502 509
   @override
503 510
   Widget build(BuildContext context) {
504 511
     var thumb = ImageLruCache.getData(entity, size);
505 512
     if (thumb != null) {
506
-      return _buildImageItem(thumb);
513
+      return _buildImageItem(context, thumb);
507 514
     }
508 515
 
509 516
     return FutureBuilder<Uint8List>(
@@ -513,22 +520,51 @@ class ImageItem extends StatelessWidget {
513 520
         if (snapshot.connectionState == ConnectionState.done &&
514 521
             futureData != null) {
515 522
           ImageLruCache.setData(entity, size, futureData);
516
-          return _buildImageItem(futureData);
523
+          return _buildImageItem(context, futureData);
517 524
         }
518 525
         return Center(
519
-          child:
520
-              loadingDelegate.buildPreviewLoading(context, entity, themeColor),
526
+          child: loadingDelegate.buildPreviewLoading(
527
+            context,
528
+            entity,
529
+            themeColor,
530
+          ),
521 531
         );
522 532
       },
523 533
     );
524 534
   }
525 535
 
526
-  Widget _buildImageItem(Uint8List data) {
527
-    return Image.memory(
536
+  Widget _buildImageItem(BuildContext context, Uint8List data) {
537
+    var image = Image.memory(
528 538
       data,
529 539
       width: double.infinity,
530 540
       height: double.infinity,
531 541
       fit: BoxFit.cover,
532 542
     );
543
+    // FutureBuilder()
544
+    var badge = FutureBuilder<Duration>(
545
+      future: entity.videoDuration,
546
+      builder: (ctx, snapshot) {
547
+        if (snapshot.hasData && snapshot != null) {
548
+          var buildBadge =
549
+              badgeDelegate?.buildBadge(context, entity.type, snapshot.data);
550
+          if (buildBadge == null) {
551
+            return Container();
552
+          } else {
553
+            return buildBadge;
554
+          }
555
+        } else {
556
+          return Container();
557
+        }
558
+      },
559
+    );
560
+
561
+    return Stack(
562
+      children: <Widget>[
563
+        image,
564
+        IgnorePointer(
565
+          child: badge,
566
+        ),
567
+      ],
568
+    );
533 569
   }
534 570
 }

+ 27
- 25
lib/src/ui/page/photo_preview_page.dart Прегледај датотеку

@@ -267,32 +267,34 @@ class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
267 267
 
268 268
   Widget _buildThumbItem(BuildContext context, int index) {
269 269
     var item = previewList[index];
270
-    return GestureDetector(
271
-      onTap: () => changeSelected(item, index),
272
-      child: Container(
273
-        width: 80.0,
274
-        child: Stack(
275
-          children: <Widget>[
276
-            ImageItem(
277
-              themeColor: themeColor,
278
-              entity: item,
279
-              size: options.thumbSize,
280
-              loadingDelegate: options.loadingDelegate,
281
-            ),
282
-            IgnorePointer(
283
-              child: StreamBuilder(
284
-                stream: pageStream,
285
-                builder: (BuildContext context, AsyncSnapshot snapshot) {
286
-                  if (selectedList.contains(item)) {
287
-                    return Container();
288
-                  }
289
-                  return Container(
290
-                    color: Colors.white.withOpacity(0.5),
291
-                  );
292
-                },
270
+    return RepaintBoundary(
271
+      child: GestureDetector(
272
+        onTap: () => changeSelected(item, index),
273
+        child: Container(
274
+          width: 80.0,
275
+          child: Stack(
276
+            children: <Widget>[
277
+              ImageItem(
278
+                themeColor: themeColor,
279
+                entity: item,
280
+                size: options.thumbSize,
281
+                loadingDelegate: options.loadingDelegate,
293 282
               ),
294
-            ),
295
-          ],
283
+              IgnorePointer(
284
+                child: StreamBuilder(
285
+                  stream: pageStream,
286
+                  builder: (BuildContext context, AsyncSnapshot snapshot) {
287
+                    if (selectedList.contains(item)) {
288
+                      return Container();
289
+                    }
290
+                    return Container(
291
+                      color: Colors.white.withOpacity(0.5),
292
+                    );
293
+                  },
294
+                ),
295
+              ),
296
+            ],
297
+          ),
296 298
         ),
297 299
       ),
298 300
     );