add detail page
This commit is contained in:
46
lib/widgets/action_button.dart
Normal file
46
lib/widgets/action_button.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ActionButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final Color color;
|
||||
final VoidCallback onPressed;
|
||||
final bool isOutlined;
|
||||
|
||||
const ActionButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.color,
|
||||
required this.onPressed,
|
||||
this.isOutlined = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton.icon(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(icon, size: 20),
|
||||
label: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: isOutlined ? Colors.white : color,
|
||||
foregroundColor: isOutlined ? color : Colors.white,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: isOutlined
|
||||
? BorderSide(color: color, width: 2)
|
||||
: BorderSide.none,
|
||||
),
|
||||
elevation: isOutlined ? 0 : 3,
|
||||
shadowColor: color.withAlpha(140),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
104
lib/widgets/detail_header.dart
Normal file
104
lib/widgets/detail_header.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DetailHeader extends StatelessWidget {
|
||||
final String name;
|
||||
final String type;
|
||||
final String color;
|
||||
|
||||
const DetailHeader({
|
||||
super.key,
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
Color _getColorFromString(String colorName) {
|
||||
final colorMap = {
|
||||
'red': Colors.red,
|
||||
'blue': Colors.blue,
|
||||
'green': Colors.green,
|
||||
'yellow': Colors.yellow,
|
||||
'black': Colors.black,
|
||||
'white': Colors.white,
|
||||
'orange': Colors.orange,
|
||||
'purple': Colors.purple,
|
||||
'pink': Colors.pink,
|
||||
'grey': Colors.grey,
|
||||
'brown': Colors.brown,
|
||||
};
|
||||
return colorMap[colorName.toLowerCase()] ?? Colors.grey;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
_getColorFromString(color).withAlpha(100),
|
||||
_getColorFromString(color).withAlpha(50),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(32),
|
||||
bottomRight: Radius.circular(32),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Color circle
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: _getColorFromString(color),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.white,
|
||||
width: 4,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _getColorFromString(color).withAlpha(120),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
// Name
|
||||
Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
// Type badge
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: _getColorFromString(color).withAlpha(120),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
type,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _getColorFromString(color).withAlpha(180),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
76
lib/widgets/detail_info_card.dart
Normal file
76
lib/widgets/detail_info_card.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DetailInfoCard extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final String value;
|
||||
final Color color;
|
||||
|
||||
const DetailInfoCard({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.color = Colors.blue,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(150),
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withAlpha(10),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(icon, color: color, size: 24),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class FilamentCard extends StatelessWidget {
|
||||
'purple': Colors.purple,
|
||||
'pink': Colors.pink,
|
||||
'grey': Colors.grey,
|
||||
'brown': Colors.brown,
|
||||
};
|
||||
|
||||
return colorMap[colorName.toLowerCase()] ?? Colors.grey;
|
||||
@@ -152,7 +153,7 @@ class FilamentCard extends StatelessWidget {
|
||||
),
|
||||
_buildDetailChip(
|
||||
icon: Icons.scale,
|
||||
label: '${filament.weight}g',
|
||||
label: '${filament.weight - filament.weightUsed}g',
|
||||
color: Colors.green,
|
||||
),
|
||||
_buildDetailChip(
|
||||
@@ -166,6 +167,11 @@ class FilamentCard extends StatelessWidget {
|
||||
label: '${filament.pices} Stk.',
|
||||
color: Colors.purple,
|
||||
),
|
||||
_buildDetailChip(
|
||||
icon: Icons.scale,
|
||||
label: '${filament.weightUsed}g',
|
||||
color: Colors.red,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
102
lib/widgets/progress_ring.dart
Normal file
102
lib/widgets/progress_ring.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProgressRing extends StatelessWidget {
|
||||
final double progress; // 0.0 to 1.0
|
||||
final double size;
|
||||
final double strokeWidth;
|
||||
final Color backgroundColor;
|
||||
final Color progressColor;
|
||||
final Widget? child;
|
||||
|
||||
const ProgressRing({
|
||||
super.key,
|
||||
required this.progress,
|
||||
this.size = 120,
|
||||
this.strokeWidth = 12,
|
||||
this.backgroundColor = Colors.grey,
|
||||
this.progressColor = Colors.blue,
|
||||
this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: CustomPaint(
|
||||
painter: _ProgressRingPainter(
|
||||
progress: progress,
|
||||
strokeWidth: strokeWidth,
|
||||
backgroundColor: backgroundColor,
|
||||
progressColor: progressColor,
|
||||
),
|
||||
child: child != null
|
||||
? Center(child: child)
|
||||
: Center(
|
||||
child: Text(
|
||||
'${(progress * 100).toStringAsFixed(0)}%',
|
||||
style: TextStyle(
|
||||
fontSize: size / 5,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey.shade800,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressRingPainter extends CustomPainter {
|
||||
final double progress;
|
||||
final double strokeWidth;
|
||||
final Color backgroundColor;
|
||||
final Color progressColor;
|
||||
|
||||
_ProgressRingPainter({
|
||||
required this.progress,
|
||||
required this.strokeWidth,
|
||||
required this.backgroundColor,
|
||||
required this.progressColor,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
final radius = (size.width - strokeWidth) / 2;
|
||||
|
||||
// Background circle
|
||||
final backgroundPaint = Paint()
|
||||
..color = backgroundColor.withAlpha(120)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
canvas.drawCircle(center, radius, backgroundPaint);
|
||||
|
||||
// Progress arc
|
||||
final progressPaint = Paint()
|
||||
..shader = LinearGradient(
|
||||
colors: [
|
||||
progressColor,
|
||||
progressColor.withAlpha(170),
|
||||
],
|
||||
).createShader(Rect.fromCircle(center: center, radius: radius))
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
final sweepAngle = 2 * pi * progress;
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(center: center, radius: radius),
|
||||
-pi / 2, // Start from top
|
||||
sweepAngle,
|
||||
false,
|
||||
progressPaint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
||||
}
|
||||
Reference in New Issue
Block a user