Drawing over an image downloaded from remote server











up vote
2
down vote

favorite












I need to do the following:




  1. Download a PNG resource from a server

  2. Draw several rectangles over that image with different color depending on state

  3. Display that image in Zoomable Image View


I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.



Here is the code that downloads the resource:



static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");

if (await file.exists()) {
// Returns the cached file
} else {
final response = await http.get(url);

if (response.statusCode >= 200 && response.statusCode < 300) {
await file.writeAsBytes(response.bodyBytes);
} else {
return null;
}
}
return file;
}


What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.



EDIT:
Below is the Android code equivalent that I would like to implement in Flutter.



// Here we have a bitmap from a file
Bitmap mapBitmap = getBitmap();

Canvas mapCanvas = new Canvas(mapBitmap);

mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
mapDrawable.draw(mapCanvas);

canvasWidth = mapCanvas.getWidth();
canvasHeight = mapCanvas.getHeight();

Paint paint = new Paint();
for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
MapObject mapObject = entry.getValue();
paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
paint.setAlpha(100);
mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
(int) (mapObject.getPosY() * scaleY),
(int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
(int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
paint);
}

photoView.setImageBitmap(mapBitmap);









share|improve this question
























  • use Image.network() constructor
    – pskink
    2 days ago












  • I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
    – RMK
    2 days ago










  • so use CachedNetworkImageProvider from here and call its resolve method
    – pskink
    2 days ago















up vote
2
down vote

favorite












I need to do the following:




  1. Download a PNG resource from a server

  2. Draw several rectangles over that image with different color depending on state

  3. Display that image in Zoomable Image View


I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.



Here is the code that downloads the resource:



static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");

if (await file.exists()) {
// Returns the cached file
} else {
final response = await http.get(url);

if (response.statusCode >= 200 && response.statusCode < 300) {
await file.writeAsBytes(response.bodyBytes);
} else {
return null;
}
}
return file;
}


What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.



EDIT:
Below is the Android code equivalent that I would like to implement in Flutter.



// Here we have a bitmap from a file
Bitmap mapBitmap = getBitmap();

Canvas mapCanvas = new Canvas(mapBitmap);

mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
mapDrawable.draw(mapCanvas);

canvasWidth = mapCanvas.getWidth();
canvasHeight = mapCanvas.getHeight();

Paint paint = new Paint();
for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
MapObject mapObject = entry.getValue();
paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
paint.setAlpha(100);
mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
(int) (mapObject.getPosY() * scaleY),
(int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
(int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
paint);
}

photoView.setImageBitmap(mapBitmap);









share|improve this question
























  • use Image.network() constructor
    – pskink
    2 days ago












  • I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
    – RMK
    2 days ago










  • so use CachedNetworkImageProvider from here and call its resolve method
    – pskink
    2 days ago













up vote
2
down vote

favorite









up vote
2
down vote

favorite











I need to do the following:




  1. Download a PNG resource from a server

  2. Draw several rectangles over that image with different color depending on state

  3. Display that image in Zoomable Image View


I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.



Here is the code that downloads the resource:



static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");

if (await file.exists()) {
// Returns the cached file
} else {
final response = await http.get(url);

if (response.statusCode >= 200 && response.statusCode < 300) {
await file.writeAsBytes(response.bodyBytes);
} else {
return null;
}
}
return file;
}


What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.



EDIT:
Below is the Android code equivalent that I would like to implement in Flutter.



// Here we have a bitmap from a file
Bitmap mapBitmap = getBitmap();

Canvas mapCanvas = new Canvas(mapBitmap);

mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
mapDrawable.draw(mapCanvas);

canvasWidth = mapCanvas.getWidth();
canvasHeight = mapCanvas.getHeight();

Paint paint = new Paint();
for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
MapObject mapObject = entry.getValue();
paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
paint.setAlpha(100);
mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
(int) (mapObject.getPosY() * scaleY),
(int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
(int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
paint);
}

photoView.setImageBitmap(mapBitmap);









share|improve this question















I need to do the following:




  1. Download a PNG resource from a server

  2. Draw several rectangles over that image with different color depending on state

  3. Display that image in Zoomable Image View


I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.



Here is the code that downloads the resource:



static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");

if (await file.exists()) {
// Returns the cached file
} else {
final response = await http.get(url);

if (response.statusCode >= 200 && response.statusCode < 300) {
await file.writeAsBytes(response.bodyBytes);
} else {
return null;
}
}
return file;
}


What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.



EDIT:
Below is the Android code equivalent that I would like to implement in Flutter.



// Here we have a bitmap from a file
Bitmap mapBitmap = getBitmap();

Canvas mapCanvas = new Canvas(mapBitmap);

mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
mapDrawable.draw(mapCanvas);

canvasWidth = mapCanvas.getWidth();
canvasHeight = mapCanvas.getHeight();

Paint paint = new Paint();
for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
MapObject mapObject = entry.getValue();
paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
paint.setAlpha(100);
mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
(int) (mapObject.getPosY() * scaleY),
(int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
(int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
paint);
}

photoView.setImageBitmap(mapBitmap);






dart flutter






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 2 days ago

























asked 2 days ago









RMK

632510




632510












  • use Image.network() constructor
    – pskink
    2 days ago












  • I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
    – RMK
    2 days ago










  • so use CachedNetworkImageProvider from here and call its resolve method
    – pskink
    2 days ago


















  • use Image.network() constructor
    – pskink
    2 days ago












  • I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
    – RMK
    2 days ago










  • so use CachedNetworkImageProvider from here and call its resolve method
    – pskink
    2 days ago
















use Image.network() constructor
– pskink
2 days ago






use Image.network() constructor
– pskink
2 days ago














I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
– RMK
2 days ago




I saw that constructor, but I need to cache the file and also draw over it so I need to get access to the canvas.
– RMK
2 days ago












so use CachedNetworkImageProvider from here and call its resolve method
– pskink
2 days ago




so use CachedNetworkImageProvider from here and call its resolve method
– pskink
2 days ago












2 Answers
2






active

oldest

votes

















up vote
0
down vote













In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.



To draw above the Image I would simply suggest using the Stack Widget.



If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.



Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.



The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.



import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Network Image Download',
theme: ThemeData(),
home: MainPage(),
);
}
}

class MainPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => MainPageState();
}

class MainPageState extends State<MainPage> {
ImageProvider provider;
bool loaded;
bool error;

@override
void initState() {
super.initState();

loaded = false;
error = false;
provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');

provider.resolve(ImageConfiguration()).addListener((_, __) {
setState(() {
loaded = true;
});
}, onError: (_, __) {
setState(() {
error = true;
});
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Image(image: provider),
Container(
width: 75.0,
height: 75.0,
color: colorByState(),
)
],
),
),
);
}

Color colorByState() {
if (error) {
return Colors.red;
} else if (loaded) {
return Colors.green;
} else {
return Colors.yellow;
}
}
}





share|improve this answer





















  • Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
    – RMK
    2 days ago










  • I edited my question and attached Android code that I want to port to Flutter
    – RMK
    2 days ago










  • The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
    – Niklas
    2 days ago










  • Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
    – RMK
    2 days ago










  • You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
    – Niklas
    2 days ago




















up vote
0
down vote













I finally managed to solve the issue!



I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).



The renderer:



class MapRenderer {
ui.Image _mapBackgroundImage;

Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
await _loadMapBackground(url);
var renderedMapImage = await _updateSensors(sensors);
var byteD = await renderedMapImage.toByteData(
format: ui.ImageByteFormat.png);
return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
}


Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas c = Canvas(recorder);

var paint = ui.Paint();
c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

for (Sensor s in sensors) {
paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
.npSensorOccupied);
c.drawRect(
ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
ui.Offset(s.posX + s.width, s.posY + s.height)),
paint,
);
}

return recorder
.endRecording()
.toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
}

Future<void> _loadMapBackground(String url) async {
var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

if (imageBytes != null) {
_mapBackgroundImage = await _getImageFromBytes(imageBytes);
} else {
return null;
}
}

Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
var imageCodec = await ui.instantiateImageCodec(bytes);
var frame = await imageCodec.getNextFrame();
return frame.image;
}

Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("${directory.path}/${_getSHA(url)}.png");

if (await file.exists()) {
return await file.readAsBytes();
} else {
Uint8List resourceBytes = await _loadFromUrl(url);

if (resourceBytes != null) {
await file.writeAsBytes(resourceBytes);
return resourceBytes;
} else {
return null;
}
}
}

Future<Uint8List> _loadFromUrl(String url) async {
final response = await http.get(url);

if (response.statusCode >= 200 && response.statusCode < 300) {
return response.bodyBytes;
} else {
return null;
}
}

String _getSHA(String sth) {
var bytes = utf8.encode(sth);
var digest = sha1.convert(bytes);

return digest.toString();
}

void dispose() {
_mapBackgroundImage.dispose();
}
}


And to supply the image to the ZoomableImage I created a custom ImageProvider:



class MapImageProvider extends ImageProvider<MapImageProvider> {
final String url;
final List<Sensor> sensors;

final MapRenderer mapRenderer = MapRenderer();

MapImageProvider(this.url, this.sensors);

@override
ImageStreamCompleter load(MapImageProvider key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key),
scale: 1.0,
informationCollector: (StringBuffer information) {
information.writeln('Image provider: $this');
information.write('Image key: $key');
});
}

Future<ui.Codec> _loadAsync(MapImageProvider key) async {
assert(key == this);

return await mapRenderer.renderMap(url, sensors);
}

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MapImageProvider &&
runtimeType == other.runtimeType &&
url == other.url;

@override
int get hashCode => url.hashCode;

@override
String toString() => '$runtimeType("$url")';

@override
Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<MapImageProvider>(this);
}
}


If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53203904%2fdrawing-over-an-image-downloaded-from-remote-server%23new-answer', 'question_page');
    }
    );

    Post as a guest
































    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.



    To draw above the Image I would simply suggest using the Stack Widget.



    If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.



    Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.



    The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.



    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    title: 'Network Image Download',
    theme: ThemeData(),
    home: MainPage(),
    );
    }
    }

    class MainPage extends StatefulWidget {
    @override
    State<StatefulWidget> createState() => MainPageState();
    }

    class MainPageState extends State<MainPage> {
    ImageProvider provider;
    bool loaded;
    bool error;

    @override
    void initState() {
    super.initState();

    loaded = false;
    error = false;
    provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');

    provider.resolve(ImageConfiguration()).addListener((_, __) {
    setState(() {
    loaded = true;
    });
    }, onError: (_, __) {
    setState(() {
    error = true;
    });
    });
    }

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    body: Center(
    child: Stack(
    alignment: Alignment.center,
    children: <Widget>[
    Image(image: provider),
    Container(
    width: 75.0,
    height: 75.0,
    color: colorByState(),
    )
    ],
    ),
    ),
    );
    }

    Color colorByState() {
    if (error) {
    return Colors.red;
    } else if (loaded) {
    return Colors.green;
    } else {
    return Colors.yellow;
    }
    }
    }





    share|improve this answer





















    • Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
      – RMK
      2 days ago










    • I edited my question and attached Android code that I want to port to Flutter
      – RMK
      2 days ago










    • The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
      – Niklas
      2 days ago










    • Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
      – RMK
      2 days ago










    • You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
      – Niklas
      2 days ago

















    up vote
    0
    down vote













    In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.



    To draw above the Image I would simply suggest using the Stack Widget.



    If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.



    Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.



    The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.



    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    title: 'Network Image Download',
    theme: ThemeData(),
    home: MainPage(),
    );
    }
    }

    class MainPage extends StatefulWidget {
    @override
    State<StatefulWidget> createState() => MainPageState();
    }

    class MainPageState extends State<MainPage> {
    ImageProvider provider;
    bool loaded;
    bool error;

    @override
    void initState() {
    super.initState();

    loaded = false;
    error = false;
    provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');

    provider.resolve(ImageConfiguration()).addListener((_, __) {
    setState(() {
    loaded = true;
    });
    }, onError: (_, __) {
    setState(() {
    error = true;
    });
    });
    }

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    body: Center(
    child: Stack(
    alignment: Alignment.center,
    children: <Widget>[
    Image(image: provider),
    Container(
    width: 75.0,
    height: 75.0,
    color: colorByState(),
    )
    ],
    ),
    ),
    );
    }

    Color colorByState() {
    if (error) {
    return Colors.red;
    } else if (loaded) {
    return Colors.green;
    } else {
    return Colors.yellow;
    }
    }
    }





    share|improve this answer





















    • Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
      – RMK
      2 days ago










    • I edited my question and attached Android code that I want to port to Flutter
      – RMK
      2 days ago










    • The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
      – Niklas
      2 days ago










    • Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
      – RMK
      2 days ago










    • You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
      – Niklas
      2 days ago















    up vote
    0
    down vote










    up vote
    0
    down vote









    In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.



    To draw above the Image I would simply suggest using the Stack Widget.



    If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.



    Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.



    The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.



    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    title: 'Network Image Download',
    theme: ThemeData(),
    home: MainPage(),
    );
    }
    }

    class MainPage extends StatefulWidget {
    @override
    State<StatefulWidget> createState() => MainPageState();
    }

    class MainPageState extends State<MainPage> {
    ImageProvider provider;
    bool loaded;
    bool error;

    @override
    void initState() {
    super.initState();

    loaded = false;
    error = false;
    provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');

    provider.resolve(ImageConfiguration()).addListener((_, __) {
    setState(() {
    loaded = true;
    });
    }, onError: (_, __) {
    setState(() {
    error = true;
    });
    });
    }

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    body: Center(
    child: Stack(
    alignment: Alignment.center,
    children: <Widget>[
    Image(image: provider),
    Container(
    width: 75.0,
    height: 75.0,
    color: colorByState(),
    )
    ],
    ),
    ),
    );
    }

    Color colorByState() {
    if (error) {
    return Colors.red;
    } else if (loaded) {
    return Colors.green;
    } else {
    return Colors.yellow;
    }
    }
    }





    share|improve this answer












    In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.



    To draw above the Image I would simply suggest using the Stack Widget.



    If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.



    Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.



    The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.



    import 'package:flutter/material.dart';

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
    return MaterialApp(
    title: 'Network Image Download',
    theme: ThemeData(),
    home: MainPage(),
    );
    }
    }

    class MainPage extends StatefulWidget {
    @override
    State<StatefulWidget> createState() => MainPageState();
    }

    class MainPageState extends State<MainPage> {
    ImageProvider provider;
    bool loaded;
    bool error;

    @override
    void initState() {
    super.initState();

    loaded = false;
    error = false;
    provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');

    provider.resolve(ImageConfiguration()).addListener((_, __) {
    setState(() {
    loaded = true;
    });
    }, onError: (_, __) {
    setState(() {
    error = true;
    });
    });
    }

    @override
    Widget build(BuildContext context) {
    return Scaffold(
    body: Center(
    child: Stack(
    alignment: Alignment.center,
    children: <Widget>[
    Image(image: provider),
    Container(
    width: 75.0,
    height: 75.0,
    color: colorByState(),
    )
    ],
    ),
    ),
    );
    }

    Color colorByState() {
    if (error) {
    return Colors.red;
    } else if (loaded) {
    return Colors.green;
    } else {
    return Colors.yellow;
    }
    }
    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered 2 days ago









    Niklas

    1255




    1255












    • Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
      – RMK
      2 days ago










    • I edited my question and attached Android code that I want to port to Flutter
      – RMK
      2 days ago










    • The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
      – Niklas
      2 days ago










    • Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
      – RMK
      2 days ago










    • You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
      – Niklas
      2 days ago




















    • Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
      – RMK
      2 days ago










    • I edited my question and attached Android code that I want to port to Flutter
      – RMK
      2 days ago










    • The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
      – Niklas
      2 days ago










    • Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
      – RMK
      2 days ago










    • You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
      – Niklas
      2 days ago


















    Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
    – RMK
    2 days ago




    Thank you for reply, It will be useful to know that. What I need is to draw over the Image that is already loaded. So let's say I have a PNG image of a square and depending on data from REST API I need to draw a rectangle over it with different color.
    – RMK
    2 days ago












    I edited my question and attached Android code that I want to port to Flutter
    – RMK
    2 days ago




    I edited my question and attached Android code that I want to port to Flutter
    – RMK
    2 days ago












    The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
    – Niklas
    2 days ago




    The Stack renders the rectangles over the Imageit should be what you wanted in first place? It justs depends on the loading state and not on the state of the REST API. Adjusting the rectangle sizes etc. should'nt be the problem?
    – Niklas
    2 days ago












    Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
    – RMK
    2 days ago




    Oh, now I see what you mean. But how the zoomable image view will behave in case of stack? How to make the rectangles stay above the correct position of an image?
    – RMK
    2 days ago












    You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
    – Niklas
    2 days ago






    You can surround the rectangle with an IgnorePointer Widget, which will let the hits pass through. Scaling both, the rectangle and the image, should not be a big problem.
    – Niklas
    2 days ago














    up vote
    0
    down vote













    I finally managed to solve the issue!



    I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).



    The renderer:



    class MapRenderer {
    ui.Image _mapBackgroundImage;

    Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
    await _loadMapBackground(url);
    var renderedMapImage = await _updateSensors(sensors);
    var byteD = await renderedMapImage.toByteData(
    format: ui.ImageByteFormat.png);
    return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
    }


    Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
    ui.PictureRecorder recorder = ui.PictureRecorder();
    Canvas c = Canvas(recorder);

    var paint = ui.Paint();
    c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

    for (Sensor s in sensors) {
    paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
    .npSensorOccupied);
    c.drawRect(
    ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
    ui.Offset(s.posX + s.width, s.posY + s.height)),
    paint,
    );
    }

    return recorder
    .endRecording()
    .toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
    }

    Future<void> _loadMapBackground(String url) async {
    var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

    if (imageBytes != null) {
    _mapBackgroundImage = await _getImageFromBytes(imageBytes);
    } else {
    return null;
    }
    }

    Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
    var imageCodec = await ui.instantiateImageCodec(bytes);
    var frame = await imageCodec.getNextFrame();
    return frame.image;
    }

    Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
    final directory = await getApplicationDocumentsDirectory();
    final file = File("${directory.path}/${_getSHA(url)}.png");

    if (await file.exists()) {
    return await file.readAsBytes();
    } else {
    Uint8List resourceBytes = await _loadFromUrl(url);

    if (resourceBytes != null) {
    await file.writeAsBytes(resourceBytes);
    return resourceBytes;
    } else {
    return null;
    }
    }
    }

    Future<Uint8List> _loadFromUrl(String url) async {
    final response = await http.get(url);

    if (response.statusCode >= 200 && response.statusCode < 300) {
    return response.bodyBytes;
    } else {
    return null;
    }
    }

    String _getSHA(String sth) {
    var bytes = utf8.encode(sth);
    var digest = sha1.convert(bytes);

    return digest.toString();
    }

    void dispose() {
    _mapBackgroundImage.dispose();
    }
    }


    And to supply the image to the ZoomableImage I created a custom ImageProvider:



    class MapImageProvider extends ImageProvider<MapImageProvider> {
    final String url;
    final List<Sensor> sensors;

    final MapRenderer mapRenderer = MapRenderer();

    MapImageProvider(this.url, this.sensors);

    @override
    ImageStreamCompleter load(MapImageProvider key) {
    return MultiFrameImageStreamCompleter(
    codec: _loadAsync(key),
    scale: 1.0,
    informationCollector: (StringBuffer information) {
    information.writeln('Image provider: $this');
    information.write('Image key: $key');
    });
    }

    Future<ui.Codec> _loadAsync(MapImageProvider key) async {
    assert(key == this);

    return await mapRenderer.renderMap(url, sensors);
    }

    @override
    bool operator ==(Object other) =>
    identical(this, other) ||
    other is MapImageProvider &&
    runtimeType == other.runtimeType &&
    url == other.url;

    @override
    int get hashCode => url.hashCode;

    @override
    String toString() => '$runtimeType("$url")';

    @override
    Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<MapImageProvider>(this);
    }
    }


    If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).






    share|improve this answer

























      up vote
      0
      down vote













      I finally managed to solve the issue!



      I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).



      The renderer:



      class MapRenderer {
      ui.Image _mapBackgroundImage;

      Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
      await _loadMapBackground(url);
      var renderedMapImage = await _updateSensors(sensors);
      var byteD = await renderedMapImage.toByteData(
      format: ui.ImageByteFormat.png);
      return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
      }


      Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
      ui.PictureRecorder recorder = ui.PictureRecorder();
      Canvas c = Canvas(recorder);

      var paint = ui.Paint();
      c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

      for (Sensor s in sensors) {
      paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
      .npSensorOccupied);
      c.drawRect(
      ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
      ui.Offset(s.posX + s.width, s.posY + s.height)),
      paint,
      );
      }

      return recorder
      .endRecording()
      .toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
      }

      Future<void> _loadMapBackground(String url) async {
      var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

      if (imageBytes != null) {
      _mapBackgroundImage = await _getImageFromBytes(imageBytes);
      } else {
      return null;
      }
      }

      Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
      var imageCodec = await ui.instantiateImageCodec(bytes);
      var frame = await imageCodec.getNextFrame();
      return frame.image;
      }

      Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
      final directory = await getApplicationDocumentsDirectory();
      final file = File("${directory.path}/${_getSHA(url)}.png");

      if (await file.exists()) {
      return await file.readAsBytes();
      } else {
      Uint8List resourceBytes = await _loadFromUrl(url);

      if (resourceBytes != null) {
      await file.writeAsBytes(resourceBytes);
      return resourceBytes;
      } else {
      return null;
      }
      }
      }

      Future<Uint8List> _loadFromUrl(String url) async {
      final response = await http.get(url);

      if (response.statusCode >= 200 && response.statusCode < 300) {
      return response.bodyBytes;
      } else {
      return null;
      }
      }

      String _getSHA(String sth) {
      var bytes = utf8.encode(sth);
      var digest = sha1.convert(bytes);

      return digest.toString();
      }

      void dispose() {
      _mapBackgroundImage.dispose();
      }
      }


      And to supply the image to the ZoomableImage I created a custom ImageProvider:



      class MapImageProvider extends ImageProvider<MapImageProvider> {
      final String url;
      final List<Sensor> sensors;

      final MapRenderer mapRenderer = MapRenderer();

      MapImageProvider(this.url, this.sensors);

      @override
      ImageStreamCompleter load(MapImageProvider key) {
      return MultiFrameImageStreamCompleter(
      codec: _loadAsync(key),
      scale: 1.0,
      informationCollector: (StringBuffer information) {
      information.writeln('Image provider: $this');
      information.write('Image key: $key');
      });
      }

      Future<ui.Codec> _loadAsync(MapImageProvider key) async {
      assert(key == this);

      return await mapRenderer.renderMap(url, sensors);
      }

      @override
      bool operator ==(Object other) =>
      identical(this, other) ||
      other is MapImageProvider &&
      runtimeType == other.runtimeType &&
      url == other.url;

      @override
      int get hashCode => url.hashCode;

      @override
      String toString() => '$runtimeType("$url")';

      @override
      Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
      return SynchronousFuture<MapImageProvider>(this);
      }
      }


      If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).






      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        I finally managed to solve the issue!



        I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).



        The renderer:



        class MapRenderer {
        ui.Image _mapBackgroundImage;

        Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
        await _loadMapBackground(url);
        var renderedMapImage = await _updateSensors(sensors);
        var byteD = await renderedMapImage.toByteData(
        format: ui.ImageByteFormat.png);
        return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
        }


        Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
        ui.PictureRecorder recorder = ui.PictureRecorder();
        Canvas c = Canvas(recorder);

        var paint = ui.Paint();
        c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

        for (Sensor s in sensors) {
        paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
        .npSensorOccupied);
        c.drawRect(
        ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
        ui.Offset(s.posX + s.width, s.posY + s.height)),
        paint,
        );
        }

        return recorder
        .endRecording()
        .toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
        }

        Future<void> _loadMapBackground(String url) async {
        var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

        if (imageBytes != null) {
        _mapBackgroundImage = await _getImageFromBytes(imageBytes);
        } else {
        return null;
        }
        }

        Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
        var imageCodec = await ui.instantiateImageCodec(bytes);
        var frame = await imageCodec.getNextFrame();
        return frame.image;
        }

        Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
        final directory = await getApplicationDocumentsDirectory();
        final file = File("${directory.path}/${_getSHA(url)}.png");

        if (await file.exists()) {
        return await file.readAsBytes();
        } else {
        Uint8List resourceBytes = await _loadFromUrl(url);

        if (resourceBytes != null) {
        await file.writeAsBytes(resourceBytes);
        return resourceBytes;
        } else {
        return null;
        }
        }
        }

        Future<Uint8List> _loadFromUrl(String url) async {
        final response = await http.get(url);

        if (response.statusCode >= 200 && response.statusCode < 300) {
        return response.bodyBytes;
        } else {
        return null;
        }
        }

        String _getSHA(String sth) {
        var bytes = utf8.encode(sth);
        var digest = sha1.convert(bytes);

        return digest.toString();
        }

        void dispose() {
        _mapBackgroundImage.dispose();
        }
        }


        And to supply the image to the ZoomableImage I created a custom ImageProvider:



        class MapImageProvider extends ImageProvider<MapImageProvider> {
        final String url;
        final List<Sensor> sensors;

        final MapRenderer mapRenderer = MapRenderer();

        MapImageProvider(this.url, this.sensors);

        @override
        ImageStreamCompleter load(MapImageProvider key) {
        return MultiFrameImageStreamCompleter(
        codec: _loadAsync(key),
        scale: 1.0,
        informationCollector: (StringBuffer information) {
        information.writeln('Image provider: $this');
        information.write('Image key: $key');
        });
        }

        Future<ui.Codec> _loadAsync(MapImageProvider key) async {
        assert(key == this);

        return await mapRenderer.renderMap(url, sensors);
        }

        @override
        bool operator ==(Object other) =>
        identical(this, other) ||
        other is MapImageProvider &&
        runtimeType == other.runtimeType &&
        url == other.url;

        @override
        int get hashCode => url.hashCode;

        @override
        String toString() => '$runtimeType("$url")';

        @override
        Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
        return SynchronousFuture<MapImageProvider>(this);
        }
        }


        If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).






        share|improve this answer












        I finally managed to solve the issue!



        I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).



        The renderer:



        class MapRenderer {
        ui.Image _mapBackgroundImage;

        Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
        await _loadMapBackground(url);
        var renderedMapImage = await _updateSensors(sensors);
        var byteD = await renderedMapImage.toByteData(
        format: ui.ImageByteFormat.png);
        return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
        }


        Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
        ui.PictureRecorder recorder = ui.PictureRecorder();
        Canvas c = Canvas(recorder);

        var paint = ui.Paint();
        c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);

        for (Sensor s in sensors) {
        paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
        .npSensorOccupied);
        c.drawRect(
        ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
        ui.Offset(s.posX + s.width, s.posY + s.height)),
        paint,
        );
        }

        return recorder
        .endRecording()
        .toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
        }

        Future<void> _loadMapBackground(String url) async {
        var imageBytes = await _getLocalCopyOrLoadFromUrl(url);

        if (imageBytes != null) {
        _mapBackgroundImage = await _getImageFromBytes(imageBytes);
        } else {
        return null;
        }
        }

        Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
        var imageCodec = await ui.instantiateImageCodec(bytes);
        var frame = await imageCodec.getNextFrame();
        return frame.image;
        }

        Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
        final directory = await getApplicationDocumentsDirectory();
        final file = File("${directory.path}/${_getSHA(url)}.png");

        if (await file.exists()) {
        return await file.readAsBytes();
        } else {
        Uint8List resourceBytes = await _loadFromUrl(url);

        if (resourceBytes != null) {
        await file.writeAsBytes(resourceBytes);
        return resourceBytes;
        } else {
        return null;
        }
        }
        }

        Future<Uint8List> _loadFromUrl(String url) async {
        final response = await http.get(url);

        if (response.statusCode >= 200 && response.statusCode < 300) {
        return response.bodyBytes;
        } else {
        return null;
        }
        }

        String _getSHA(String sth) {
        var bytes = utf8.encode(sth);
        var digest = sha1.convert(bytes);

        return digest.toString();
        }

        void dispose() {
        _mapBackgroundImage.dispose();
        }
        }


        And to supply the image to the ZoomableImage I created a custom ImageProvider:



        class MapImageProvider extends ImageProvider<MapImageProvider> {
        final String url;
        final List<Sensor> sensors;

        final MapRenderer mapRenderer = MapRenderer();

        MapImageProvider(this.url, this.sensors);

        @override
        ImageStreamCompleter load(MapImageProvider key) {
        return MultiFrameImageStreamCompleter(
        codec: _loadAsync(key),
        scale: 1.0,
        informationCollector: (StringBuffer information) {
        information.writeln('Image provider: $this');
        information.write('Image key: $key');
        });
        }

        Future<ui.Codec> _loadAsync(MapImageProvider key) async {
        assert(key == this);

        return await mapRenderer.renderMap(url, sensors);
        }

        @override
        bool operator ==(Object other) =>
        identical(this, other) ||
        other is MapImageProvider &&
        runtimeType == other.runtimeType &&
        url == other.url;

        @override
        int get hashCode => url.hashCode;

        @override
        String toString() => '$runtimeType("$url")';

        @override
        Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
        return SynchronousFuture<MapImageProvider>(this);
        }
        }


        If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered yesterday









        RMK

        632510




        632510






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53203904%2fdrawing-over-an-image-downloaded-from-remote-server%23new-answer', 'question_page');
            }
            );

            Post as a guest




















































































            Popular posts from this blog

            Guess what letter conforming each word

            Port of Spain

            Run scheduled task as local user group (not BUILTIN)