Bläddra i källkod

add a check box delegate to build preview page's right bottom checkbox.

Caijinglong 6 år sedan
förälder
incheckning
c766bf6998

+ 3
- 2
example/lib/main.dart Visa fil

@@ -35,16 +35,17 @@ class _MyHomePageState extends State<MyHomePage> {
35 35
 
36 36
       /// The following are optional parameters.
37 37
       themeColor: Colors.green, // the title color and bottom color
38
-      padding: 5.0, // item padding
38
+      padding: 1.0, // item padding
39 39
       dividerColor: Colors.deepOrange, // divider color
40 40
       disableColor: Colors.grey.shade300, // the check box disable color
41 41
       itemRadio: 0.88, // the content item radio
42 42
       maxSelected: 8, // max picker image count
43
-      provider: CNProvider(), // i18n provider ,default is chinese. , you can custom I18nProvider or use ENProvider()
43
+      provider: I18nProvider.chinese, // i18n provider ,default is chinese. , you can custom I18nProvider or use ENProvider()
44 44
       rowCount: 5,  // item row count
45 45
       textColor: Colors.white, // text color
46 46
       thumbSize: 150, // preview thumb size , default is 64
47 47
       sortDelegate: SortDelegate.common, // default is common ,or you make custom delegate to sort your gallery
48
+      checkBoxBuilderDelegate: DefaultCheckBoxBuilderDelegate(), // default is DefaultCheckBoxBuilderDelegate ,or you make custom delegate to create checkbox
48 49
     );
49 50
 
50 51
     if (imgList == null) {

+ 5
- 0
lib/photo.dart Visa fil

@@ -10,10 +10,12 @@ import 'package:photo/src/provider/i18n_provider.dart';
10 10
 import 'package:photo/src/ui/dialog/not_permission_dialog.dart';
11 11
 import 'package:photo/src/ui/photo_app.dart';
12 12
 import 'package:photo/src/delegate/sort_delegate.dart';
13
+import 'package:photo/src/delegate/checkbox_builder_delegate.dart';
13 14
 
14 15
 export 'package:photo/src/provider/i18n_provider.dart'
15 16
     show I18NCustomProvider, I18nProvider, CNProvider, ENProvider;
16 17
 export 'package:photo/src/delegate/sort_delegate.dart';
18
+export 'package:photo/src/delegate/checkbox_builder_delegate.dart';
17 19
 
18 20
 class PhotoPicker {
19 21
   static PhotoPicker _instance;
@@ -58,6 +60,7 @@ class PhotoPicker {
58 60
     int thumbSize = 64,
59 61
     I18nProvider provider = I18nProvider.chinese,
60 62
     SortDelegate sortDelegate,
63
+    CheckBoxBuilderDelegate checkBoxBuilderDelegate,
61 64
   }) {
62 65
     assert(provider != null, "provider must be not null");
63 66
     assert(context != null, "context must be not null");
@@ -68,6 +71,7 @@ class PhotoPicker {
68 71
     textColor ??= Colors.white;
69 72
 
70 73
     sortDelegate ??= SortDelegate.common;
74
+    checkBoxBuilderDelegate ??= DefaultCheckBoxBuilderDelegate();
71 75
 
72 76
     var options = Options(
73 77
       rowCount: rowCount,
@@ -80,6 +84,7 @@ class PhotoPicker {
80 84
       themeColor: themeColor,
81 85
       thumbSize: thumbSize,
82 86
       sortDelegate: sortDelegate,
87
+      checkBoxBuilderDelegate: checkBoxBuilderDelegate,
83 88
     );
84 89
 
85 90
     return PhotoPicker()._pickImage(

+ 75
- 0
lib/src/delegate/checkbox_builder_delegate.dart Visa fil

@@ -0,0 +1,75 @@
1
+import 'package:flutter/material.dart' hide CheckboxListTile;
2
+import 'package:photo/src/entity/options.dart';
3
+import 'package:photo/src/provider/i18n_provider.dart';
4
+import 'package:photo/src/ui/widget/check_tile_copy.dart';
5
+
6
+abstract class CheckBoxBuilderDelegate {
7
+  Widget buildCheckBox(
8
+    BuildContext context,
9
+    bool checked,
10
+    int index,
11
+    Options options,
12
+    I18nProvider i18nProvider,
13
+  );
14
+}
15
+
16
+class DefaultCheckBoxBuilderDelegate extends CheckBoxBuilderDelegate {
17
+  Color activeColor;
18
+
19
+  DefaultCheckBoxBuilderDelegate({this.activeColor = Colors.white});
20
+
21
+  @override
22
+  Widget buildCheckBox(
23
+    BuildContext context,
24
+    bool checked,
25
+    int index,
26
+    Options options,
27
+    I18nProvider i18nProvider,
28
+  ) {
29
+    return CheckboxListTile(
30
+      value: checked,
31
+      onChanged: (bool check) {},
32
+      activeColor: activeColor,
33
+      title: Text(
34
+        i18nProvider.getSelectedOptionsText(options),
35
+        textAlign: TextAlign.end,
36
+        style: TextStyle(color: options.textColor),
37
+      ),
38
+    );
39
+  }
40
+}
41
+
42
+class RadioCheckBoxBuilderDelegate extends CheckBoxBuilderDelegate {
43
+  Color activeColor;
44
+  Color unselectedColor;
45
+
46
+  RadioCheckBoxBuilderDelegate({
47
+    this.activeColor = Colors.white,
48
+    this.unselectedColor = Colors.white,
49
+  });
50
+
51
+  @override
52
+  Widget buildCheckBox(
53
+    BuildContext context,
54
+    bool checked,
55
+    int index,
56
+    Options options,
57
+    I18nProvider i18nProvider,
58
+  ) {
59
+    return Theme(
60
+      data: Theme.of(context).copyWith(unselectedWidgetColor: unselectedColor),
61
+      child: RadioListTile<bool>(
62
+        value: true,
63
+        onChanged: (bool check) {},
64
+        activeColor: activeColor,
65
+        title: Text(
66
+          i18nProvider.getSelectedOptionsText(options),
67
+          textAlign: TextAlign.end,
68
+          style: TextStyle(color: options.textColor, fontSize: 14.0),
69
+        ),
70
+        groupValue: checked,
71
+        controlAffinity: ListTileControlAffinity.trailing,
72
+      ),
73
+    );
74
+  }
75
+}

+ 4
- 0
lib/src/entity/options.dart Visa fil

@@ -1,4 +1,5 @@
1 1
 import 'package:flutter/material.dart';
2
+import 'package:photo/src/delegate/checkbox_builder_delegate.dart';
2 3
 import 'package:photo/src/delegate/sort_delegate.dart';
3 4
 
4 5
 class Options {
@@ -22,6 +23,8 @@ class Options {
22 23
 
23 24
   final SortDelegate sortDelegate;
24 25
 
26
+  final CheckBoxBuilderDelegate checkBoxBuilderDelegate;
27
+
25 28
   const Options({
26 29
     this.rowCount,
27 30
     this.maxSelected,
@@ -33,5 +36,6 @@ class Options {
33 36
     this.disableColor,
34 37
     this.thumbSize,
35 38
     this.sortDelegate,
39
+    this.checkBoxBuilderDelegate,
36 40
   });
37 41
 }

+ 41
- 19
lib/src/ui/page/photo_preview_page.dart Visa fil

@@ -116,15 +116,15 @@ class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
116 116
             StreamBuilder(
117 117
               stream: pageStream,
118 118
               builder: (ctx, s) => FlatButton(
119
-                splashColor: Colors.transparent,
120
-                onPressed: selectedList.length == 0 ? null : sure,
121
-                child: Text(
119
+                    splashColor: Colors.transparent,
120
+                    onPressed: selectedList.length == 0 ? null : sure,
121
+                    child: Text(
122 122
                       config.provider.getSureText(options, selectedList.length),
123 123
                       style: selectedList.length == 0
124 124
                           ? textStyle.copyWith(color: options.disableColor)
125 125
                           : textStyle,
126 126
                     ),
127
-              ),
127
+                  ),
128 128
             ),
129 129
           ],
130 130
         ),
@@ -168,21 +168,24 @@ class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
168 168
         builder: (ctx, snapshot) {
169 169
           var index = snapshot.data;
170 170
           var data = list[index];
171
-          return CheckboxListTile(
172
-            value: selectedList.contains(data),
173
-            onChanged: (bool check) {
174
-              if (changeProviderOnCheckChange) {
175
-                _onChangeProvider(check, index);
176
-              } else {
177
-                _onCheckInOnlyPreview(check, index);
178
-              }
179
-            },
180
-            activeColor: Color.lerp(textColor, themeColor, 0.6),
181
-            title: Text(
182
-              config.provider.getSelectedOptionsText(options),
183
-              textAlign: TextAlign.end,
184
-              style: TextStyle(color: options.textColor),
185
-            ),
171
+          var checked = selectedList.contains(data);
172
+          return Stack(
173
+            children: <Widget>[
174
+              IgnorePointer(
175
+                child: _buildCheckboxContent(checked, index),
176
+              ),
177
+              Positioned(
178
+                top: 0.0,
179
+                bottom: 0.0,
180
+                left: 0.0,
181
+                right: 0.0,
182
+                child: GestureDetector(
183
+                  onTap: () => _changeSelected(!checked, index),
184
+                  behavior: HitTestBehavior.translucent,
185
+                  child: Container(),
186
+                ),
187
+              ),
188
+            ],
186 189
           );
187 190
         },
188 191
         initialData: widget.initIndex,
@@ -191,6 +194,25 @@ class _PhotoPreviewPageState extends State<PhotoPreviewPage> {
191 194
     );
192 195
   }
193 196
 
197
+  Widget _buildCheckboxContent(bool checked, int index) {
198
+    return options.checkBoxBuilderDelegate.buildCheckBox(
199
+      context,
200
+      checked,
201
+      index,
202
+      options,
203
+      config.provider,
204
+    );
205
+  }
206
+
207
+  void _changeSelected(bool isChecked, int index) {
208
+    print("onTap out to change checkbox value = $isChecked, index = $index");
209
+    if (changeProviderOnCheckChange) {
210
+      _onChangeProvider(isChecked, index);
211
+    } else {
212
+      _onCheckInOnlyPreview(isChecked, index);
213
+    }
214
+  }
215
+
194 216
   /// 仅仅修改预览时的状态,在退出时,再更新provider的顺序,这里无论添加与否不修改顺序
195 217
   void _onCheckInOnlyPreview(bool check, int index) {
196 218
     var item = list[index];

+ 371
- 0
lib/src/ui/widget/check_box_copy.dart Visa fil

@@ -0,0 +1,371 @@
1
+import 'dart:math' as math;
2
+
3
+import 'package:flutter/material.dart' hide Checkbox;
4
+import 'package:flutter/rendering.dart';
5
+import 'package:flutter/widgets.dart';
6
+
7
+/// A material design checkbox.
8
+///
9
+/// The checkbox itself does not maintain any state. Instead, when the state of
10
+/// the checkbox changes, the widget calls the [onChanged] callback. Most
11
+/// widgets that use a checkbox will listen for the [onChanged] callback and
12
+/// rebuild the checkbox with a new [value] to update the visual appearance of
13
+/// the checkbox.
14
+///
15
+/// The checkbox can optionally display three values - true, false, and null -
16
+/// if [tristate] is true. When [value] is null a dash is displayed. By default
17
+/// [tristate] is false and the checkbox's [value] must be true or false.
18
+///
19
+/// Requires one of its ancestors to be a [Material] widget.
20
+///
21
+/// See also:
22
+///
23
+///  * [CheckboxListTile], which combines this widget with a [ListTile] so that
24
+///    you can give the checkbox a label.
25
+///  * [Switch], a widget with semantics similar to [Checkbox].
26
+///  * [Radio], for selecting among a set of explicit values.
27
+///  * [Slider], for selecting a value in a range.
28
+///  * <https://material.google.com/components/selection-controls.html#selection-controls-checkbox>
29
+///  * <https://material.google.com/components/lists-controls.html#lists-controls-types-of-list-controls>
30
+class Checkbox extends StatefulWidget {
31
+  /// Creates a material design checkbox.
32
+  ///
33
+  /// The checkbox itself does not maintain any state. Instead, when the state of
34
+  /// the checkbox changes, the widget calls the [onChanged] callback. Most
35
+  /// widgets that use a checkbox will listen for the [onChanged] callback and
36
+  /// rebuild the checkbox with a new [value] to update the visual appearance of
37
+  /// the checkbox.
38
+  ///
39
+  /// The following arguments are required:
40
+  ///
41
+  /// * [value], which determines whether the checkbox is checked. The [value]
42
+  ///   can only be null if [tristate] is true.
43
+  /// * [onChanged], which is called when the value of the checkbox should
44
+  ///   change. It can be set to null to disable the checkbox.
45
+  ///
46
+  /// The value of [tristate] must not be null.
47
+  const Checkbox({
48
+    Key key,
49
+    @required this.value,
50
+    this.tristate = false,
51
+    @required this.onChanged,
52
+    this.activeColor,
53
+    this.checkColor,
54
+    this.materialTapTargetSize,
55
+  }) : assert(tristate != null),
56
+        assert(tristate || value != null),
57
+        super(key: key);
58
+
59
+  /// Whether this checkbox is checked.
60
+  ///
61
+  /// This property must not be null.
62
+  final bool value;
63
+
64
+  /// Called when the value of the checkbox should change.
65
+  ///
66
+  /// The checkbox passes the new value to the callback but does not actually
67
+  /// change state until the parent widget rebuilds the checkbox with the new
68
+  /// value.
69
+  ///
70
+  /// If this callback is null, the checkbox will be displayed as disabled
71
+  /// and will not respond to input gestures.
72
+  ///
73
+  /// When the checkbox is tapped, if [tristate] is false (the default) then
74
+  /// the [onChanged] callback will be applied to `!value`. If [tristate] is
75
+  /// true this callback cycle from false to true to null.
76
+  ///
77
+  /// The callback provided to [onChanged] should update the state of the parent
78
+  /// [StatefulWidget] using the [State.setState] method, so that the parent
79
+  /// gets rebuilt; for example:
80
+  ///
81
+  /// ```dart
82
+  /// Checkbox(
83
+  ///   value: _throwShotAway,
84
+  ///   onChanged: (bool newValue) {
85
+  ///     setState(() {
86
+  ///       _throwShotAway = newValue;
87
+  ///     });
88
+  ///   },
89
+  /// )
90
+  /// ```
91
+  final ValueChanged<bool> onChanged;
92
+
93
+  /// The color to use when this checkbox is checked.
94
+  ///
95
+  /// Defaults to [ThemeData.toggleableActiveColor].
96
+  final Color activeColor;
97
+
98
+  final Color checkColor;
99
+
100
+  /// If true the checkbox's [value] can be true, false, or null.
101
+  ///
102
+  /// Checkbox displays a dash when its value is null.
103
+  ///
104
+  /// When a tri-state checkbox is tapped its [onChanged] callback will be
105
+  /// applied to true if the current value is null or false, false otherwise.
106
+  /// Typically tri-state checkboxes are disabled (the onChanged callback is
107
+  /// null) so they don't respond to taps.
108
+  ///
109
+  /// If tristate is false (the default), [value] must not be null.
110
+  final bool tristate;
111
+
112
+  /// Configures the minimum size of the tap target.
113
+  ///
114
+  /// Defaults to [ThemeData.materialTapTargetSize].
115
+  ///
116
+  /// See also:
117
+  ///
118
+  ///   * [MaterialTapTargetSize], for a description of how this affects tap targets.
119
+  final MaterialTapTargetSize materialTapTargetSize;
120
+
121
+  /// The width of a checkbox widget.
122
+  static const double width = 18.0;
123
+
124
+  @override
125
+  _CheckboxState createState() => _CheckboxState();
126
+}
127
+
128
+class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
129
+  @override
130
+  Widget build(BuildContext context) {
131
+    assert(debugCheckHasMaterial(context));
132
+    final ThemeData themeData = Theme.of(context);
133
+    Size size;
134
+    switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
135
+      case MaterialTapTargetSize.padded:
136
+        size = const Size(2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
137
+        break;
138
+      case MaterialTapTargetSize.shrinkWrap:
139
+        size = const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius);
140
+        break;
141
+    }
142
+    final BoxConstraints additionalConstraints = BoxConstraints.tight(size);
143
+    return _CheckboxRenderObjectWidget(
144
+      value: widget.value,
145
+      tristate: widget.tristate,
146
+      activeColor: widget.activeColor ?? themeData.toggleableActiveColor,
147
+      checkColor: widget.checkColor ?? themeData.primaryColor,
148
+      inactiveColor: widget.onChanged != null ? themeData.unselectedWidgetColor : themeData.disabledColor,
149
+      onChanged: widget.onChanged,
150
+      additionalConstraints: additionalConstraints,
151
+      vsync: this,
152
+    );
153
+  }
154
+}
155
+
156
+class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
157
+  const _CheckboxRenderObjectWidget({
158
+    Key key,
159
+    @required this.value,
160
+    @required this.tristate,
161
+    @required this.activeColor,
162
+    @required this.inactiveColor,
163
+    @required this.checkColor,
164
+    @required this.onChanged,
165
+    @required this.vsync,
166
+    @required this.additionalConstraints,
167
+  }) : assert(tristate != null),
168
+        assert(tristate || value != null),
169
+        assert(activeColor != null),
170
+        assert(inactiveColor != null),
171
+        assert(vsync != null),
172
+        super(key: key);
173
+
174
+  final bool value;
175
+  final bool tristate;
176
+  final Color activeColor;
177
+  final Color checkColor;
178
+  final Color inactiveColor;
179
+  final ValueChanged<bool> onChanged;
180
+  final TickerProvider vsync;
181
+  final BoxConstraints additionalConstraints;
182
+
183
+  @override
184
+  _RenderCheckbox createRenderObject(BuildContext context) => _RenderCheckbox(
185
+    value: value,
186
+    tristate: tristate,
187
+    activeColor: activeColor,
188
+    inactiveColor: inactiveColor,
189
+    onChanged: onChanged,
190
+    vsync: vsync,
191
+    additionalConstraints: additionalConstraints,
192
+  );
193
+
194
+  @override
195
+  void updateRenderObject(BuildContext context, _RenderCheckbox renderObject) {
196
+    renderObject
197
+      ..value = value
198
+      ..tristate = tristate
199
+      ..activeColor = activeColor
200
+      ..inactiveColor = inactiveColor
201
+      ..checkColor = checkColor
202
+      ..onChanged = onChanged
203
+      ..additionalConstraints = additionalConstraints
204
+      ..vsync = vsync;
205
+  }
206
+}
207
+
208
+const double _kEdgeSize = Checkbox.width;
209
+const Radius _kEdgeRadius = Radius.circular(1.0);
210
+const double _kStrokeWidth = 2.0;
211
+
212
+class _RenderCheckbox extends RenderToggleable {
213
+  _RenderCheckbox({
214
+    bool value,
215
+    bool tristate,
216
+    Color activeColor,
217
+    Color inactiveColor,
218
+    BoxConstraints additionalConstraints,
219
+    ValueChanged<bool> onChanged,
220
+    @required TickerProvider vsync,
221
+  }): _oldValue = value,
222
+        super(
223
+        value: value,
224
+        tristate: tristate,
225
+        activeColor: activeColor,
226
+        inactiveColor: inactiveColor,
227
+        onChanged: onChanged,
228
+        additionalConstraints: additionalConstraints,
229
+        vsync: vsync,
230
+      );
231
+
232
+  bool _oldValue;
233
+
234
+  Color checkColor;
235
+
236
+  @override
237
+  set value(bool newValue) {
238
+    if (newValue == value)
239
+      return;
240
+    _oldValue = value;
241
+    super.value = newValue;
242
+  }
243
+
244
+  @override
245
+  void describeSemanticsConfiguration(SemanticsConfiguration config) {
246
+    super.describeSemanticsConfiguration(config);
247
+    config.isChecked = value == true;
248
+  }
249
+
250
+  // The square outer bounds of the checkbox at t, with the specified origin.
251
+  // At t == 0.0, the outer rect's size is _kEdgeSize (Checkbox.width)
252
+  // At t == 0.5, .. is _kEdgeSize - _kStrokeWidth
253
+  // At t == 1.0, .. is _kEdgeSize
254
+  RRect _outerRectAt(Offset origin, double t) {
255
+    final double inset = 1.0 - (t - 0.5).abs() * 2.0;
256
+    final double size = _kEdgeSize - inset * _kStrokeWidth;
257
+    final Rect rect = Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
258
+    return RRect.fromRectAndRadius(rect, _kEdgeRadius);
259
+  }
260
+
261
+  // The checkbox's border color if value == false, or its fill color when
262
+  // value == true or null.
263
+  Color _colorAt(double t) {
264
+    // As t goes from 0.0 to 0.25, animate from the inactiveColor to activeColor.
265
+    return onChanged == null
266
+        ? inactiveColor
267
+        : (t >= 0.25 ? activeColor : Color.lerp(inactiveColor, activeColor, t * 4.0));
268
+  }
269
+
270
+  // White stroke used to paint the check and dash.
271
+  void _initStrokePaint(Paint paint) {
272
+    paint
273
+      ..color = checkColor
274
+      ..style = PaintingStyle.stroke
275
+      ..strokeWidth = _kStrokeWidth;
276
+  }
277
+
278
+  void _drawBorder(Canvas canvas, RRect outer, double t, Paint paint) {
279
+    assert(t >= 0.0 && t <= 0.5);
280
+    final double size = outer.width;
281
+    // As t goes from 0.0 to 1.0, gradually fill the outer RRect.
282
+    final RRect inner = outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
283
+    canvas.drawDRRect(outer, inner, paint);
284
+  }
285
+
286
+  void _drawCheck(Canvas canvas, Offset origin, double t, Paint paint) {
287
+    assert(t >= 0.0 && t <= 1.0);
288
+    // As t goes from 0.0 to 1.0, animate the two check mark strokes from the
289
+    // short side to the long side.
290
+    final Path path = Path();
291
+    const Offset start = Offset(_kEdgeSize * 0.15, _kEdgeSize * 0.45);
292
+    const Offset mid = Offset(_kEdgeSize * 0.4, _kEdgeSize * 0.7);
293
+    const Offset end = Offset(_kEdgeSize * 0.85, _kEdgeSize * 0.25);
294
+    if (t < 0.5) {
295
+      final double strokeT = t * 2.0;
296
+      final Offset drawMid = Offset.lerp(start, mid, strokeT);
297
+      path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
298
+      path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
299
+    } else {
300
+      final double strokeT = (t - 0.5) * 2.0;
301
+      final Offset drawEnd = Offset.lerp(mid, end, strokeT);
302
+      path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
303
+      path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
304
+      path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
305
+    }
306
+    canvas.drawPath(path, paint);
307
+  }
308
+
309
+  void _drawDash(Canvas canvas, Offset origin, double t, Paint paint) {
310
+    assert(t >= 0.0 && t <= 1.0);
311
+    // As t goes from 0.0 to 1.0, animate the horizontal line from the
312
+    // mid point outwards.
313
+    const Offset start = Offset(_kEdgeSize * 0.2, _kEdgeSize * 0.5);
314
+    const Offset mid = Offset(_kEdgeSize * 0.5, _kEdgeSize * 0.5);
315
+    const Offset end = Offset(_kEdgeSize * 0.8, _kEdgeSize * 0.5);
316
+    final Offset drawStart = Offset.lerp(start, mid, 1.0 - t);
317
+    final Offset drawEnd = Offset.lerp(mid, end, t);
318
+    canvas.drawLine(origin + drawStart, origin + drawEnd, paint);
319
+  }
320
+
321
+  @override
322
+  void paint(PaintingContext context, Offset offset) {
323
+    final Canvas canvas = context.canvas;
324
+    paintRadialReaction(canvas, offset, size.center(Offset.zero));
325
+
326
+    final Offset origin = offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0);
327
+    final AnimationStatus status = position.status;
328
+    final double tNormalized = status == AnimationStatus.forward || status == AnimationStatus.completed
329
+        ? position.value
330
+        : 1.0 - position.value;
331
+
332
+    // Four cases: false to null, false to true, null to false, true to false
333
+    if (_oldValue == false || value == false) {
334
+      final double t = value == false ? 1.0 - tNormalized : tNormalized;
335
+      final RRect outer = _outerRectAt(origin, t);
336
+      final Paint paint = Paint()..color = _colorAt(t);
337
+
338
+      if (t <= 0.5) {
339
+        _drawBorder(canvas, outer, t, paint);
340
+      } else {
341
+        canvas.drawRRect(outer, paint);
342
+
343
+        _initStrokePaint(paint);
344
+        final double tShrink = (t - 0.5) * 2.0;
345
+        if (_oldValue == null)
346
+          _drawDash(canvas, origin, tShrink, paint);
347
+        else
348
+          _drawCheck(canvas, origin, tShrink, paint);
349
+      }
350
+    } else { // Two cases: null to true, true to null
351
+      final RRect outer = _outerRectAt(origin, 1.0);
352
+      final Paint paint = Paint() ..color = _colorAt(1.0);
353
+      canvas.drawRRect(outer, paint);
354
+
355
+      _initStrokePaint(paint);
356
+      if (tNormalized <= 0.5) {
357
+        final double tShrink = 1.0 - tNormalized * 2.0;
358
+        if (_oldValue == true)
359
+          _drawCheck(canvas, origin, tShrink, paint);
360
+        else
361
+          _drawDash(canvas, origin, tShrink, paint);
362
+      } else {
363
+        final double tExpand = (tNormalized - 0.5) * 2.0;
364
+        if (value == true)
365
+          _drawCheck(canvas, origin, tExpand, paint);
366
+        else
367
+          _drawDash(canvas, origin, tExpand, paint);
368
+      }
369
+    }
370
+  }
371
+}

+ 208
- 0
lib/src/ui/widget/check_tile_copy.dart Visa fil

@@ -0,0 +1,208 @@
1
+// Copyright 2017 The Chromium Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+import 'package:flutter/material.dart' hide Checkbox,CheckboxListTile;
6
+
7
+import 'package:photo/src/ui/widget/check_box_copy.dart';
8
+
9
+/// A [ListTile] with a [Checkbox]. In other words, a checkbox with a label.
10
+///
11
+/// The entire list tile is interactive: tapping anywhere in the tile toggles
12
+/// the checkbox.
13
+///
14
+/// The [value], [onChanged], and [activeColor] properties of this widget are
15
+/// identical to the similarly-named properties on the [Checkbox] widget.
16
+///
17
+/// The [title], [subtitle], [isThreeLine], and [dense] properties are like
18
+/// those of the same name on [ListTile].
19
+///
20
+/// The [selected] property on this widget is similar to the [ListTile.selected]
21
+/// property, but the color used is that described by [activeColor], if any,
22
+/// defaulting to the accent color of the current [Theme]. No effort is made to
23
+/// coordinate the [selected] state and the [value] state; to have the list tile
24
+/// appear selected when the checkbox is checked, pass the same value to both.
25
+///
26
+/// The checkbox is shown on the right by default in left-to-right languages
27
+/// (i.e. the trailing edge). This can be changed using [controlAffinity]. The
28
+/// [secondary] widget is placed on the opposite side. This maps to the
29
+/// [ListTile.leading] and [ListTile.trailing] properties of [ListTile].
30
+///
31
+/// To show the [CheckboxListTile] as disabled, pass null as the [onChanged]
32
+/// callback.
33
+///
34
+/// ## Sample code
35
+///
36
+/// This widget shows a checkbox that, when checked, slows down all animations
37
+/// (including the animation of the checkbox itself getting checked!).
38
+///
39
+/// ```dart
40
+/// CheckboxListTile(
41
+///   title: const Text('Animate Slowly'),
42
+///   value: timeDilation != 1.0,
43
+///   onChanged: (bool value) {
44
+///     setState(() { timeDilation = value ? 20.0 : 1.0; });
45
+///   },
46
+///   secondary: const Icon(Icons.hourglass_empty),
47
+/// )
48
+/// ```
49
+///
50
+/// This sample requires that you also import 'package:flutter/scheduler.dart',
51
+/// so that you can reference [timeDilation].
52
+///
53
+/// See also:
54
+///
55
+///  * [ListTileTheme], which can be used to affect the style of list tiles,
56
+///    including checkbox list tiles.
57
+///  * [RadioListTile], a similar widget for radio buttons.
58
+///  * [SwitchListTile], a similar widget for switches.
59
+///  * [ListTile] and [Checkbox], the widgets from which this widget is made.
60
+class CheckboxListTile extends StatelessWidget {
61
+  /// Creates a combination of a list tile and a checkbox.
62
+  ///
63
+  /// The checkbox tile itself does not maintain any state. Instead, when the
64
+  /// state of the checkbox changes, the widget calls the [onChanged] callback.
65
+  /// Most widgets that use a checkbox will listen for the [onChanged] callback
66
+  /// and rebuild the checkbox tile with a new [value] to update the visual
67
+  /// appearance of the checkbox.
68
+  ///
69
+  /// The following arguments are required:
70
+  ///
71
+  /// * [value], which determines whether the checkbox is checked, and must not
72
+  ///   be null.
73
+  ///
74
+  /// * [onChanged], which is called when the value of the checkbox should
75
+  ///   change. It can be set to null to disable the checkbox.
76
+  const CheckboxListTile({
77
+    Key key,
78
+    @required this.value,
79
+    @required this.onChanged,
80
+    this.activeColor,
81
+    this.checkColor,
82
+    this.title,
83
+    this.subtitle,
84
+    this.isThreeLine = false,
85
+    this.dense,
86
+    this.secondary,
87
+    this.selected = false,
88
+    this.controlAffinity = ListTileControlAffinity.platform,
89
+  }) : assert(value != null),
90
+        assert(isThreeLine != null),
91
+        assert(!isThreeLine || subtitle != null),
92
+        assert(selected != null),
93
+        assert(controlAffinity != null),
94
+        super(key: key);
95
+
96
+  /// Whether this checkbox is checked.
97
+  ///
98
+  /// This property must not be null.
99
+  final bool value;
100
+
101
+  /// Called when the value of the checkbox should change.
102
+  ///
103
+  /// The checkbox passes the new value to the callback but does not actually
104
+  /// change state until the parent widget rebuilds the checkbox tile with the
105
+  /// new value.
106
+  ///
107
+  /// If null, the checkbox will be displayed as disabled.
108
+  ///
109
+  /// The callback provided to [onChanged] should update the state of the parent
110
+  /// [StatefulWidget] using the [State.setState] method, so that the parent
111
+  /// gets rebuilt; for example:
112
+  ///
113
+  /// ```dart
114
+  /// CheckboxListTile(
115
+  ///   value: _throwShotAway,
116
+  ///   onChanged: (bool newValue) {
117
+  ///     setState(() {
118
+  ///       _throwShotAway = newValue;
119
+  ///     });
120
+  ///   },
121
+  ///   title: Text('Throw away your shot'),
122
+  /// )
123
+  /// ```
124
+  final ValueChanged<bool> onChanged;
125
+
126
+  /// The color to use when this checkbox is checked.
127
+  ///
128
+  /// Defaults to accent color of the current [Theme].
129
+  final Color activeColor;
130
+
131
+  final Color checkColor;
132
+
133
+  /// The primary content of the list tile.
134
+  ///
135
+  /// Typically a [Text] widget.
136
+  final Widget title;
137
+
138
+  /// Additional content displayed below the title.
139
+  ///
140
+  /// Typically a [Text] widget.
141
+  final Widget subtitle;
142
+
143
+  /// A widget to display on the opposite side of the tile from the checkbox.
144
+  ///
145
+  /// Typically an [Icon] widget.
146
+  final Widget secondary;
147
+
148
+  /// Whether this list tile is intended to display three lines of text.
149
+  ///
150
+  /// If false, the list tile is treated as having one line if the subtitle is
151
+  /// null and treated as having two lines if the subtitle is non-null.
152
+  final bool isThreeLine;
153
+
154
+  /// Whether this list tile is part of a vertically dense list.
155
+  ///
156
+  /// If this property is null then its value is based on [ListTileTheme.dense].
157
+  final bool dense;
158
+
159
+  /// Whether to render icons and text in the [activeColor].
160
+  ///
161
+  /// No effort is made to automatically coordinate the [selected] state and the
162
+  /// [value] state. To have the list tile appear selected when the checkbox is
163
+  /// checked, pass the same value to both.
164
+  ///
165
+  /// Normally, this property is left to its default value, false.
166
+  final bool selected;
167
+
168
+  /// Where to place the control relative to the text.
169
+  final ListTileControlAffinity controlAffinity;
170
+
171
+  @override
172
+  Widget build(BuildContext context) {
173
+    final Widget control = Checkbox(
174
+      value: value,
175
+      onChanged: onChanged,
176
+      activeColor: activeColor,
177
+      materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
178
+    );
179
+    Widget leading, trailing;
180
+    switch (controlAffinity) {
181
+      case ListTileControlAffinity.leading:
182
+        leading = control;
183
+        trailing = secondary;
184
+        break;
185
+      case ListTileControlAffinity.trailing:
186
+      case ListTileControlAffinity.platform:
187
+        leading = secondary;
188
+        trailing = control;
189
+        break;
190
+    }
191
+    return MergeSemantics(
192
+      child: ListTileTheme.merge(
193
+        selectedColor: activeColor ?? Theme.of(context).accentColor,
194
+        child: ListTile(
195
+          leading: leading,
196
+          title: title,
197
+          subtitle: subtitle,
198
+          trailing: trailing,
199
+          isThreeLine: isThreeLine,
200
+          dense: dense,
201
+          enabled: onChanged != null,
202
+          onTap: onChanged != null ? () { onChanged(!value); } : null,
203
+          selected: selected,
204
+        ),
205
+      ),
206
+    );
207
+  }
208
+}