zefyr

rounded_check_box.dart 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. part of ct_assets_picker;
  2. class RoundedCheckbox extends StatefulWidget {
  3. const RoundedCheckbox({
  4. Key key,
  5. @required this.value,
  6. this.tristate = false,
  7. @required this.onChanged,
  8. this.activeColor,
  9. this.inactiveColor,
  10. this.checkColor,
  11. this.materialTapTargetSize,
  12. }) : assert(tristate != null),
  13. assert(tristate || value != null),
  14. super(key: key);
  15. final bool value;
  16. final ValueChanged<bool> onChanged;
  17. final Color activeColor;
  18. final Color inactiveColor;
  19. final Color checkColor;
  20. final bool tristate;
  21. final MaterialTapTargetSize materialTapTargetSize;
  22. static const double width = 18.0;
  23. @override
  24. _RoundedCheckboxState createState() => _RoundedCheckboxState();
  25. }
  26. class _RoundedCheckboxState extends State<RoundedCheckbox>
  27. with TickerProviderStateMixin {
  28. @override
  29. Widget build(BuildContext context) {
  30. assert(debugCheckHasMaterial(context));
  31. final ThemeData themeData = Theme.of(context);
  32. Size size;
  33. switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize) {
  34. case MaterialTapTargetSize.padded:
  35. size = const Size(
  36. 2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
  37. break;
  38. case MaterialTapTargetSize.shrinkWrap:
  39. size = const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius);
  40. break;
  41. }
  42. final BoxConstraints additionalConstraints = BoxConstraints.tight(size);
  43. return _CheckboxRenderObjectWidget(
  44. value: widget.value,
  45. tristate: widget.tristate,
  46. activeColor: widget.activeColor ?? themeData.toggleableActiveColor,
  47. checkColor: widget.checkColor ?? const Color(0xFFFFFFFF),
  48. inactiveColor: widget.inactiveColor ??
  49. (widget.onChanged != null
  50. ? themeData.unselectedWidgetColor
  51. : themeData.disabledColor),
  52. onChanged: widget.onChanged,
  53. additionalConstraints: additionalConstraints,
  54. vsync: this,
  55. );
  56. }
  57. }
  58. class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
  59. const _CheckboxRenderObjectWidget({
  60. Key key,
  61. @required this.value,
  62. @required this.tristate,
  63. @required this.activeColor,
  64. @required this.checkColor,
  65. @required this.inactiveColor,
  66. @required this.onChanged,
  67. @required this.vsync,
  68. @required this.additionalConstraints,
  69. }) : assert(tristate != null),
  70. assert(tristate || value != null),
  71. assert(activeColor != null),
  72. assert(inactiveColor != null),
  73. assert(vsync != null),
  74. super(key: key);
  75. final bool value;
  76. final bool tristate;
  77. final Color activeColor;
  78. final Color checkColor;
  79. final Color inactiveColor;
  80. final ValueChanged<bool> onChanged;
  81. final TickerProvider vsync;
  82. final BoxConstraints additionalConstraints;
  83. @override
  84. _RenderCheckbox createRenderObject(BuildContext context) => _RenderCheckbox(
  85. value: value,
  86. tristate: tristate,
  87. activeColor: activeColor,
  88. checkColor: checkColor,
  89. inactiveColor: inactiveColor,
  90. onChanged: onChanged,
  91. vsync: vsync,
  92. additionalConstraints: additionalConstraints,
  93. );
  94. @override
  95. void updateRenderObject(BuildContext context, _RenderCheckbox renderObject) {
  96. renderObject
  97. ..value = value
  98. ..tristate = tristate
  99. ..activeColor = activeColor
  100. ..checkColor = checkColor
  101. ..inactiveColor = inactiveColor
  102. ..onChanged = onChanged
  103. ..additionalConstraints = additionalConstraints
  104. ..vsync = vsync;
  105. }
  106. }
  107. const double _kEdgeSize = RoundedCheckbox.width;
  108. const Radius _kEdgeRadius = Radius.circular(123546);
  109. const double _kStrokeWidth = 2.0;
  110. class _RenderCheckbox extends RenderToggleable {
  111. _RenderCheckbox({
  112. bool value,
  113. bool tristate,
  114. Color activeColor,
  115. this.checkColor,
  116. Color inactiveColor,
  117. BoxConstraints additionalConstraints,
  118. ValueChanged<bool> onChanged,
  119. @required TickerProvider vsync,
  120. }) : _oldValue = value,
  121. super(
  122. value: value,
  123. tristate: tristate,
  124. activeColor: activeColor,
  125. inactiveColor: inactiveColor,
  126. onChanged: onChanged,
  127. additionalConstraints: additionalConstraints,
  128. vsync: vsync,
  129. );
  130. bool _oldValue;
  131. Color checkColor;
  132. @override
  133. set value(bool newValue) {
  134. if (newValue == value) {
  135. return;
  136. }
  137. _oldValue = value;
  138. super.value = newValue;
  139. }
  140. @override
  141. void describeSemanticsConfiguration(SemanticsConfiguration config) {
  142. super.describeSemanticsConfiguration(config);
  143. config.isChecked = value == true;
  144. }
  145. RRect _outerRectAt(Offset origin, double t) {
  146. final double inset = 1.0 - (t - 0.5).abs() * 2.0;
  147. final double size = _kEdgeSize - inset * _kStrokeWidth;
  148. final Rect rect =
  149. Rect.fromLTWH(origin.dx + inset, origin.dy + inset, size, size);
  150. return RRect.fromRectAndRadius(rect, _kEdgeRadius);
  151. }
  152. Color _colorAt(double t) {
  153. return onChanged == null
  154. ? inactiveColor
  155. : (t >= 0.25
  156. ? activeColor
  157. : Color.lerp(inactiveColor, activeColor, t * 4.0));
  158. }
  159. void _initStrokePaint(Paint paint) {
  160. paint
  161. ..color = checkColor
  162. ..style = PaintingStyle.stroke
  163. ..strokeWidth = _kStrokeWidth;
  164. }
  165. void _drawBorder(Canvas canvas, RRect outer, double t, Paint paint) {
  166. assert(t >= 0.0 && t <= 0.5);
  167. final double size = outer.width;
  168. final RRect inner =
  169. outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
  170. canvas.drawDRRect(outer, inner, paint);
  171. }
  172. void _drawCheck(Canvas canvas, Offset origin, double t, Paint paint) {
  173. assert(t >= 0.0 && t <= 1.0);
  174. final Path path = Path();
  175. const double size = _kEdgeSize - 2;
  176. const Offset start = Offset(size * 0.2, size * 0.55);
  177. const Offset mid = Offset(size * 0.45, size * 0.8);
  178. const Offset end = Offset(size * 0.9, size * 0.35);
  179. if (t < 0.5) {
  180. final double strokeT = t * 2.0;
  181. final Offset drawMid = Offset.lerp(start, mid, strokeT);
  182. path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
  183. path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
  184. } else {
  185. final double strokeT = (t - 0.5) * 2.0;
  186. final Offset drawEnd = Offset.lerp(mid, end, strokeT);
  187. path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
  188. path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
  189. path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
  190. }
  191. canvas.drawPath(path, paint);
  192. }
  193. void _drawDash(Canvas canvas, Offset origin, double t, Paint paint) {
  194. assert(t >= 0.0 && t <= 1.0);
  195. const Offset start = Offset(_kEdgeSize * 0.2, _kEdgeSize * 0.5);
  196. const Offset mid = Offset(_kEdgeSize * 0.5, _kEdgeSize * 0.5);
  197. const Offset end = Offset(_kEdgeSize * 0.8, _kEdgeSize * 0.5);
  198. final Offset drawStart = Offset.lerp(start, mid, 1.0 - t);
  199. final Offset drawEnd = Offset.lerp(mid, end, t);
  200. canvas.drawLine(origin + drawStart, origin + drawEnd, paint);
  201. }
  202. @override
  203. void paint(PaintingContext context, Offset offset) {
  204. final Canvas canvas = context.canvas;
  205. paintRadialReaction(canvas, offset, size.center(Offset.zero));
  206. final Offset origin =
  207. offset + (size / 2.0 - const Size.square(_kEdgeSize) / 2.0 as Offset);
  208. final AnimationStatus status = position.status;
  209. final double tNormalized =
  210. status == AnimationStatus.forward || status == AnimationStatus.completed
  211. ? position.value
  212. : 1.0 - position.value;
  213. if (_oldValue == false || value == false) {
  214. final double t = value == false ? 1.0 - tNormalized : tNormalized;
  215. final RRect outer = _outerRectAt(origin, t);
  216. final Paint paint = Paint()..color = _colorAt(t);
  217. if (t <= 0.5) {
  218. _drawBorder(canvas, outer, t, paint);
  219. } else {
  220. canvas.drawRRect(outer, paint);
  221. _initStrokePaint(paint);
  222. final double tShrink = (t - 0.5) * 2.0;
  223. if (_oldValue == null || value == null) {
  224. _drawDash(canvas, origin, tShrink, paint);
  225. } else {
  226. _drawCheck(canvas, origin, tShrink, paint);
  227. }
  228. }
  229. } else {
  230. final RRect outer = _outerRectAt(origin, 1.0);
  231. final Paint paint = Paint()..color = _colorAt(1.0);
  232. canvas.drawRRect(outer, paint);
  233. _initStrokePaint(paint);
  234. if (tNormalized <= 0.5) {
  235. final double tShrink = 1.0 - tNormalized * 2.0;
  236. if (_oldValue == true) {
  237. _drawCheck(canvas, origin, tShrink, paint);
  238. } else {
  239. _drawDash(canvas, origin, tShrink, paint);
  240. }
  241. } else {
  242. final double tExpand = (tNormalized - 0.5) * 2.0;
  243. if (value == true) {
  244. _drawCheck(canvas, origin, tExpand, paint);
  245. } else {
  246. _drawDash(canvas, origin, tExpand, paint);
  247. }
  248. }
  249. }
  250. }
  251. }