| 
				
			 | 
			
			
				@@ -0,0 +1,114 @@ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1
			 | 
			
			
				+## Images 
			 | 
		
	
		
			
			| 
				
			 | 
			
				2
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				3
			 | 
			
			
				+> Note that described API is considered experimental and is likely to be 
			 | 
		
	
		
			
			| 
				
			 | 
			
				4
			 | 
			
			
				+> changed in backward incompatible ways. If this happens all changes will be 
			 | 
		
	
		
			
			| 
				
			 | 
			
				5
			 | 
			
			
				+> described in detail in the changelog to simplify upgrading. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				6
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				+Zefyr (and Notus) supports embedded images. In order to handle images in 
			 | 
		
	
		
			
			| 
				
			 | 
			
				8
			 | 
			
			
				+your application you need to implement `ZefyrImageDelegate` interface which 
			 | 
		
	
		
			
			| 
				
			 | 
			
				9
			 | 
			
			
				+looks like this: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				10
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				11
			 | 
			
			
				+```dart 
			 | 
		
	
		
			
			| 
				
			 | 
			
				12
			 | 
			
			
				+abstract class ZefyrImageDelegate<S> { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				13
			 | 
			
			
				+  /// Builds image widget for specified [imageSource] and [context]. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				+  Widget buildImage(BuildContext context, String imageSource); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				15
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				16
			 | 
			
			
				+  /// Picks an image from specified [source]. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				17
			 | 
			
			
				+  /// 
			 | 
		
	
		
			
			| 
				
			 | 
			
				18
			 | 
			
			
				+  /// Returns unique string key for the selected image. Returned key is stored 
			 | 
		
	
		
			
			| 
				
			 | 
			
				19
			 | 
			
			
				+  /// in the document. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				20
			 | 
			
			
				+  Future<String> pickImage(S source); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				21
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				22
			 | 
			
			
				+``` 
			 | 
		
	
		
			
			| 
				
			 | 
			
				23
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				24
			 | 
			
			
				+Zefyr comes with default implementation which exists mostly to provide an 
			 | 
		
	
		
			
			| 
				
			 | 
			
				25
			 | 
			
			
				+example and a starting point for your own version. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				26
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				27
			 | 
			
			
				+It is recommended to always have your own implementation specific to your 
			 | 
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				+application. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				29
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				30
			 | 
			
			
				+### Implementing ZefyrImageDelegate 
			 | 
		
	
		
			
			| 
				
			 | 
			
				31
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				32
			 | 
			
			
				+Let's start from the `pickImage` method: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				33
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				34
			 | 
			
			
				+```dart 
			 | 
		
	
		
			
			| 
				
			 | 
			
				35
			 | 
			
			
				+// Currently Zefyr depends on image_picker plugin to show camera or image gallery. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				36
			 | 
			
			
				+// (note that in future versions this may change so that users can choose their 
			 | 
		
	
		
			
			| 
				
			 | 
			
				37
			 | 
			
			
				+// own plugin and define custom sources) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				38
			 | 
			
			
				+import 'package:image_picker/image_picker.dart'; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				39
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				40
			 | 
			
			
				+class MyAppZefyrImageDelegate implements ZefyrImageDelegate<ImageSource> { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				41
			 | 
			
			
				+  @override 
			 | 
		
	
		
			
			| 
				
			 | 
			
				42
			 | 
			
			
				+  Future<String> pickImage(ImageSource source) async { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				43
			 | 
			
			
				+    final file = await ImagePicker.pickImage(source: source); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				44
			 | 
			
			
				+    if (file == null) return null; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				45
			 | 
			
			
				+    // We simply return the absolute path to selected file. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				46
			 | 
			
			
				+    return file.uri.toString(); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				47
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				48
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				49
			 | 
			
			
				+``` 
			 | 
		
	
		
			
			| 
				
			 | 
			
				50
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				51
			 | 
			
			
				+This method is responsible for initiating image selection flow (either using 
			 | 
		
	
		
			
			| 
				
			 | 
			
				52
			 | 
			
			
				+camera or gallery), handling result of selection and returning a string value 
			 | 
		
	
		
			
			| 
				
			 | 
			
				53
			 | 
			
			
				+which essentially serves as an identifier for the image. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				54
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				55
			 | 
			
			
				+Returned value is stored in the document Delta and later on used to build the 
			 | 
		
	
		
			
			| 
				
			 | 
			
				56
			 | 
			
			
				+appropriate `Widget`. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				57
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				58
			 | 
			
			
				+It is up to the developer to define what this value represents. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				59
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				60
			 | 
			
			
				+In the above example we simply return a full path to the file on user's device, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				61
			 | 
			
			
				+e.g. `file:///Users/something/something/image.jpg`. Some other examples 
			 | 
		
	
		
			
			| 
				
			 | 
			
				62
			 | 
			
			
				+may include a web link, `https://myapp.com/images/some.jpg` or just some 
			 | 
		
	
		
			
			| 
				
			 | 
			
				63
			 | 
			
			
				+arbitrary string like an ID. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				64
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				65
			 | 
			
			
				+For instance, if you upload files to your server you can initiate this task 
			 | 
		
	
		
			
			| 
				
			 | 
			
				66
			 | 
			
			
				+in `pickImage`, for instance: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				67
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				68
			 | 
			
			
				+```dart 
			 | 
		
	
		
			
			| 
				
			 | 
			
				69
			 | 
			
			
				+class MyAppZefyrImageDelegate implements ZefyrImageDelegate<ImageSource> { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				+  final MyFileStorage storage; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				71
			 | 
			
			
				+  MyAppZefyrImageDelegate(this.storage); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				72
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				+  @override 
			 | 
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				+  Future<String> pickImage(ImageSource source) async { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				+    final file = await ImagePicker.pickImage(source: source); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				+    if (file == null) return null; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				+    // Use my storage service to upload selected file. The uploadImage method 
			 | 
		
	
		
			
			| 
				
			 | 
			
				78
			 | 
			
			
				+    // returns unique ID of newly uploaded image on my server. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				+    final String imageId = await storage.uploadImage(file); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				+    return imageId; 
			 | 
		
	
		
			
			| 
				
			 | 
			
				81
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				+``` 
			 | 
		
	
		
			
			| 
				
			 | 
			
				84
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				85
			 | 
			
			
				+Next we need to implement `buildImage`. This method takes `imageSource` argument 
			 | 
		
	
		
			
			| 
				
			 | 
			
				86
			 | 
			
			
				+which contains that same string you returned from `pickImage`. Here you can 
			 | 
		
	
		
			
			| 
				
			 | 
			
				87
			 | 
			
			
				+use this value to create a Flutter `Widget` which renders the image. Normally 
			 | 
		
	
		
			
			| 
				
			 | 
			
				88
			 | 
			
			
				+you would return the standard `Image` widget from this method, but it is not 
			 | 
		
	
		
			
			| 
				
			 | 
			
				89
			 | 
			
			
				+a requirement. You are free to create a custom widget which, for instance, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				+shows progress of upload operation that you initiated in the `pickImage` call. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				+Assuming our first example where we returned full path to the image file on 
			 | 
		
	
		
			
			| 
				
			 | 
			
				93
			 | 
			
			
				+user's device, our `buildImage` method can be as simple as following: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				94
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				95
			 | 
			
			
				+```dart 
			 | 
		
	
		
			
			| 
				
			 | 
			
				96
			 | 
			
			
				+class MyAppZefyrImageDelegate implements ZefyrImageDelegate<ImageSource> { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				97
			 | 
			
			
				+  // ... 
			 | 
		
	
		
			
			| 
				
			 | 
			
				98
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				99
			 | 
			
			
				+  @override 
			 | 
		
	
		
			
			| 
				
			 | 
			
				100
			 | 
			
			
				+  Widget buildImage(BuildContext context, String imageSource) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				101
			 | 
			
			
				+    final file = new File.fromUri(Uri.parse(imageSource)); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				+    /// Create standard [FileImage] provider. If [imageSource] was an HTTP link 
			 | 
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				+    /// we could use [NetworkImage] instead. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				104
			 | 
			
			
				+    final image = new FileImage(file); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				+    return new Image(image: image); 
			 | 
		
	
		
			
			| 
				
			 | 
			
				106
			 | 
			
			
				+  } 
			 | 
		
	
		
			
			| 
				
			 | 
			
				107
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				108
			 | 
			
			
				+``` 
			 | 
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				110
			 | 
			
			
				+### Previous 
			 | 
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				+* [Heuristics][heuristics] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				+[heuristics]: /doc/heuristics.md 
			 |