I'm trying to display images from Firebase storage
for that I've done as explain in here firebase storage how to display image
What I've done:
Declare a variable
Future<ListResult>? _futureListResult;
Initialize it inside initState
@override
void initState() {
_futureListResult = getInstructorDocument();
super.initState();
}
Create a method to get value for the initialized variable
Future<ListResult> getInstructorDocument() async {
final documentViewModel = Provider.of<DocumentViewModel>(context, listen: false);
ListResult listResult = await documentViewModel.listFile(user!.uid);
return listResult;
}
Here is the full code
DocumentPicker:
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:app_test/provider/document_view_model.dart';
import 'package:path/path.dart' as Path;
import 'package:provider/provider.dart';
import '../constant/color.dart';
import '../constant/text.dart';
import '../model/document.dart';
import '../utils/file_list_controller.dart';
import '../utils/utils.dart';
import 'document_card.dart';
import 'loader.dart';
class DocumentPicker extends StatefulWidget {
const DocumentPicker({Key? key}) : super(key: key);
@override
State<DocumentPicker> createState() => _DocumentPickerState();
}
class _DocumentPickerState extends State<DocumentPicker> {
final photoTitleController = TextEditingController();
File? _file;
User? user = FirebaseAuth.instance.currentUser;
List<Document>? documents = [];
late Document document;
String extension = "";
String destination = "";
final FileListController fileController = FileListController();
UploadTask? uploadTask;
Future<ListResult>? _futureListResult;
@override
void initState() {
_futureListResult = getInstructorDocument();
super.initState();
}
//----------------------------------------------------------------------------------------------
//----------------------------- Free memory allocated to the existing variables ----------------
//----------------------------------------------------------------------------------------------
@override
void dispose() {
photoTitleController.dispose();
fileController.controller.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
String fileName = _file != null ? Path.basename(_file!.path) : noFileSelectedText;
print("Doc list 1: $documents");
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//----------------------------- Pick file from device --------------
TextButton.icon(
onPressed: () {
_clickOnAddFile();
},
icon: const Icon(
Icons.attach_file,
size: 30,
color: googleButtonTextColor,
),
label: const Text(
addFile,
style: TextStyle(
fontSize: 18,
color: googleButtonTextColor,
),
),
),
//----------------------------- Validate picked file ---------------
TextButton.icon(
onPressed: () async {
await _clickOnValidate();
_clearPhotoTitleTextField();
},
icon: const Icon(
Icons.cloud_upload,
size: 30,
color: googleButtonTextColor,
),
label: const Text(
upload,
style: TextStyle(
fontSize: 18,
color: googleButtonTextColor,
),
),
),
],
),
//----------------------------- Picked file default name ---------------
Row(
children: [
Expanded(
child: Text(
fileName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: black,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
uploadTask != null
? loadingStatus(uploadTask!)
: Container(),
],
),
const SizedBox(height: 10,),
//----------------------------- Picked file title ----------------------
TextField(
controller: photoTitleController,
cursorColor: cursorColor,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.edit,
color: grey,
size: 30,
),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(5.5),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.orange),
borderRadius: BorderRadius.circular(5.5),
),
labelText: fileTitleText,
),
),
const SizedBox(height: 10,),
//----------------------------- Show list of picked file ---------------
FutureBuilder(
future: _futureListResult,//documentViewModel.listFile(user!.uid),
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.items.length,
itemBuilder: (context, index) {
final photo = snapshot.data!.items[index].getDownloadURL(); // <---------- Problem with this
final photoName = snapshot.data!.items[index].name;
print("Photo: $photo");
print("PhotoName: $photoName");
return Row(
children: [
Container(
width: 100,
height: 100,
child: Image.network(
fit: BoxFit.cover,
photo.toString(), // <--------------------------------------
/*errorBuilder: (context, exception, stackTrace) {
return Container(
color: grey,
width: 100,
height: 100,
child: const Center(
child: Text(
errorLoadImage,
textAlign: TextAlign.center,
),
),
);
},*/
),
),
const SizedBox(width: 20,),
Container(
width: 50,
height: 50,
child: Text(
/*snapshot.data!.items[index].name.contains(".jpeg")
|| snapshot.data!.items[index].name.contains(".jpg")
|| snapshot.data!.items[index].name.contains(".png")
|| snapshot.data!.items[index].name.contains(".pdf")
? ""
: */photoName,
),
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
),
],
);
}
//----------------------------------------------------------------------------
//----------------------------- Get instructor document ----------------------
//----------------------------------------------------------------------------
Future<ListResult> getInstructorDocument() async {
final documentViewModel = Provider.of<DocumentViewModel>(context, listen: false);
ListResult listResult = await documentViewModel.listFile(user!.uid);
return listResult;
}
//----------------------------------------------------------------------------
//----------------------------- Pick file from device ------------------------
//----------------------------------------------------------------------------
Future _clickOnAddFile() async{
final result = await FilePicker.platform.pickFiles(
allowMultiple: false,
type: FileType.custom,
allowedExtensions: ["jpg", "jpeg", "png", "pdf"],
);
if(result == null) return;
final path = result.files.single.path!;
extension = result.files.first.extension!;
setState(() => _file = File(path));
}
//----------------------------------------------------------------------------
//----------------------------- Validate picked file -------------------------
//----------------------------------------------------------------------------
Future _clickOnValidate() async {
if(_file == null) return;
final defaultName = Path.basename(_file!.path);
final fileName = photoTitleController.text.trim();
destination = "instructorDocuments/${user!.uid}/$fileName";
document = Document(_file, fileName);
documents?.add(document);
fileController.inputFileList.add(documents);
if(fileName.isEmpty) {
destination = "instructorDocuments/${user!.uid}/$defaultName";
uploadTask = DocumentViewModel.uploadInstructorDocument(destination, _file!);
setState((){});
}
else {
uploadTask = DocumentViewModel.uploadInstructorDocument(destination, _file!);
setState((){});
}
}
//----------------------------------------------------------------------------
//----------------------------- Clear photo title text field -----------------
//----------------------------------------------------------------------------
_clearPhotoTitleTextField() {
photoTitleController.clear();
}
//----------------------------------------------------------------------------
//----------------------------- Loading status percentage --------------------
//----------------------------------------------------------------------------
Widget loadingStatus(UploadTask uploadTask) => StreamBuilder<TaskSnapshot>(
stream: uploadTask.snapshotEvents,
builder: (context, snapshot) {
if(snapshot.hasData){
final snap = snapshot.data;
final progress = snap!.bytesTransferred / snap.totalBytes;
final percentage = (progress * 100).toStringAsFixed(2);
return Text(
"$percentage %",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
);
}
else {
return Container();
}
},
);
}
DocumentViewModel:
import 'dart:io';
import 'dart:typed_data';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:app_test/utils/utils.dart';
class DocumentViewModel extends ChangeNotifier {
//----------------------------------------------------------------------------
//----------------------------- Upload instructor document -------------------
//----------------------------------------------------------------------------
static UploadTask? uploadInstructorDocument(String destination, File file) {
try {
final reference = FirebaseStorage.instance
.ref(destination);
return reference.putFile(file);
} on FirebaseException catch (e) {
return null;
}
}
//----------------------------------------------------------------------------
//----------------------------- Get instructor document ----------------------
//----------------------------------------------------------------------------
Future<ListResult> listFile(String uid) async {
try {
ListResult result = await FirebaseStorage.instance
.ref("instructorDocuments/")
.child("$uid/")
.listAll();
return result;
} on FirebaseException catch (e) {
return Utils.showErrorMessage(e.message);
}
}
//----------------------------------------------------------------------------
//----------------------------- Upload instructor document bytes -------------
//----------------------------------------------------------------------------
static UploadTask? uploadBytes(String destination, Uint8List data) {
try {
final reference = FirebaseStorage.instance
.ref(destination);
return reference.putData(data);
} on FirebaseException catch (e) {
return null;
}
}
}
What I've tried:
Try #1: Replace with a valid photo Url
Image.network(
fit: BoxFit.cover,
"https://www.whysoseriousredux.com/suspects/joker.jpg", // <------------------------- with this it works
),
It means photo
is the problem
Try #2: Use a break point to see what kind of data variable will show
final photo = snapshot.data!.items[index].getDownloadURL(); // photo: _Future
final photoName = snapshot.data!.items[index].name; // photoName: "Joker 1"
This confirms that photo
is the problem
What am I missing?
Thanks in advance
CodePudding user response:
If we decode Instance of 'Future
it becomes Instance of 'Future<String>'
. So somewhere in the code you are not calling await
on a Future
but rather .toString()
implicitly or explicitly. And you write yourself:
final photo = snapshot.data!.items[index].getDownloadURL(); // photo: _Future
There you must add await
like this:
final photo = await snapshot.data!.items[index].getDownloadURL(); // photo: _Future
CodePudding user response:
@Frank van Puffelen
FutureBuilder
inside a FutureBuilder
was the solution
Thanks for the help. Finally solved the problem with this.
FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.items.length,
itemBuilder: (context, index) {
final photo = snapshot.data!.items[index].getDownloadURL();
final photoName = snapshot.data!.items[index].name;
return Row(
children: [
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data;
return Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
image!,
);
}
return Container();
},
),
const SizedBox(
width: 20,
),
Text(
photoName,
style: const TextStyle(
fontSize: 16,
color: black,
),
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if (snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
),