_CastError (type 'Null' is not a subtype of type 'List<int>' in type cast) when having Network Image inside Listview

Viewed 8348

i have an error that i just dont find a solution for. I created a little messenger thingy in flutter and have a problem when using a NetworkImage inside one of my chat bubbles.

When i send the image as message, it is displayed without problem in the bubble. Also when i send multiple images, it is no problem and they extend beyond the screen and i can just scroll up and down without any problems.

Though when i reopen the room screen and there are multiple images and they extend over the visible screen i get _CastError (type 'Null' is not a subtype of type 'List' in type cast) from network_image dart file. BUT this only happens after a hot restart. If i just navigate back and then reopen the room screen its also all fine, but as soon as i hot restarted once i always get the error when trying to open a room, which has images extending the visible space.

Iam still kinda new to flutter, so i know my code sucks mostly but this time i just dont even find a "dirty" way to solve it.

Flutter 2.2.0 (beta channel)
Dart 2.13.0 On Android Emulator Pixel 4a API 30

Edit 1: i removed a lot to make it easier to read.

Edit 2: i found it to be somehow connected to using the downloadURL from Firebase Storage. When i replace the url with just some test png url it doesnt seem to be a problem.

import 'package:bubble/bubble.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:app/src/app/provider/firestore_provider.dart';

class MessageRoomCopy extends ConsumerWidget {
  final String roomName;
  final String roomID;
  MessageRoomCopy(this.roomName, this.roomID);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final firestoreServ = watch(firestoreServiceProvider);
    return Scaffold(
      backgroundColor: Color(0xFFECE5DD),
      appBar: AppBar(title: Text("$roomName", style: TextStyle(color: Colors.white))),
      body: StreamBuilder(
          stream: firestoreServ.messagesOfRoom("$roomID"),
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
            if (snapshot.hasError) {
              return Center(
                child: Text("Error"),
              );
            } else if (snapshot.connectionState == ConnectionState.waiting) {
              return Center(child: CircularProgressIndicator());
            } else {
              final List<QueryDocumentSnapshot<Map<String, dynamic>>> messagesList = snapshot.data!.docs;

              return ListView.separated(
                reverse: true,
                //shrinkWrap: true,
                itemBuilder: (BuildContext context, int index) {
                  var _messageBubble;
                  double? _elevation = 2.0;
                  BubbleEdges? _margin = BubbleEdges.only(top: 10);
                  BubbleNip? _nip = BubbleNip.rightTop;
                  bool? _showNip = true;
                  Alignment? _alignment = Alignment.topRight;
                  Color? _color = Color.fromRGBO(225, 255, 199, 1.0);
                  Widget? _messageChild;

                  if (messagesList[index].get("messagetype") == "text") {
                    _messageChild = Text(
                      messagesList[index].get("content"),
                      style: Theme.of(context).textTheme.bodyText2,
                    );
                  } else if (messagesList[index].get("messagetype") == "image") {
                    _messageChild = Image.network(
                      messagesList[index].get("content"),
                      errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
                        return Text("Error Wdiget");
                      },
                    );
                  }

                  _messageBubble = Bubble(
                    elevation: _elevation,
                    margin: _margin,
                    nip: _nip,
                    showNip: _showNip,
                    alignment: _alignment,
                    color: _color,
                    child: _messageChild,
                  );

                  return Container(
                    child: Column(
                      children: [
                        _messageBubble,
                      ],
                    ),
                  );
                },
                separatorBuilder: (BuildContext context, int index) => Divider(
                  height: 1,
                  color: Colors.transparent,
                ),
                itemCount: messagesList.length,
              );
            }
          }),
    );
  }
}

Also this is where the error rethrow is coming from inside _network_image_io.dart

Future<ui.Codec> _loadAsync(
    NetworkImage key,
    StreamController<ImageChunkEvent> chunkEvents,
    image_provider.DecoderCallback decode,
  ) async {
    try {
      assert(key == this);

      final Uri resolved = Uri.base.resolve(key.url);

      final HttpClientRequest request = await _httpClient.getUrl(resolved);

      headers?.forEach((String name, String value) {
        request.headers.add(name, value);
      });
      final HttpClientResponse response = await request.close();
      if (response.statusCode != HttpStatus.ok) {
        // The network may be only temporarily unavailable, or the file will be
        // added on the server later. Avoid having future calls to resolve
        // fail to check the network again.
        await response.drain<List<int>>();
        throw image_provider.NetworkImageLoadException(statusCode: response.statusCode, uri: resolved);
      }

      final Uint8List bytes = await consolidateHttpClientResponseBytes(
        response,
        onBytesReceived: (int cumulative, int? total) {
          chunkEvents.add(ImageChunkEvent(
            cumulativeBytesLoaded: cumulative,
            expectedTotalBytes: total,
          ));
        },
      );
      if (bytes.lengthInBytes == 0)
        throw Exception('NetworkImage is an empty file: $resolved');

      return decode(bytes);
    } catch (e) {
      // Depending on where the exception was thrown, the image cache may not
      // have had a chance to track the key in the cache at all.
      // Schedule a microtask to give the cache a chance to add the key.
      scheduleMicrotask(() {
        PaintingBinding.instance!.imageCache!.evict(key);
      });
      rethrow; // here this one
    } finally {
      chunkEvents.close();
    }
  }
4 Answers

Ok, i kind of found the problem. I took the Firebase servertimestamp as variable for the filename in the Firebase Storage. That of course only resulted in the filename being

FieldValue(Instance of 'MethodChannelFieldValue')

Though i dont really understand why that was a problem, but now with a normal Datetime toString timestamp it all works perfectly fine. Maybe its some special character escaping in the generated downloadURL from Firebase Storage with this kind of filename.

i got it solved by using this chached Networkimage code :

CachedNetworkImage(
    imageUrl: snapshot.data!.docs[index]['image'],
    imageBuilder: (context, imageProvider) =>
    Container(
        decoration: BoxDecoration(
        image: DecorationImage(
            image: imageProvider,
        ),
    ),
),
placeholder: (context, url) =>
    CircularProgressIndicator(),
    // errorWidget: (context, url, error) =>
    // Icon(Icons.error),
),

Use CachedNetworkImage instead:

return Scaffold(
  appBar: AppBar(
    title: Text(title),
  ),
  body: Center(
    child: CachedNetworkImage(
      placeholder: (context, url) => const CircularProgressIndicator(),
      imageUrl: 'https://somepicurl',
    ),
  ),
);

I had the same issue, and it was because my images were too big, I reduced the image resolution and worked.

Related