Browse Source

大图页面的展示和基础框架

Caijinglong 6 years ago
parent
commit
97f0e46061

+ 4
- 4
lib/photo.dart View File

@@ -1,12 +1,12 @@
1 1
 library photo;
2 2
 
3
-import 'package:flutter/material.dart';
4 3
 import 'dart:async';
5 4
 
5
+import 'package:flutter/material.dart';
6 6
 import 'package:photo/src/entity/options.dart';
7 7
 import 'package:photo/src/provider/i18n_provider.dart';
8 8
 import 'package:photo/src/ui/dialog/not_permission_dialog.dart';
9
-import 'package:photo/src/ui/page/photo_main_page.dart';
9
+import 'package:photo/src/ui/photo_app.dart';
10 10
 import 'package:photo_manager/photo_manager.dart';
11 11
 
12 12
 /// A Calculator.
@@ -62,7 +62,7 @@ class PhotoPicker {
62 62
       rowCount: rowCount,
63 63
       dividerColor: dividerColor,
64 64
       maxSelected: maxSelected,
65
-      itemRadio:itemRadio,
65
+      itemRadio: itemRadio,
66 66
       padding: padding,
67 67
       disableColor: disableColor,
68 68
       textColor: textColor,
@@ -98,7 +98,7 @@ class PhotoPicker {
98 98
       BuildContext context, Options options, I18nProvider provider) {
99 99
     return Navigator.of(context).push(
100 100
       MaterialPageRoute(
101
-        builder: (ctx) => PhotoMainPage(
101
+        builder: (ctx) => PhotoApp(
102 102
               options: options,
103 103
               provider: provider,
104 104
             ),

+ 23
- 0
lib/src/provider/config_provider.dart View File

@@ -0,0 +1,23 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:photo/src/entity/options.dart';
3
+import 'package:photo/src/provider/i18n_provider.dart';
4
+
5
+class ConfigProvider extends InheritedWidget {
6
+  final Options options;
7
+  final I18nProvider provider;
8
+
9
+  ConfigProvider({
10
+    @required this.options,
11
+    @required this.provider,
12
+    @required Widget child,
13
+    Key key,
14
+  }) : super(key: key, child: child);
15
+
16
+  @override
17
+  bool updateShouldNotify(InheritedWidget oldWidget) {
18
+    return true;
19
+  }
20
+
21
+  static ConfigProvider of(BuildContext context) =>
22
+      context.inheritFromWidgetOfExactType(ConfigProvider);
23
+}

+ 52
- 22
lib/src/ui/page/photo_main_page.dart View File

@@ -3,17 +3,21 @@ import 'dart:typed_data';
3 3
 import 'package:flutter/material.dart';
4 4
 import 'package:photo/src/engine/lru_cache.dart';
5 5
 import 'package:photo/src/entity/options.dart';
6
+import 'package:photo/src/provider/config_provider.dart';
6 7
 import 'package:photo/src/provider/gallery_list_provider.dart';
7 8
 import 'package:photo/src/provider/i18n_provider.dart';
8 9
 import 'package:photo/src/provider/selected_provider.dart';
9 10
 import 'package:photo/src/ui/dialog/change_gallery_dialog.dart';
11
+import 'package:photo/src/ui/page/photo_preview_page.dart';
10 12
 import 'package:photo_manager/photo_manager.dart';
11 13
 
12 14
 class PhotoMainPage extends StatefulWidget {
13
-  final Options options;
14
-  final I18nProvider provider;
15
+  final ValueChanged<List<ImageEntity>> onClose;
15 16
 
16
-  const PhotoMainPage({Key key, this.options, this.provider}) : super(key: key);
17
+  const PhotoMainPage({
18
+    Key key,
19
+    this.onClose,
20
+  }) : super(key: key);
17 21
 
18 22
   @override
19 23
   _PhotoMainPageState createState() => _PhotoMainPageState();
@@ -21,9 +25,9 @@ class PhotoMainPage extends StatefulWidget {
21 25
 
22 26
 class _PhotoMainPageState extends State<PhotoMainPage>
23 27
     with SelectedProvider, GalleryListProvider {
24
-  Options get options => widget.options;
28
+  Options get options => ConfigProvider.of(context).options;
25 29
 
26
-  I18nProvider get i18nProvider => widget.provider;
30
+  I18nProvider get i18nProvider => ConfigProvider.of(context).provider;
27 31
 
28 32
   List<ImageEntity> list = [];
29 33
 
@@ -65,12 +69,16 @@ class _PhotoMainPageState extends State<PhotoMainPage>
65 69
       color: options.textColor,
66 70
       fontSize: 14.0,
67 71
     );
68
-    return WillPopScope(
72
+    return Theme(
73
+      data: Theme.of(context).copyWith(primaryColor: options.themeColor),
69 74
       child: DefaultTextStyle(
70 75
         style: textStyle,
71 76
         child: Scaffold(
72 77
           appBar: AppBar(
73
-            backgroundColor: options.themeColor,
78
+            leading: IconButton(
79
+              icon: Icon(Icons.close),
80
+              onPressed: _cancel,
81
+            ),
74 82
             title: Text(
75 83
               i18nProvider.getTitleText(options),
76 84
             ),
@@ -93,19 +101,20 @@ class _PhotoMainPageState extends State<PhotoMainPage>
93 101
             options: options,
94 102
             galleryName: currentPath.name,
95 103
             onGalleryChange: _onGalleryChange,
104
+            onTapPreview: _onTapPreview,
96 105
             selectedProvider: this,
97 106
             galleryListProvider: this,
98 107
           ),
99 108
         ),
100 109
       ),
101
-      onWillPop: () async {
102
-        selectedList.clear();
103
-        Navigator.of(context).pop(selectedList);
104
-        return false;
105
-      },
106 110
     );
107 111
   }
108 112
 
113
+  void _cancel() {
114
+    selectedList.clear();
115
+    widget.onClose(selectedList);
116
+  }
117
+
109 118
   @override
110 119
   bool isUpperLimit() {
111 120
     var result = selectedCount == options.maxSelected;
@@ -114,7 +123,7 @@ class _PhotoMainPageState extends State<PhotoMainPage>
114 123
   }
115 124
 
116 125
   void sure() {
117
-    Navigator.pop(context, selectedList);
126
+    widget.onClose?.call(selectedList);
118 127
   }
119 128
 
120 129
   void _showTip(String msg) {
@@ -141,7 +150,6 @@ class _PhotoMainPageState extends State<PhotoMainPage>
141 150
     var imageList = await currentPath.imageList;
142 151
     this.list.clear();
143 152
     this.list.addAll(imageList);
144
-    print(list);
145 153
     setState(() {});
146 154
   }
147 155
 
@@ -258,6 +266,21 @@ class _PhotoMainPageState extends State<PhotoMainPage>
258 266
       setState(() {});
259 267
     });
260 268
   }
269
+
270
+  void _onTapPreview() {
271
+    Navigator.of(context).push(
272
+      MaterialPageRoute(
273
+        builder: (ctx) => ConfigProvider(
274
+              provider: ConfigProvider.of(context).provider,
275
+              options: options,
276
+              child: PhotoPreviewPage(
277
+                selectedProvider: this,
278
+                list: List.of(selectedList),
279
+              ),
280
+            ),
281
+      ),
282
+    );
283
+  }
261 284
 }
262 285
 
263 286
 class _BottomWidget extends StatefulWidget {
@@ -272,6 +295,7 @@ class _BottomWidget extends StatefulWidget {
272 295
   final String galleryName;
273 296
 
274 297
   final GalleryListProvider galleryListProvider;
298
+  final VoidCallback onTapPreview;
275 299
 
276 300
   const _BottomWidget({
277 301
     Key key,
@@ -281,6 +305,7 @@ class _BottomWidget extends StatefulWidget {
281 305
     this.selectedProvider,
282 306
     this.galleryName = "",
283 307
     this.galleryListProvider,
308
+    this.onTapPreview,
284 309
   }) : super(key: key);
285 310
 
286 311
   @override
@@ -295,12 +320,12 @@ class __BottomWidgetState extends State<_BottomWidget> {
295 320
   @override
296 321
   Widget build(BuildContext context) {
297 322
     var textStyle = TextStyle(fontSize: 14.0, color: options.textColor);
298
-    print(MediaQuery.of(context).padding.bottom);
299 323
     const textPadding = const EdgeInsets.symmetric(horizontal: 16.0);
300 324
     return Container(
301 325
       color: options.themeColor,
302 326
       child: SafeArea(
303 327
         bottom: true,
328
+        top: false,
304 329
         child: Container(
305 330
           height: 44.0,
306 331
           child: Row(
@@ -321,14 +346,19 @@ class __BottomWidgetState extends State<_BottomWidget> {
321 346
               Expanded(
322 347
                 child: Container(),
323 348
               ),
324
-              Container(
325
-                height: 44.0,
326
-                alignment: Alignment.center,
327
-                child: Text(
328
-                  i18nProvider.getPreviewText(options, widget.selectedProvider),
329
-                  style: textStyle,
349
+              GestureDetector(
350
+                behavior: HitTestBehavior.translucent,
351
+                onTap: widget.onTapPreview,
352
+                child: Container(
353
+                  height: 44.0,
354
+                  alignment: Alignment.center,
355
+                  child: Text(
356
+                    i18nProvider.getPreviewText(
357
+                        options, widget.selectedProvider),
358
+                    style: textStyle,
359
+                  ),
360
+                  padding: textPadding,
330 361
                 ),
331
-                padding: textPadding,
332 362
               ),
333 363
             ],
334 364
           ),

+ 123
- 0
lib/src/ui/page/photo_preview_page.dart View File

@@ -0,0 +1,123 @@
1
+import 'dart:async';
2
+import 'dart:io';
3
+
4
+import 'package:flutter/material.dart';
5
+import 'package:photo/src/entity/options.dart';
6
+import 'package:photo/src/provider/config_provider.dart';
7
+import 'package:photo/src/provider/selected_provider.dart';
8
+import 'package:photo_manager/photo_manager.dart';
9
+
10
+class PhotoPreviewPage extends StatefulWidget {
11
+  final SelectedProvider selectedProvider;
12
+
13
+  final List<ImageEntity> list;
14
+
15
+  const PhotoPreviewPage({Key key, this.selectedProvider, this.list})
16
+      : super(key: key);
17
+
18
+  @override
19
+  _PhotoPreviewPageState createState() => _PhotoPreviewPageState();
20
+}
21
+
22
+class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
23
+  ConfigProvider get config => ConfigProvider.of(context);
24
+
25
+  Options get options => config.options;
26
+
27
+  Color get themeColor => options.themeColor;
28
+
29
+  Color get textColor => options.textColor;
30
+
31
+  List<ImageEntity> get list => widget.list;
32
+
33
+  StreamController<int> pageChangeController = StreamController.broadcast();
34
+
35
+  @override
36
+  void initState() {
37
+    super.initState();
38
+    pageChangeController.add(0);
39
+  }
40
+
41
+  @override
42
+  void dispose() {
43
+    pageChangeController.close();
44
+    super.dispose();
45
+  }
46
+
47
+  @override
48
+  Widget build(BuildContext context) {
49
+    return Theme(
50
+      data: Theme.of(context).copyWith(primaryColor: options.themeColor),
51
+      child: Scaffold(
52
+        appBar: AppBar(
53
+          backgroundColor: config.options.themeColor,
54
+          title: StreamBuilder(
55
+            stream: pageChangeController.stream,
56
+            initialData: 0,
57
+            builder: (ctx, snap) => Text(
58
+                  "${snap.data + 1}/${widget.list.length}",
59
+                ),
60
+          ),
61
+        ),
62
+        body: PageView.builder(
63
+          itemBuilder: _buildItem,
64
+          itemCount: list.length,
65
+          onPageChanged: _onPageChanged,
66
+        ),
67
+        bottomNavigationBar: _buildBottom(),
68
+      ),
69
+    );
70
+  }
71
+
72
+  Widget _buildBottom() {
73
+    return Container(
74
+      height: 44.0,
75
+      color: themeColor,
76
+    );
77
+  }
78
+
79
+  Widget _buildItem(BuildContext context, int index) {
80
+    var data = list[index];
81
+    return BigPhotoImage(
82
+      imageEntity: data,
83
+    );
84
+  }
85
+
86
+  void _onPageChanged(int value) {
87
+    pageChangeController.add(value);
88
+  }
89
+}
90
+
91
+class BigPhotoImage extends StatefulWidget {
92
+  final ImageEntity imageEntity;
93
+
94
+  const BigPhotoImage({Key key, this.imageEntity}) : super(key: key);
95
+
96
+  @override
97
+  _BigPhotoImageState createState() => _BigPhotoImageState();
98
+}
99
+
100
+class _BigPhotoImageState extends State<BigPhotoImage>
101
+    with AutomaticKeepAliveClientMixin {
102
+  @override
103
+  Widget build(BuildContext context) {
104
+    return FutureBuilder(
105
+      future: widget.imageEntity.file,
106
+      builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
107
+        var file = snapshot.data;
108
+        if (snapshot.connectionState == ConnectionState.done && file != null) {
109
+          return Image.file(
110
+            file,
111
+            fit: BoxFit.contain,
112
+            width: double.infinity,
113
+            height: double.infinity,
114
+          );
115
+        }
116
+        return Container();
117
+      },
118
+    );
119
+  }
120
+
121
+  @override
122
+  bool get wantKeepAlive => true;
123
+}

+ 26
- 0
lib/src/ui/photo_app.dart View File

@@ -0,0 +1,26 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:photo/src/entity/options.dart';
3
+import 'package:photo/src/provider/config_provider.dart';
4
+import 'package:photo/src/provider/i18n_provider.dart';
5
+import 'package:photo/src/ui/page/photo_main_page.dart';
6
+import 'package:photo_manager/photo_manager.dart';
7
+
8
+class PhotoApp extends StatelessWidget {
9
+  final Options options;
10
+  final I18nProvider provider;
11
+
12
+  const PhotoApp({Key key, this.options, this.provider}) : super(key: key);
13
+
14
+  @override
15
+  Widget build(BuildContext context) {
16
+    return ConfigProvider(
17
+      provider: provider,
18
+      options: options,
19
+      child: PhotoMainPage(
20
+        onClose: (List<ImageEntity> value) {
21
+          Navigator.pop(context, value);
22
+        },
23
+      ),
24
+    );
25
+  }
26
+}