asset_marker_icon.dart 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:immich_mobile/shared/models/store.dart';
  4. import 'package:immich_mobile/utils/image_url_builder.dart';
  5. class AssetMarkerIcon extends StatelessWidget {
  6. const AssetMarkerIcon({
  7. Key? key,
  8. required this.id,
  9. this.isDarkTheme = false,
  10. }) : super(key: key);
  11. final String id;
  12. final bool isDarkTheme;
  13. @override
  14. Widget build(BuildContext context) {
  15. final imageUrl = getThumbnailUrlForRemoteId(id);
  16. final cacheKey = getThumbnailCacheKeyForRemoteId(id);
  17. return LayoutBuilder(
  18. builder: (context, constraints) {
  19. return Stack(
  20. children: [
  21. Positioned(
  22. bottom: 0,
  23. left: constraints.maxWidth * 0.5,
  24. child: CustomPaint(
  25. painter: _PinPainter(
  26. primaryColor: isDarkTheme ? Colors.white : Colors.black,
  27. secondaryColor: isDarkTheme ? Colors.black : Colors.white,
  28. primaryRadius: constraints.maxHeight * 0.06,
  29. secondaryRadius: constraints.maxHeight * 0.038,
  30. ),
  31. child: SizedBox(
  32. height: constraints.maxHeight * 0.14,
  33. width: constraints.maxWidth * 0.14,
  34. ),
  35. ),
  36. ),
  37. Positioned(
  38. top: constraints.maxHeight * 0.07,
  39. left: constraints.maxWidth * 0.17,
  40. child: CircleAvatar(
  41. radius: constraints.maxHeight * 0.40,
  42. backgroundColor: isDarkTheme ? Colors.white : Colors.black,
  43. child: CircleAvatar(
  44. radius: constraints.maxHeight * 0.37,
  45. backgroundImage: CachedNetworkImageProvider(
  46. imageUrl,
  47. cacheKey: cacheKey,
  48. headers: {
  49. "Authorization":
  50. "Bearer ${Store.get(StoreKey.accessToken)}",
  51. },
  52. errorListener: () =>
  53. const Icon(Icons.image_not_supported_outlined),
  54. ),
  55. ),
  56. ),
  57. ),
  58. ],
  59. );
  60. },
  61. );
  62. }
  63. }
  64. class _PinPainter extends CustomPainter {
  65. final Color primaryColor;
  66. final Color secondaryColor;
  67. final double primaryRadius;
  68. final double secondaryRadius;
  69. _PinPainter({
  70. this.primaryColor = Colors.black,
  71. this.secondaryColor = Colors.white,
  72. required this.primaryRadius,
  73. required this.secondaryRadius,
  74. });
  75. @override
  76. void paint(Canvas canvas, Size size) {
  77. Paint primaryBrush = Paint()
  78. ..color = primaryColor
  79. ..style = PaintingStyle.fill;
  80. Paint secondaryBrush = Paint()
  81. ..color = secondaryColor
  82. ..style = PaintingStyle.fill;
  83. Paint lineBrush = Paint()
  84. ..color = primaryColor
  85. ..style = PaintingStyle.stroke
  86. ..strokeWidth = 2;
  87. canvas.drawCircle(
  88. Offset(size.width / 2, size.height),
  89. primaryRadius,
  90. primaryBrush,
  91. );
  92. canvas.drawCircle(
  93. Offset(size.width / 2, size.height),
  94. secondaryRadius,
  95. secondaryBrush,
  96. );
  97. canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush);
  98. // The line is to make the above triangluar path more prominent since it has a slight curve
  99. canvas.drawLine(
  100. Offset(size.width / 2, 0),
  101. Offset(
  102. size.width / 2,
  103. size.height,
  104. ),
  105. lineBrush,
  106. );
  107. }
  108. Path getTrianglePath(double x, double y) {
  109. final firstEndPoint = Offset(x / 2, y);
  110. final controlPoint = Offset(x / 2, y * 0.3);
  111. final secondEndPoint = Offset(x, 0);
  112. return Path()
  113. ..quadraticBezierTo(
  114. controlPoint.dx,
  115. controlPoint.dy,
  116. firstEndPoint.dx,
  117. firstEndPoint.dy,
  118. )
  119. ..quadraticBezierTo(
  120. controlPoint.dx,
  121. controlPoint.dy,
  122. secondEndPoint.dx,
  123. secondEndPoint.dy,
  124. )
  125. ..lineTo(0, 0);
  126. }
  127. @override
  128. bool shouldRepaint(_PinPainter old) {
  129. return old.primaryColor != primaryColor ||
  130. old.secondaryColor != secondaryColor;
  131. }
  132. }