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();
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With