refactor: save current workspace state including FinlyticAnalyzer fixes, FinlyticApp trade route alignment, and DTO audit documentation
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '../network/api_client.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import '../utils/asset_utils.dart';
|
||||
|
||||
/// Reusable performance-optimized Asset Logo Widget supporting SVG, PNG, gradient fallbacks, and Hero transitions.
|
||||
class AssetLogoWidget extends StatelessWidget {
|
||||
@@ -15,24 +15,29 @@ class AssetLogoWidget extends StatelessWidget {
|
||||
const AssetLogoWidget({
|
||||
super.key,
|
||||
required this.symbolOrName,
|
||||
this.imageUrl,
|
||||
required this.imageUrl,
|
||||
this.size = 32,
|
||||
this.enableHero = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final rawUrl = imageUrl ?? AssetUtils.getLogoUrl(symbolOrName);
|
||||
final logoUrl = rawUrl != null && rawUrl.isNotEmpty ? AssetUtils.resolveUrl(rawUrl) : null;
|
||||
// Resolve relative URLs (e.g. /api/v1/logo/...) to include host and port (e.g. http://localhost:5000)
|
||||
String? resolveUrl(String? url) {
|
||||
if (url == null || url.isEmpty) return null;
|
||||
if (url.startsWith('http://') || url.startsWith('https://')) return url;
|
||||
return url.startsWith('/') ? '${ApiClient.baseUrl}$url' : '${ApiClient.baseUrl}/$url';
|
||||
}
|
||||
|
||||
final image = resolveUrl(imageUrl);
|
||||
final initial = symbolOrName.isNotEmpty ? symbolOrName[0].toUpperCase() : 'A';
|
||||
final colors = _getGradientColors(initial);
|
||||
|
||||
Widget content;
|
||||
|
||||
if (logoUrl != null && logoUrl.isNotEmpty && !_failedUrls.contains(logoUrl)) {
|
||||
final isSvg = logoUrl.toLowerCase().endsWith('.svg') ||
|
||||
logoUrl.contains('traderepublic.com') ||
|
||||
logoUrl.contains('/api/logo/');
|
||||
if (image != null && image.isNotEmpty && !_failedUrls.contains(image)) {
|
||||
final isSvg = image.toLowerCase().endsWith('.svg') ||
|
||||
image.contains('/api/v1/logo/');
|
||||
|
||||
content = ClipRRect(
|
||||
borderRadius: BorderRadius.circular(size * 0.3),
|
||||
@@ -50,26 +55,26 @@ class AssetLogoWidget extends StatelessWidget {
|
||||
padding: EdgeInsets.all(size * 0.1),
|
||||
child: isSvg
|
||||
? SvgPicture.network(
|
||||
logoUrl,
|
||||
width: size,
|
||||
height: size,
|
||||
fit: BoxFit.contain,
|
||||
placeholderBuilder: (context) => _buildFallback(initial, colors),
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
_failedUrls.add(logoUrl);
|
||||
return _buildFallback(initial, colors);
|
||||
},
|
||||
)
|
||||
image,
|
||||
width: size,
|
||||
height: size,
|
||||
fit: BoxFit.contain,
|
||||
placeholderBuilder: (context) => _buildFallback(initial, colors),
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
_failedUrls.add(image);
|
||||
return _buildFallback(initial, colors);
|
||||
},
|
||||
)
|
||||
: Image.network(
|
||||
logoUrl,
|
||||
width: size,
|
||||
height: size,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
_failedUrls.add(logoUrl);
|
||||
return _buildFallback(initial, colors);
|
||||
},
|
||||
),
|
||||
image,
|
||||
width: size,
|
||||
height: size,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
_failedUrls.add(image);
|
||||
return _buildFallback(initial, colors);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -78,7 +83,7 @@ class AssetLogoWidget extends StatelessWidget {
|
||||
|
||||
if (enableHero && symbolOrName.isNotEmpty) {
|
||||
return Hero(
|
||||
tag: 'asset_logo_$symbolOrName',
|
||||
tag: 'asset_logo_${symbolOrName}_$size',
|
||||
child: content,
|
||||
);
|
||||
}
|
||||
@@ -127,4 +132,4 @@ class AssetLogoWidget extends StatelessWidget {
|
||||
return [AppTheme.activePreset.accentColor, const Color(0xFF3B82F6)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user