-
Notifications
You must be signed in to change notification settings - Fork 550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some Questions & Requests #14
Comments
For the first issue, I don't have good documentation yet but you kind find an example in the example app. Specifically this bit here: zefyr/packages/zefyr/example/lib/main.dart Lines 114 to 124 in 8598fb5
This API is still experimental but it should be flexible enough to handle custom workflows for managing image assets. For Firebase you'd probably need a delegate class similar to this: class FirebaseStorageImageDelegate extends ZefyrDefaultImageDelegate {
final FirebaseStorage storage;
FirebaseStorageImageDelegate(this.storage);
@override
Future<String> pickImage(ImageSource source) async {
// Default delegate uses standard image_picker plugin to choose images
final imagePath = await super.pickImage(source);
// Create file and upload to Firestore
final file = new File.fromUri(Uri.parse(imagePath));
final ref = storage.ref().child('unique-filename.png');
await ref.putFile(file);
// Return path to the Firestore Storage object. This value will be passed
// to createImageProvider function below which is responsible for
// resolving this value into an instance of ImageProvider: FileImage,
// NetworkImage, etc.
return ref.path;
/// You can also define a custom scheme if you expect to use multiple
/// mechanisms to store images. So for Firestore it could be:
///
/// return "firestore://${ref.path}";
}
@override
ImageProvider createImageProvider(String imageSource) {
// Convert imageSource to StorageReference
final ref = storage.ref().child(imageSource);
return createFirestoreImageProvider(ref);
}
ImageProvider createFirestoreImageProvider(StorageReference ref) {
// We can't use standard NetworkImage provider here because
// ref.getDownloadUrl() is asynchronous.
// TODO: implement
}
} I have not used FirebaseStorage yet so above example is very much pseudo-code, but I hope it provides enough details on how this can be handled.
It should normally expand if inside of an
This all should be configurable with ZefyrThemeData, default TextStyle for paragraphs in Zefyr is defined in Theming is not very well document yet and I have this on my list, just need to find time for it.
This makes sense to me. Curious about your use case for not having a ScrollView there. I suspect you'd still have it inside a scrollable anyway? Note that you can make the editor read-only by setting P.S: If you have more questions, please create separate issue for each question or suggestion. It would be easier to keep unrelated conversations separate and will allow me to help you more effectively. |
Closing this issue. Feel free to submit a new one if you have more questions, |
I had this implemented well, was working until the 0.2.0 update that broke the createImageProvider line, and I had to replace it with the buildImage method that returns a widget instead of ImageProvider. So here's the updated code replacement that should work, in case anybody else needs it:
|
Yes, there is a breaking change in 0.2.0. Make sure to always check changelog for updates, all breaking changes are announced there. There is also a documentation page describing embedded images which should help migrating or implementing. |
@Skquark i try above code for Firebase Storage image but getting this error : Image provider: NetworkImage("Instance of 'Future'", scale: 1.0) This error has gone when i change below code : class CustomImageDelegate extends ZefyrImageDelegate { @OverRide @OverRide
} Future getImageUrl(StorageTaskSnapshot snapshot) { @OverRide |
@Skquark Hi, hope you're doing okay. I used your code. It's awesome. But, how can I upload the photos after sometime, not exactly when i selected the pictures. I mean, It would cause unnecessarary uploads, as the user can also choose the wrong picture. An example would be uploading on a button click |
That's a good point, what I did in my app was whenever I deleted a post/item or cancel, I would search the description text for the firestore image embedded then delete the refs from the server. Here's the code that worked for me: void deleteImages(Delta delta) {
for (var t in delta.toList()) {
if (t.data.startsWith('firestore://')) {
String refPath = t.data.replaceFirst('firestore://', '');
final ref = FirebaseStorage.instance.ref().child(refPath);
ref.delete();
}
}
}
void deleteImagesNotus(NotusDocument document) => deleteImages(document.toDelta()); That does the trick, but you make a point where when you're editing the rich text and upload an image, then delete it in the editor without saving, that junk image still gets uploaded without cleanup. I'm not actually sure how I'd get a callback from the ZepherEditor when a media block gets removed so we can delete upload. class CustomImageDelegate implements ZefyrImageDelegate<ImageSource> {
@override
Future<String> pickImage(ImageSource source) async {
final picker = ImagePicker();
final file = await picker.getImage(source: source);
if (file == null) return null;
final ref = FirebaseStorage.instance.ref().child("images").child(file.path);
UploadTask fileUpload = ref.putFile(File(file.path));
await fileUpload.then((upload) async {
String url = await upload.ref.getDownloadURL();
print("Uploaded: " + url);
});
return "firestore://${ref.fullPath}";
}
@override
Widget buildImage(BuildContext context, String key) {
if (key.startsWith('asset://')) {
return Image.asset(key.replaceFirst('asset://', ''));
} else if (key.startsWith('firestore://')) {
key = key.replaceFirst('firestore://', '');
final ref = FirebaseStorage.instance.ref().child(key);
return FutureBuilder<Widget>(
future: createFirestoreImageWidget(context, ref),
initialData: Container(),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
if (snapshot == null) return Container();
if (!snapshot.hasData) return Container();
return snapshot.data;
},
);
} else {
print("returning createImageProvider which is now with buildImage " + key);
if (key.startsWith('http://') || key.startsWith('https://')) return cachedNetworkImage(key);
return null;
}
}
@override
ImageSource get cameraSource => ImageSource.camera;
@override
ImageSource get gallerySource => ImageSource.gallery;
Future<Widget> createFirestoreImageWidget(BuildContext context, Reference ref) async {
String downloadUrl = await ref.getDownloadURL();
return GestureDetector(
child: cachedNetworkImage(downloadUrl),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullScreenWrapper(
imageProvider: NetworkImage(downloadUrl),
maxScale: PhotoViewComputedScale.covered * 8,
minScale: 0.3,
loadingChild: CircularProgressIndicator(),
),
),
);
},
);
}
} Hope that works and is helpful. Note that in my version I'm using CachedNetworkImage for efficiency and made the image expand to full screen when tapped, but adapt it to your own needs. |
First off, big thanks for this package, much needed, well implemented and very capable. I got it integrated into my app easily enough, just a few minor hickups when coding it in, but got past most of the tricky bits. Just a few things I'm trying to work out now... When embedding an image, it's saved there with {"embed":{"type":"image","source":"file:///... but naturally I needed to take the local file and upload it to server (I'm personally using Firebase Cloud Storage, but may also push to an ftp with Dio or something) to make it shared. Thought there would of been a built in way to do it, but I ended up writing a method to search through the Delta List for the embed attribute, look for the source that starts with file:/// then clean the path, upload the file, wait for complete, get the new image url, make the source attribute = url as an https://...jpg and I'd be good to go. After going through all that and getting the updated delta in place correctly, I end up with this exception:
So either I'm missing a trick in the way to do a network embed source, or the ability is not in there yet and it's only meant for local files.. Suggestions?
Next thing was, I've got a place where I was stacking up a list of dynamic Delta descriptions from the database for viewing only, didn't need the extra controllers and focusnode stuff but got it in there anyways because I had to. So I got it mostly working, but a few problems I could use help with.. One is I wanted to height to be Flexible/Expaded to fit like I have the Text component, but I could only get it working with a fixed height in the parent container. No matter how I experimented with it, I couldn't make it sized dynamic. I hope there's a way to do it, just couldn't find it.
I also wanted to change the line-spacing, the fontFamily, fontSize, color like we have in the standard Text components, but didn't see any method to change the text style. I expected to see those options as part of the ZefyrThemeData class or ZefyrEditor properties, but wasn't there, I could live with those limitations in the editor, but where I'm displaying it, just doesn't look right..
What I would propose is a separate ZefyrViewer component without the extras in the Editor, but with more options for displaying and a flexible height without the ScrollView in it. I'm pretty sure most people using this would find that useful... There were more little things I was gonna bring up, but probably asking too much already. Thanks guys, much appreciated, looking great so far...
The text was updated successfully, but these errors were encountered: