runable
This commit is contained in:
269
lib/shared/widgets/price_display.dart
Normal file
269
lib/shared/widgets/price_display.dart
Normal file
@@ -0,0 +1,269 @@
|
||||
/// Price Display Widget
|
||||
///
|
||||
/// Formats and displays prices in Vietnamese currency format
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../core/utils/formatters.dart';
|
||||
|
||||
/// Price display with Vietnamese currency formatting
|
||||
class PriceDisplay extends StatelessWidget {
|
||||
final double price;
|
||||
final TextStyle? style;
|
||||
final bool showSymbol;
|
||||
final int decimalDigits;
|
||||
final Color? color;
|
||||
final FontWeight? fontWeight;
|
||||
final double? fontSize;
|
||||
|
||||
const PriceDisplay({
|
||||
super.key,
|
||||
required this.price,
|
||||
this.style,
|
||||
this.showSymbol = true,
|
||||
this.decimalDigits = 0,
|
||||
this.color,
|
||||
this.fontWeight,
|
||||
this.fontSize,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formattedPrice = CurrencyFormatter.formatWithDecimals(
|
||||
price,
|
||||
decimalDigits: decimalDigits,
|
||||
showSymbol: showSymbol,
|
||||
);
|
||||
|
||||
return Text(
|
||||
formattedPrice,
|
||||
style: style ??
|
||||
TextStyle(
|
||||
color: color,
|
||||
fontWeight: fontWeight ?? FontWeight.w600,
|
||||
fontSize: fontSize,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Price display with sale price comparison
|
||||
class SalePriceDisplay extends StatelessWidget {
|
||||
final double originalPrice;
|
||||
final double salePrice;
|
||||
final TextStyle? originalPriceStyle;
|
||||
final TextStyle? salePriceStyle;
|
||||
final bool showSymbol;
|
||||
final MainAxisAlignment alignment;
|
||||
|
||||
const SalePriceDisplay({
|
||||
super.key,
|
||||
required this.originalPrice,
|
||||
required this.salePrice,
|
||||
this.originalPriceStyle,
|
||||
this.salePriceStyle,
|
||||
this.showSymbol = true,
|
||||
this.alignment = MainAxisAlignment.start,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: alignment,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
// Sale price (larger, prominent)
|
||||
Text(
|
||||
CurrencyFormatter.format(salePrice, showSymbol: showSymbol),
|
||||
style: salePriceStyle ??
|
||||
const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Original price (smaller, strikethrough)
|
||||
Text(
|
||||
CurrencyFormatter.format(originalPrice, showSymbol: showSymbol),
|
||||
style: originalPriceStyle ??
|
||||
TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.grey[600],
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Price display with discount percentage badge
|
||||
class PriceWithDiscount extends StatelessWidget {
|
||||
final double originalPrice;
|
||||
final double salePrice;
|
||||
final bool showSymbol;
|
||||
final TextStyle? salePriceStyle;
|
||||
final TextStyle? originalPriceStyle;
|
||||
|
||||
const PriceWithDiscount({
|
||||
super.key,
|
||||
required this.originalPrice,
|
||||
required this.salePrice,
|
||||
this.showSymbol = true,
|
||||
this.salePriceStyle,
|
||||
this.originalPriceStyle,
|
||||
});
|
||||
|
||||
double get discountPercentage {
|
||||
return ((originalPrice - salePrice) / originalPrice * 100);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// Sale price
|
||||
Text(
|
||||
CurrencyFormatter.format(salePrice, showSymbol: showSymbol),
|
||||
style: salePriceStyle ??
|
||||
const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Discount badge
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
'-${discountPercentage.toStringAsFixed(0)}%',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// Original price
|
||||
Text(
|
||||
CurrencyFormatter.format(originalPrice, showSymbol: showSymbol),
|
||||
style: originalPriceStyle ??
|
||||
TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.grey[600],
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compact price display for lists/grids
|
||||
class CompactPriceDisplay extends StatelessWidget {
|
||||
final double price;
|
||||
final double? salePrice;
|
||||
final bool showSymbol;
|
||||
|
||||
const CompactPriceDisplay({
|
||||
super.key,
|
||||
required this.price,
|
||||
this.salePrice,
|
||||
this.showSymbol = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isOnSale = salePrice != null && salePrice! < price;
|
||||
|
||||
if (isOnSale) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
CurrencyFormatter.format(salePrice!, showSymbol: showSymbol),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
CurrencyFormatter.format(price, showSymbol: showSymbol),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Text(
|
||||
CurrencyFormatter.format(price, showSymbol: showSymbol),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Large price display for checkout/order summary
|
||||
class LargePriceDisplay extends StatelessWidget {
|
||||
final double price;
|
||||
final String? label;
|
||||
final bool showSymbol;
|
||||
final Color? color;
|
||||
|
||||
const LargePriceDisplay({
|
||||
super.key,
|
||||
required this.price,
|
||||
this.label,
|
||||
this.showSymbol = true,
|
||||
this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (label != null) ...[
|
||||
Text(
|
||||
label!,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
Text(
|
||||
CurrencyFormatter.format(price, showSymbol: showSymbol),
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color ?? Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user