import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:archive/archive.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'ZIP List',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyPage(),
);
}
}
class Item {
final int id;
String titulo; // title
String descricao; // description
String img;
double nota; // rating
Item({
required this.id,
required this.titulo, // title
required this.descricao, // description
required this.img,
required this.nota, // rating
});
void updateData(Map<String, dynamic> newData) {
titulo = newData['titulo'] ?? titulo; // title
descricao = newData['descricao'] ?? descricao; // description
img = newData['img'] ?? img;
nota = newData['nota']?.toDouble() ?? 0.0; // rating
}
}
class MyPage extends StatefulWidget {
const MyPage({Key? key}) : super(key: key);
@override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
List<Item> itemList = [];
@override
void initState() {
super.initState();
_loadList();
}
Future<void> _loadList() async {
final response = await http.get(
Uri.parse(
'https://raw.githubusercontent.com/SGomes764/database/main/lista/database.json'),
);
if (response.statusCode == 200) {
Map<String, dynamic>? data = json.decode(response.body);
if (data != null && data.containsKey('items')) {
List<dynamic> jsonList = data['items'];
List<Item> tempItemList = [];
for (var jsonItem in jsonList) {
Item item = Item(
id: jsonItem['id'],
titulo: jsonItem['titulo'] ?? 'Click to download', // title
descricao: jsonItem['descricao'] ?? 'No information was found', //description
img: jsonItem['img'],
nota: jsonItem['nota']?.toDouble() ?? 0.0, // rating
);
// Check if the data has already been transferred
if (await _transferredData(item)) {
print('\t\t«----{Using transferred data for ID: ${item.id}}----»\n');
} else {
print('\t\t«----{Using default data for ID: ${item.id}}----»\n');
await _downloadZip(item);
}
tempItemList.add(item);
}
print(
'\t\t«----{Loaded ${tempItemList.length} items from the server.}----»\n');
setState(() {
itemList = tempItemList;
});
} else {
print('\t\t«----{Invalid or missing data from the server.}----»\n');
throw Exception('Invalid or missing data');
}
} else {
print(
'\t\t«----{Failed to load data from the server. Status code: ${response.statusCode}}----»\n');
throw Exception('Failed to load data');
}
}
Future<bool> _transferredData(Item item) async {
// Check if the data for the item has already been transferred
final appDocumentsDir = await getApplicationDocumentsDirectory();
final extractedDir = Directory("${appDocumentsDir.path}/extracted");
final jsonFile = File("${extractedDir.path}/${item.id}.json");
return jsonFile.existsSync();
}
Future<void> _downloadZip(Item item) async {
// Exclude IDs 1 to 3 from the download process
if (item.id >= 1 && item.id <= 3) {
print('\t\t«----{Skipping download for ID: ${item.id}}----»\n');
return;
}
const baseUrl =
'https://raw.githubusercontent.com/SGomes764/database/main/lista/';
final url = '$baseUrl${item.id}.zip';
print('\t\t«----{ZIP URL: $url}----»\n');
try {
var response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
print('\t\t«----{Downloading file for ID card ${item.id}...}----»\n');
// Get the app's documents directory
final appDocumentsDir = await getApplicationDocumentsDirectory();
// Create the target directory for the file downloads
final downloadDir = Directory("${appDocumentsDir.path}/downloads");
await downloadDir.create(recursive: true);
// Save the ZIP file to the downloads directory
var zipFile = File("${downloadDir.path}/${item.id}_file.zip");
await zipFile.writeAsBytes(response.bodyBytes);
print('\t\t«----{File downloaded successfully.}----»\n');
// Extract the ZIP file
await _extractZip(zipFile, item);
// Display a success message after download and extraction
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Download and extraction completed for ID: ${item.id}'),
),
);
print("\t\t«----{File downloaded and extracted successfully.}----»\n");
} else {
print(
"\t\t«----{Failed to download file. Status code: ${response.statusCode}}----»\n");
}
} catch (error) {
print("\t\t«----{Error during file download: $error}----»\n");
}
}
Future<void> _extractZip(File zipFile, Item item) async {
try {
// Get the temporary directory for extraction
Directory tempDir = await getTemporaryDirectory();
// Create the target directory for extraction
Directory extractDir = Directory("${tempDir.path}/extracted");
await extractDir.create();
// Read the contents of the ZIP file
List<int> bytes = await zipFile.readAsBytes();
Archive archive = ZipDecoder().decodeBytes(bytes);
print('\t\t«----{Starting ZIP extraction...}----»\n');
// Extract the content to the target directory
for (ArchiveFile file in archive) {
String fileName = file.name;
String filePath = "${extractDir.path}/$fileName";
if (file.isFile) {
print('\t\t«----{Extracting file: $fileName}----»\n');
File(filePath)
..createSync(recursive: true)
..writeAsBytesSync(file.content);
// Check if the extracted file is a JSON file
if (fileName.endsWith('.json')) {
// Read the content of the JSON file
String jsonContent = await File(filePath).readAsString();
print('File path: $filePath');
Map<String, dynamic> jsonData = json.decode(jsonContent);
// Associate the downloaded JSON data with the corresponding Item
setState(() {
item.updateData(jsonData);
});
// Save the JSON to a file to indicate that the data has been transferred
await File("${extractDir.path}/${item.id}.json")
.writeAsString(jsonContent);
}
}
}
print('\t\t«----{ZIP extraction completed successfully}----»\n');
} catch (error) {
print("\t\t«----{Error during ZIP extraction: $error}----»\n");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Listing'),
),
body: ListView.builder(
itemCount: itemList.length,
itemBuilder: (context, index) {
return _buildItemCard(itemList[index]);
},
),
);
}
Widget _buildItemCard(Item item) {
return GestureDetector(
onTap: () {
print('\t\t«----{Pressed card ID: ${item.id}}---»\n');
_downloadZip(item);
},
child: Card(
margin: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${item.titulo} | ${item.id}', // title | id
style: const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold),
),
Text(
item.descricao, // description
style: const TextStyle(
fontSize: 16.0,
),
),
const SizedBox(
height: 8.0,
),
Text(
'Rating: ${item.nota}',
style:
const TextStyle(fontSize: 14.0, color: Colors.grey),
),
],
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
item.img,
width: 75.0,
height: 75.0,
fit: BoxFit.cover,
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return Image.network(
'https://cdn-icons-png.flaticon.com/512/5064/5064064.png',
width: 75.0,
height: 75.0,
fit: BoxFit.cover,
);
},
),
),
],
),
),
);
}
}
Currently the problem lies in the need to show the data that is in JSON if it has already been transferred, when the app is started the data is presented but there are attempts to download it from the code, which is not intended. Downloads are only intended when the onTap event occurs.
// Check if the data has already been transferred
if (await _transferredData(item)) {
print('\t\t«----{Using transferred data for ID: ${item.id}}----»\n');
} else {
print('\t\t«----{Using default data for ID: ${item.id}}----»\n');
await _downloadZip(item);
}
I already tried to remove the _downloadZip(item) call from this logic but the code did not load the data that was already transferred
Future<bool> _dadosTransferidos(Item item) async {
// Checks whether the data for the item has already been transferred
final appDocumentsDir = await getApplicationDocumentsDirectory();
final extractedDir = Directory("${appDocumentsDir.path}/extracted");
final jsonFile = File("${extractedDir.path}/${item.id}.json");
if (jsonFile.existsSync()) {
// If file exist, load data
String jsonContent = await jsonFile.readAsString();
Map<String, dynamic> jsonData = json.decode(jsonContent);
item.atualizarDados(jsonData);
return true;
}
return false;
}
I also tried to implement functions and logic similar to this and it didn't result in anything, maybe they work but they were poorly implemented by me