Polish navigation, Quran flows, and sharing UX
This commit is contained in:
686
lib/core/widgets/ayat_share_sheet.dart
Normal file
686
lib/core/widgets/ayat_share_sheet.dart
Normal file
@@ -0,0 +1,686 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../app/icons/app_icons.dart';
|
||||
import '../../app/theme/app_colors.dart';
|
||||
import '../../core/widgets/arabic_text.dart';
|
||||
|
||||
String buildAyatShareText(Map<String, dynamic> ayat) {
|
||||
final arabic = (ayat['teksArab'] ?? '').toString().trim();
|
||||
final translation = (ayat['teksIndonesia'] ?? '').toString().trim();
|
||||
final surahName = (ayat['surahName'] ?? '').toString().trim();
|
||||
final verseNumber = (ayat['nomorAyat'] ?? '').toString().trim();
|
||||
final reference = surahName.isNotEmpty && verseNumber.isNotEmpty
|
||||
? 'QS. $surahName: $verseNumber'
|
||||
: 'Ayat Hari Ini';
|
||||
|
||||
final parts = <String>[
|
||||
if (arabic.isNotEmpty) arabic,
|
||||
if (translation.isNotEmpty) '"$translation"',
|
||||
reference,
|
||||
'Dibagikan dari Jam Shalat Diary',
|
||||
];
|
||||
|
||||
return parts.join('\n\n');
|
||||
}
|
||||
|
||||
Future<void> showAyatShareSheet(
|
||||
BuildContext context,
|
||||
Map<String, dynamic> ayat,
|
||||
) async {
|
||||
final shareText = buildAyatShareText(ayat);
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
builder: (sheetContext) {
|
||||
Future<void> handleShareImage() async {
|
||||
Navigator.of(sheetContext).pop();
|
||||
|
||||
try {
|
||||
final pngBytes = await _captureAyatShareCardPng(context, ayat);
|
||||
final file = await _writeAyatShareImage(pngBytes);
|
||||
await Share.shareXFiles(
|
||||
[XFile(file.path)],
|
||||
text: 'Ayat Hari Ini',
|
||||
subject: 'Ayat Hari Ini',
|
||||
);
|
||||
} catch (_) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Gagal menyiapkan gambar ayat'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleShareText() async {
|
||||
Navigator.of(sheetContext).pop();
|
||||
await Share.share(
|
||||
shareText,
|
||||
subject: 'Ayat Hari Ini',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleCopyText() async {
|
||||
await Clipboard.setData(ClipboardData(text: shareText));
|
||||
if (!sheetContext.mounted) return;
|
||||
Navigator.of(sheetContext).pop();
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Teks ayat disalin ke clipboard'),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Bagikan Ayat',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'Pilih cara tercepat untuk membagikan ayat hari ini.',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
height: 1.5,
|
||||
color: isDark
|
||||
? AppColors.textSecondaryDark
|
||||
: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
_AyatShareActionTile(
|
||||
icon: const Icon(
|
||||
LucideIcons.image,
|
||||
size: 20,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
title: 'Bagikan Gambar',
|
||||
subtitle: 'Kirim kartu ayat yang siap dibagikan',
|
||||
badge: 'Utama',
|
||||
onTap: handleShareImage,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_AyatShareActionTile(
|
||||
icon: const AppIcon(
|
||||
glyph: AppIcons.share,
|
||||
size: 20,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
title: 'Bagikan Teks',
|
||||
subtitle: 'Kirim ayat dan terjemahan ke aplikasi lain',
|
||||
onTap: handleShareText,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_AyatShareActionTile(
|
||||
icon: const Icon(
|
||||
LucideIcons.copy,
|
||||
size: 20,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
title: 'Salin Teks',
|
||||
subtitle: 'Simpan ke clipboard untuk ditempel manual',
|
||||
onTap: handleCopyText,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Uint8List> _captureAyatShareCardPng(
|
||||
BuildContext context,
|
||||
Map<String, dynamic> ayat,
|
||||
) async {
|
||||
final overlay = Overlay.of(context, rootOverlay: true);
|
||||
final boundaryKey = GlobalKey();
|
||||
final theme = Theme.of(context);
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final textDirection = Directionality.of(context);
|
||||
|
||||
late final OverlayEntry entry;
|
||||
entry = OverlayEntry(
|
||||
builder: (_) => IgnorePointer(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Opacity(
|
||||
opacity: 0.01,
|
||||
child: MediaQuery(
|
||||
data: mediaQuery,
|
||||
child: Theme(
|
||||
data: theme,
|
||||
child: Directionality(
|
||||
textDirection: textDirection,
|
||||
child: UnconstrainedBox(
|
||||
constrainedAxis: Axis.horizontal,
|
||||
child: RepaintBoundary(
|
||||
key: boundaryKey,
|
||||
child: _AyatShareCard(
|
||||
ayat: ayat,
|
||||
isDark: theme.brightness == Brightness.dark,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
overlay.insert(entry);
|
||||
|
||||
try {
|
||||
await Future<void>.delayed(const Duration(milliseconds: 20));
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
await Future<void>.delayed(const Duration(milliseconds: 20));
|
||||
|
||||
final boundary = boundaryKey.currentContext?.findRenderObject()
|
||||
as RenderRepaintBoundary?;
|
||||
if (boundary == null) {
|
||||
throw StateError('Ayat share card is not ready');
|
||||
}
|
||||
|
||||
final image = await boundary.toImage(pixelRatio: 3);
|
||||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
if (byteData == null) {
|
||||
throw StateError('Failed to encode ayat share card');
|
||||
}
|
||||
return byteData.buffer.asUint8List();
|
||||
} finally {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
|
||||
Future<File> _writeAyatShareImage(Uint8List pngBytes) async {
|
||||
final directory = await Directory.systemTemp.createTemp('jamshalat_ayat_');
|
||||
final file = File('${directory.path}/ayat_hari_ini.png');
|
||||
await file.writeAsBytes(pngBytes, flush: true);
|
||||
return file;
|
||||
}
|
||||
|
||||
class _AyatShareCard extends StatelessWidget {
|
||||
const _AyatShareCard({
|
||||
required this.ayat,
|
||||
required this.isDark,
|
||||
});
|
||||
|
||||
final Map<String, dynamic> ayat;
|
||||
final bool isDark;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final arabic = (ayat['teksArab'] ?? '').toString().trim();
|
||||
final translation = (ayat['teksIndonesia'] ?? '').toString().trim();
|
||||
final surahName = (ayat['surahName'] ?? '').toString().trim();
|
||||
final verseNumber = (ayat['nomorAyat'] ?? '').toString().trim();
|
||||
final reference = surahName.isNotEmpty && verseNumber.isNotEmpty
|
||||
? 'QS. $surahName: $verseNumber'
|
||||
: 'Ayat Hari Ini';
|
||||
final isLongArabic = arabic.length > 120;
|
||||
final isVeryLongArabic = arabic.length > 180;
|
||||
final isLongTranslation = translation.length > 140;
|
||||
final isVeryLongTranslation = translation.length > 220;
|
||||
final arabicFontSize = isVeryLongArabic
|
||||
? 22.0
|
||||
: isLongArabic
|
||||
? 24.0
|
||||
: 28.0;
|
||||
final arabicHeight = isVeryLongArabic
|
||||
? 1.55
|
||||
: isLongArabic
|
||||
? 1.62
|
||||
: 1.75;
|
||||
final translationFontSize = isVeryLongTranslation
|
||||
? 13.0
|
||||
: isLongTranslation
|
||||
? 14.0
|
||||
: 15.0;
|
||||
final translationHeight = isVeryLongTranslation ? 1.5 : 1.6;
|
||||
final verticalPadding = isVeryLongTranslation ? 22.0 : 24.0;
|
||||
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: isDark
|
||||
? const [
|
||||
Color(0xFF102028),
|
||||
Color(0xFF0F1217),
|
||||
Color(0xFF16343A),
|
||||
]
|
||||
: const [
|
||||
Color(0xFFF6FBFB),
|
||||
Color(0xFFFFFFFF),
|
||||
Color(0xFFEAF7F7),
|
||||
],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
AppColors.primary.withValues(alpha: isDark ? 0.24 : 0.12),
|
||||
blurRadius: 28,
|
||||
offset: const Offset(0, 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: CustomPaint(
|
||||
painter: _AyatFramePainter(isDark: isDark),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: -38,
|
||||
right: -34,
|
||||
child: Container(
|
||||
width: 116,
|
||||
height: 116,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.primary.withValues(alpha: 0.05),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: -46,
|
||||
left: -28,
|
||||
child: Container(
|
||||
width: 132,
|
||||
height: 132,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.navActiveGold.withValues(alpha: 0.05),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
28,
|
||||
28,
|
||||
28,
|
||||
verticalPadding,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 42,
|
||||
height: 42,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withValues(alpha: 0.14),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.bookMarked,
|
||||
size: 20,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Ayat Hari Ini',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 1.3,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
reference,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: isDark
|
||||
? Colors.white
|
||||
: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (arabic.isNotEmpty) ...[
|
||||
const SizedBox(height: 28),
|
||||
ArabicText(
|
||||
arabic,
|
||||
baseFontSize: arabicFontSize,
|
||||
height: arabicHeight,
|
||||
textAlign: TextAlign.right,
|
||||
color: isDark
|
||||
? AppColors.textPrimaryDark
|
||||
: AppColors.textPrimaryLight,
|
||||
),
|
||||
],
|
||||
if (translation.isNotEmpty) ...[
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
color: (isDark ? Colors.white : AppColors.primary)
|
||||
.withValues(alpha: isDark ? 0.05 : 0.08),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: (isDark ? Colors.white : AppColors.primary)
|
||||
.withValues(alpha: 0.08),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'"$translation"',
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: translationFontSize,
|
||||
height: translationHeight,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: isDark
|
||||
? AppColors.textPrimaryDark
|
||||
: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 22),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColors.navActiveGold.withValues(alpha: 0.16),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: const Text(
|
||||
'Jam Shalat Diary',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.navActiveGoldDeep,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Bagikan kebaikan',
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDark
|
||||
? AppColors.textSecondaryDark
|
||||
: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AyatShareActionTile extends StatelessWidget {
|
||||
const _AyatShareActionTile({
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.onTap,
|
||||
this.badge,
|
||||
});
|
||||
|
||||
final Widget icon;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final VoidCallback onTap;
|
||||
final String? badge;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
border: Border.all(
|
||||
color: isDark
|
||||
? AppColors.primary.withValues(alpha: 0.12)
|
||||
: AppColors.cream,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 42,
|
||||
height: 42,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary.withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Center(child: icon),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
if (badge != null) ...[
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
AppColors.navActiveGold.withValues(alpha: 0.18),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Text(
|
||||
badge!,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: AppColors.navActiveGoldDeep,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
height: 1.4,
|
||||
color: isDark
|
||||
? AppColors.textSecondaryDark
|
||||
: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
size: 18,
|
||||
color: isDark
|
||||
? AppColors.textSecondaryDark
|
||||
: AppColors.textSecondaryLight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AyatFramePainter extends CustomPainter {
|
||||
const _AyatFramePainter({required this.isDark});
|
||||
|
||||
final bool isDark;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final outerRect = RRect.fromRectAndRadius(
|
||||
Offset.zero & size,
|
||||
const Radius.circular(22),
|
||||
);
|
||||
|
||||
final outerPaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 1.6
|
||||
..shader = const LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
AppColors.navActiveGoldPale,
|
||||
AppColors.navActiveGold,
|
||||
AppColors.navActiveGoldDeep,
|
||||
],
|
||||
).createShader(Offset.zero & size);
|
||||
|
||||
final outerGlow = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 5
|
||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6)
|
||||
..color = AppColors.navActiveGold.withValues(alpha: isDark ? 0.08 : 0.05);
|
||||
|
||||
final innerBounds = Rect.fromLTWH(8, 8, size.width - 16, size.height - 16);
|
||||
final innerFrame = RRect.fromRectAndRadius(
|
||||
innerBounds,
|
||||
const Radius.circular(18),
|
||||
);
|
||||
final innerPaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.8
|
||||
..color = (isDark ? Colors.white : AppColors.primary)
|
||||
.withValues(alpha: isDark ? 0.08 : 0.10);
|
||||
|
||||
canvas.drawRRect(outerRect, outerGlow);
|
||||
canvas.drawRRect(outerRect, outerPaint);
|
||||
canvas.drawRRect(innerFrame, innerPaint);
|
||||
|
||||
_drawMidMotif(canvas, size, top: true);
|
||||
_drawMidMotif(canvas, size, top: false);
|
||||
}
|
||||
|
||||
void _drawMidMotif(Canvas canvas, Size size, {required bool top}) {
|
||||
final y = top ? 14.0 : size.height - 14.0;
|
||||
final centerX = size.width / 2;
|
||||
|
||||
final linePaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.9
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = AppColors.navActiveGold.withValues(alpha: isDark ? 0.26 : 0.22);
|
||||
final diamondPaint = Paint()
|
||||
..style = PaintingStyle.fill
|
||||
..color = AppColors.primary.withValues(alpha: isDark ? 0.34 : 0.22);
|
||||
final diamondStroke = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.9
|
||||
..color = AppColors.navActiveGold.withValues(alpha: isDark ? 0.58 : 0.48);
|
||||
|
||||
canvas.drawLine(
|
||||
Offset(centerX - 26, y),
|
||||
Offset(centerX - 10, y),
|
||||
linePaint,
|
||||
);
|
||||
canvas.drawLine(
|
||||
Offset(centerX + 10, y),
|
||||
Offset(centerX + 26, y),
|
||||
linePaint,
|
||||
);
|
||||
|
||||
final diamondPath = Path()
|
||||
..moveTo(centerX, y - 5)
|
||||
..lineTo(centerX + 5, y)
|
||||
..lineTo(centerX, y + 5)
|
||||
..lineTo(centerX - 5, y)
|
||||
..close();
|
||||
canvas.drawPath(diamondPath, diamondPaint);
|
||||
canvas.drawPath(diamondPath, diamondStroke);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _AyatFramePainter oldDelegate) {
|
||||
return oldDelegate.isDark != isDark;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user