How to support HTTP headers NetworkImage in flutter web
Asked Answered
C

3

5

I tried to get some images from the internet in flutter web that need to add headers. I've used cached_network_image: ^2.5.1 and it doesn't support the web completely also Image.network seems when running flutter on the web, headers are not used. Both of these ways successfully work on the Android release. enter image description here

environment:
  sdk: ">=2.7.0 <3.0.0"

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.4, on Microsoft Windows [Version 10.0.19042.985], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] IntelliJ IDEA Ultimate Edition (version 2020.1)
[√] Connected device (3 available)

• No issues found!
Costanza answered 25/5, 2021 at 13:52 Comment(0)
R
7

With cached_network_image: 3.0.0 I got it working to add headers by using the CachedNetworkImageProvider class.

CachedNetworkImageProvider(
              "${IOConfig.apiUrl}",
              headers: {"Authorization": "Bearer $apiToken",
                "Access-Control-Allow-Headers": "Access-Control-Allow-Origin, Accept"}                                                
             ,imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet)

I had to start it with an argument to change the web-renderer

flutter run -d chrome --web-renderer html 
Reparable answered 31/7, 2021 at 23:13 Comment(1)
This is it!. but I use CachedNetworkImage with imageRenderMethodForWeb. It needs import package:cached_network_image_platform_interface/cached_network_image_platform_interface.dartGoaltender
D
1

Most likely, this particular limitation is due to the reduced customization capabilities of the img HTML-element itself. Fortunately, there are many other ways to display network images - here are just two examples:

Using http package:

  void loadImageV1(String url, Map<String, String>? headers) async {
    final res = await http.get(Uri.parse(url), headers: headers);
    final blob = html.Blob([res.bodyBytes]);
    setState(() {
      _url = html.Url.createObjectUrlFromBlob(blob);
    });
  }

or using exposed by browser fetch API:

  void loadImageV2(String url, Map<String, String>? headers) async {
    final res = await html.window.fetch(url, {'method': 'GET', 'headers': headers});
    final blob = await res.blob();
    setState(() {
      _url = html.Url.createObjectUrlFromBlob(blob);
    });
  }

And now we can use generated syntetic url with Image.Network constructor.

Here is full demo:

import 'dart:html' as html;
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

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

class DemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Network Image Demo',
      home: NextLabPage(),
    );
  }
}

const imgUrl = 'https://robohash.org/1234';
const imgHeaders = {'Accept': 'image/*', 'Accept-language': 'en'};

class NextLabPage extends StatefulWidget {
  @override
  _NextLabPageState createState() => _NextLabPageState();
}

class _NextLabPageState extends State<NextLabPage> {
  String? _url;

  @override
  void initState() {
    super.initState();
    loadImageV1(imgUrl, imgHeaders);
  }

  @override
  void dispose() {
    if (_url != null) html.Url.revokeObjectUrl(_url!);
    super.dispose();
  }

  void loadImageV1(String url, Map<String, String>? headers) async {
    final res = await http.get(Uri.parse(url), headers: headers);
    final blob = html.Blob([res.bodyBytes]);
    setState(() {
      _url = html.Url.createObjectUrlFromBlob(blob);
    });
  }

  void loadImageV2(String url, Map<String, String>? headers) async {
    final res = await html.window.fetch(url, {'method': 'GET', 'headers': headers});
    final blob = await res.blob();
    setState(() {
      _url = html.Url.createObjectUrlFromBlob(blob);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: _url != null ? Image.network(_url!) : Text('Loading...'),
      ),
    );
  }
}

Despite answered 27/5, 2021 at 14:14 Comment(1)
Why even convert to a blob? I would just pass the bytes to a memory image, so it's not web exclusive.Speculation
S
1

Instead of converting the image to a blob, I recommend just passing the raw bytes to a memory image:

  Uint8List? _imageBytes;

  void loadImage(String url, Map<String, String>? headers) async {
    final res = await http.get(Uri.parse(url), headers: headers);
    setState(() {
      _imageBytes = res.bodyBytes;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: _imageBytes == null ? Text('Loading...') : Image.memory(_imageBytes!),
      ),
    );
  }

Remember to use the errorBuilder parameter of Image, in case the request has failed or doesn't contain a valid image.

Speculation answered 6/2, 2023 at 21:36 Comment(1)
This is a great recommendation! Simple, and no need for another dependencyMelonymelos

© 2022 - 2024 — McMap. All rights reserved.