glanzlicht / Meinweimar_Saving_App

MW Rettertuete & Deals - clean

Verified secure showcase
112 Files 341 KB Public size 7 Redacted Dart · XCCONFIG · H · XML · GITIGNORE · PLIST

Project overview

Meinweimar_Saving_App is a verified code showcase with 112 visible project entries across 9 top-level modules. The primary detected language is Dart; protected files remain visible in the structure while their contents stay private.

README

meinweimar_box__nativ

A new Flutter project.

Getting Started

This project is a starting point for a Flutter application.

A few resources to get you started if this is your first Flutter project:

For help getting started with Flutter development, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.

lib/product_card.dartDart
import 'package:flutter/material.dart';
import 'constants.dart';
import 'product_detail_page.dart';
import 'main.dart';
class ProductCard extends StatelessWidget {
final dynamic product;
final String timerText;
final bool isExpired;
final bool isOngoing;
final bool isOutOfStock;
final double distance;
final bool isFavorite; // Status für WCFM Favorit
final VoidCallback? onFavoriteToggle; // Aktion für Klick
const ProductCard({
super.key,
required this.product,
required this.timerText,
required this.isExpired,
this.isOngoing = false,
this.isOutOfStock = false,
required this.distance,
this.isFavorite = false,
this.onFavoriteToggle,
});
@override
Widget build(BuildContext context) {
final v = product['vendor_info'];
final bool hasDiscount = product['regular_price'] != null &&
product['regular_price'] != "" &&
product['regular_price'] != product['price'];
final int stockQty = product['stock_quantity'] ?? 0;
final bool showLowStockBadge = !isOutOfStock && !isExpired && stockQty > 0 && stockQty <= 2;
double rating = 0.0;
try {
if (v != null && v is Map) {
var finalVRating = v['store_rating'] ?? v['avg_review_rating'] ?? v['vendor_rating'];
rating = double.tryParse(finalVRating?.toString() ?? "0.0") ?? 0.0;
}
if (rating == 0) {
rating = double.tryParse(product['average_rating']?.toString() ?? "0.0") ?? 0.0;
}
} catch (e) {
rating = 0.0;
}
return Opacity(
opacity: (isExpired || isOutOfStock) ? 0.7 : 1.0,
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => ProductDetailPage(product: product)),
),
child: Container(
width: 280,
margin: const EdgeInsets.only(right: 15, bottom: 2),
decoration: BoxDecoration(
color: AppConstants.baseWhite,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
)
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
child: ColorFiltered(
colorFilter: (isExpired || isOutOfStock)
? const ColorFilter.mode(Colors.grey, BlendMode.saturation)
: const ColorFilter.mode(Colors.transparent, BlendMode.multiply),
child: product['images'] != null && product['images'].isNotEmpty
? Image.network(
product['images'][0]['src'],
height: 160,
width: 280,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => Container(
height: 160,
width: 280,
color: Colors.grey[200],
child: const Icon(Icons.broken_image, color: Colors.grey)
),
)
: Container(height: 160, width: 280, color: Colors.grey[200], child: const Icon(Icons.image_not_supported)),
),
),
if (isOutOfStock || showLowStockBadge)
Positioned(
top: 10,
right: 10,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: isOutOfStock ? Colors.grey[800] : AppConstants.mainColor,
borderRadius: BorderRadius.circular(10),
),
child: Text(
isOutOfStock ? "Ausverkauft" : "Nur noch $stockQty!",
style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
),
),
),
if (v != null && v['logo'] != null && v['logo'] != "" && v['logo'] != false)
Positioned(
top: 10,
left: 10,
child: CircleAvatar(
backgroundImage: NetworkImage(v['logo']),
radius: 20,
backgroundColor: AppConstants.baseWhite,
),
),
if (rating > 0)
Positioned(
bottom: 10,
left: 10,
child: _buildInfoChip(Row(
children: [
const Icon(Icons.star, color: Colors.amber, size: 14),
const SizedBox(width: 4),
Text(rating.toStringAsFixed(1), style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: AppConstants.fontGrey)),
],
)),
),
Positioned(
bottom: 10,
right: 10,
child: _buildInfoChip(Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
if (hasDiscount)
Text("${formatPrice(product['regular_price'])} €", style: const TextStyle(color: Colors.grey, fontSize: 10, decoration: TextDecoration.lineThrough)),
Text("${formatPrice(product['price'])} €", style: const TextStyle(color: AppConstants.mainColor, fontWeight: FontWeight.bold, fontSize: 15)),
],
)),
),
],
),
Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
v?['store_name'] ?? "Partner",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: AppConstants.fontGrey),
),
),
if (distance > 0)
Text("${distance.toStringAsFixed(1)} km", style: const TextStyle(fontSize: 12, color: Colors.grey)),
],
),
const SizedBox(height: 2),
Text(product['name'] ?? "", maxLines: 1, style: const TextStyle(color: Colors.grey, fontSize: 12, overflow: TextOverflow.ellipsis)),
const SizedBox(height: 8),
// Row für Timer (links) und Herz-Button (rechts)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildStatusRow(),
GestureDetector(
onTap: onFavoriteToggle,
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: isFavorite ? AppConstants.mainColor.withOpacity(0.1) : Colors.grey[100],
shape: BoxShape.circle,
),
child: Icon(
isFavorite ? Icons.favorite : Icons.favorite_border,
size: 18,
color: isFavorite ? AppConstants.mainColor : Colors.grey,
),
),
),
],
),
],
),
),
],
),
),
),
);
}
Widget _buildInfoChip(Widget child) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: AppConstants.baseWhite,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 4)],
),
child: child,
);
}
Widget _buildStatusRow() {
if (isOutOfStock) {
return const Row(
children: [
Icon(Icons.block, size: 14, color: Colors.grey),
SizedBox(width: 5),
Text("Leider schon weg", style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: Colors.grey)),
],
);
}
if (timerText.isNotEmpty) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isOngoing ? AppConstants.mainColor.withOpacity(0.1) : Colors.grey[100],
borderRadius: BorderRadius.circular(10)
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.timer, size: 14, color: isOngoing ? AppConstants.mainColor : AppConstants.fontGrey),
const SizedBox(width: 6),
Text(timerText, style: TextStyle(color: isOngoing ? AppConstants.mainColor : AppConstants.fontGrey, fontWeight: FontWeight.bold, fontSize: 11)),
],
),
);
} else if (isExpired) {
return const Row(
children: [
Icon(Icons.info_outline, size: 14, color: AppConstants.mainColor),
SizedBox(width: 5),
Text("Aktion für heute beendet", style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: AppConstants.mainColor)),
],
);
}
return const SizedBox.shrink();
}
}

Architecture

High-level modules detected from the verified public snapshot. Protected implementation details are intentionally omitted.

macosmodule
iosmodule
libmodule
windowsmodule
androidmodule
linuxmodule
webmodule
testmodule

Technology stack

DartPBXPROJLOCKXIBTXTCPPFlutter / Dart
Dart39.9%
PBXPROJ16.4%
LOCK7.1%
XIB6.8%
TXT5.2%
CPP3.9%
JSON2.9%
XCSCHEME2.2%