advanced_cached_network_image 1.0.0
advanced_cached_network_image: ^1.0.0 copied to clipboard
A Flutter package that provides queued image loading with caching to prevent 429 (Too Many Requests) errors. Load images sequentially through a queue system with retry mechanisms.
example/advanced_cached_network_image_example.dart
import 'package:flutter/material.dart';
import 'package:advanced_cached_network_image/advanced_cached_network_image.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Queued Cached Network Image Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ImageListPage(),
);
}
}
class ImageListPage extends StatefulWidget {
@override
_ImageListPageState createState() => _ImageListPageState();
}
class _ImageListPageState extends State<ImageListPage> {
final List<String> imageUrls = [
'https://picsum.photos/200/300?random=1',
'https://picsum.photos/200/300?random=2',
'https://picsum.photos/200/300?random=3',
'https://picsum.photos/200/300?random=4',
'https://picsum.photos/200/300?random=5',
'https://picsum.photos/200/300?random=6',
'https://picsum.photos/200/300?random=7',
'https://picsum.photos/200/300?random=8',
'https://picsum.photos/200/300?random=9',
'https://picsum.photos/200/300?random=10',
];
int retryCount = 3;
Duration queueDelay = Duration(milliseconds: 500);
bool isLoading = false;
final List<String> logs = [];
void _addLog(String message) {
setState(() {
logs.add('${DateTime.now().toString().substring(11, 19)}: $message');
});
}
Future<void> _loadImagesWithQueue() async {
if (isLoading) return;
setState(() {
isLoading = true;
logs.clear();
});
_addLog('Starting queued image loading...');
_addLog('Retry count: $retryCount');
_addLog('Queue delay: ${queueDelay.inMilliseconds}ms');
for (int i = 0; i < imageUrls.length; i++) {
final url = imageUrls[i];
_addLog('Loading image ${i + 1}: $url');
try {
await AdvancedCachedNetworkImageProvider.create(
url: url,
retryCount: retryCount,
queueDelay: queueDelay,
);
_addLog('✅ Successfully loaded: $url');
} catch (e) {
_addLog('❌ Failed to load: $url - $e');
}
}
setState(() {
isLoading = false;
});
_addLog('=== Loading completed ===');
}
Future<void> _loadImagesDirect() async {
if (isLoading) return;
setState(() {
isLoading = true;
logs.clear();
});
_addLog('Starting direct image loading (no queue)...');
for (int i = 0; i < imageUrls.length; i++) {
final url = imageUrls[i];
_addLog('Loading image ${i + 1}: $url');
try {
AdvancedCachedNetworkImageProvider.createDirect(url: url);
_addLog('✅ Direct load successful: $url');
} catch (e) {
_addLog('❌ Direct load failed: $url - $e');
}
}
setState(() {
isLoading = false;
});
_addLog('=== Direct loading completed ===');
}
Future<void> _simulate429Error() async {
if (isLoading) return;
setState(() {
isLoading = true;
logs.clear();
});
_addLog('Simulating 429 error scenario...');
_addLog('Loading all images simultaneously with minimal delay...');
// Load all images simultaneously with very small delay
final futures = <Future>[];
for (int i = 0; i < imageUrls.length; i++) {
final url = imageUrls[i];
_addLog('Starting load: $url');
futures.add(
AdvancedCachedNetworkImageProvider.create(
url: url,
retryCount: retryCount,
queueDelay: Duration(milliseconds: 50), // Very small delay
).then((provider) {
_addLog('✅ Successfully loaded: $url');
}).catchError((e) {
_addLog('❌ Failed to load: $url - $e');
})
);
}
await Future.wait(futures);
setState(() {
isLoading = false;
});
_addLog('=== 429 simulation completed ===');
}
void _clearQueue() {
AdvancedCachedNetworkImageProvider.clearQueue();
_addLog('Queue cleared');
}
void _showQueueInfo() {
_addLog('=== Queue Information ===');
_addLog('Queue length: ${AdvancedCachedNetworkImageProvider.queueLength}');
_addLog('Currently loading: ${AdvancedCachedNetworkImageProvider.loadingCount}');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Queued Cached Network Image Example'),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
actions: [
Text(
'Queue: ${AdvancedCachedNetworkImageProvider.queueLength}',
style: TextStyle(fontSize: 16),
),
SizedBox(width: 16),
],
),
body: Column(
children: [
// Control Panel
Container(
padding: EdgeInsets.all(16),
color: Colors.grey.shade100,
child: Column(
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Retry Count: $retryCount'),
Slider(
value: retryCount.toDouble(),
min: 1,
max: 10,
divisions: 9,
onChanged: isLoading ? null : (value) {
setState(() {
retryCount = value.round();
});
},
),
],
),
),
SizedBox(width: 20),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Queue Delay: ${queueDelay.inMilliseconds}ms'),
Slider(
value: queueDelay.inMilliseconds.toDouble(),
min: 0,
max: 2000,
divisions: 20,
onChanged: isLoading ? null : (value) {
setState(() {
queueDelay = Duration(milliseconds: value.round());
});
},
),
],
),
),
],
),
SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed: isLoading ? null : _loadImagesWithQueue,
child: Text('Load with Queue'),
),
ElevatedButton(
onPressed: isLoading ? null : _loadImagesDirect,
child: Text('Load Direct'),
),
ElevatedButton(
onPressed: isLoading ? null : _simulate429Error,
child: Text('Simulate 429'),
),
ElevatedButton(
onPressed: _showQueueInfo,
child: Text('Queue Info'),
),
ElevatedButton(
onPressed: _clearQueue,
child: Text('Clear Queue'),
),
],
),
],
),
),
// Image List
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Image List:',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Expanded(
child: ListView.builder(
itemCount: imageUrls.length,
itemBuilder: (context, index) {
final url = imageUrls[index];
return Card(
margin: EdgeInsets.symmetric(vertical: 4),
child: ListTile(
leading: FutureBuilder<CachedNetworkImageProvider>(
future: AdvancedCachedNetworkImageProvider.create(
url: url,
retryCount: retryCount,
queueDelay: queueDelay,
),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
width: 60,
height: 60,
child: Image(
image: snapshot.data!,
fit: BoxFit.cover,
),
);
} else if (snapshot.hasError) {
return Container(
width: 60,
height: 60,
child: Icon(Icons.error),
);
}
return Container(
width: 60,
height: 60,
child: CircularProgressIndicator(),
);
},
),
title: Text('Image ${index + 1}'),
subtitle: Text(
url,
style: TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis,
),
trailing: AdvancedCachedNetworkImageProvider.isLoading(url)
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Icon(Icons.check_circle, color: Colors.green),
),
);
},
),
),
],
),
),
),
// Logs
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Logs:',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) {
final log = logs[index];
final isError = log.contains('❌');
final isSuccess = log.contains('✅');
return Container(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: isError
? Colors.red.shade50
: isSuccess
? Colors.green.shade50
: null,
),
child: Text(
log,
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
color: isError
? Colors.red.shade700
: isSuccess
? Colors.green.shade700
: Colors.black87,
),
),
);
},
),
),
),
],
),
),
),
],
),
);
}
}