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();
}
}