phase3: util_web null-safe (showImage, WebImagePicker, ImageInfo)

util_io deferred (excluded from web build via conditional import).
This commit is contained in:
2026-07-24 20:17:07 +08:00
parent 7e8182259f
commit f831fd1405

View File

@@ -41,21 +41,21 @@ class Util {
return box; return box;
} }
static Widget showImage(String imageUrl, {double width, double height, static Widget showImage(String imageUrl, {double? width, double? height,
BoxFit fit, Widget Function(BuildContext, String, dynamic) errorWidget}) { BoxFit? fit, Widget Function(BuildContext, String?, dynamic)? errorWidget}) {
return Image.network(imageUrl, return Image.network(imageUrl,
fit: fit, fit: fit,
width: width, width: width,
height: height, height: height,
cacheWidth: width != null ? width.round() : null, cacheWidth: width != null ? width.round() : null,
cacheHeight: height != null ? height.round() : null, cacheHeight: height != null ? height.round() : null,
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent loadingProgress) { loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child; if (loadingProgress == null) return child;
return Center( return Center(
child: Utils.imageLoadingIndicator(), child: Utils.imageLoadingIndicator(),
); );
}, },
errorBuilder: (BuildContext context, Object object, StackTrace stackTrace) { errorBuilder: (BuildContext context, Object object, StackTrace? stackTrace) {
print('StackTrace: $stackTrace'); print('StackTrace: $stackTrace');
if (errorWidget != null) { if (errorWidget != null) {
return errorWidget(context, null, null); return errorWidget(context, null, null);
@@ -105,16 +105,16 @@ class Util {
} }
void startFilePicker(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async { void startFilePicker(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async {
InputElement uploadInput = FileUploadInputElement(); FileUploadInputElement uploadInput = FileUploadInputElement();
uploadInput.click(); uploadInput.click();
uploadInput.onChange.listen((e) { uploadInput.onChange.listen((e) {
final files = uploadInput.files; final files = uploadInput.files;
if (files.length >= 1) { if (files != null && files.length >= 1) {
final File file = files[0]; final File file = files[0];
final FileReader reader = new FileReader(); final FileReader reader = new FileReader();
reader.onLoadEnd.listen((e) { reader.onLoadEnd.listen((e) {
uploadPicture(context, reader.result, file.name, user, commentId: commentId, orderId: orderId); uploadPicture(context, reader.result as Uint8List, file.name, user, commentId: commentId, orderId: orderId);
}); });
reader.onError.listen((e) { reader.onError.listen((e) {
Fluttertoast.showToast( Fluttertoast.showToast(
@@ -209,11 +209,11 @@ class Util {
void startFilePicker2(BuildContext context, int imageId, OnGotFile onGotFile) async { void startFilePicker2(BuildContext context, int imageId, OnGotFile onGotFile) async {
final _image = await WebImagePicker().pickImage(); final _image = await WebImagePicker().pickImage();
onGotFile(imageId, _image.dataScheme); onGotFile(imageId, _image?.dataScheme ?? '');
} }
void createTicket(BuildContext context, String msg, List<Map<String, dynamic>> images, void createTicket(BuildContext context, String msg, List<Map<String, dynamic>> images,
OnSuccess onSuccess, OnError onError, {int id}) { OnSuccess onSuccess, OnError onError, {int? id}) {
var formData = FormData(); var formData = FormData();
formData.fields.add(MapEntry("msg", msg)); formData.fields.add(MapEntry("msg", msg));
formData.fields.add(MapEntry('id', id == null ? '0' : id.toString())); formData.fields.add(MapEntry('id', id == null ? '0' : id.toString()));
@@ -291,32 +291,32 @@ class Util {
} }
class ImageInfo { class ImageInfo {
String name; String? name;
String data; String? data;
String dataScheme; String? dataScheme;
String path; String? path;
} }
class WebImagePicker { class WebImagePicker {
Future<ImageInfo> pickImage() async { Future<ImageInfo?> pickImage() async {
print('pickImage'); print('pickImage');
final ImageInfo data = ImageInfo(); final ImageInfo data = ImageInfo();
final FileUploadInputElement input = FileUploadInputElement(); final FileUploadInputElement input = FileUploadInputElement();
document.body.children.add(input); document.body?.children.add(input);
input..accept = 'image/*'; input..accept = 'image/*';
input.click(); input.click();
await input.onChange.first; await input.onChange.first;
if (input.files.isEmpty) return null; if (input.files == null || input.files!.isEmpty) return null;
final reader = FileReader(); final reader = FileReader();
reader.readAsDataUrl(input.files[0]); reader.readAsDataUrl(input.files![0]);
await reader.onLoad.first; await reader.onLoad.first;
final encoded = reader.result as String; final encoded = reader.result as String;
// remove data:image/*;base64 preambule // remove data:image/*;base64 preambule
final stripped = final stripped =
encoded.replaceFirst(RegExp(r'data:image/[^;]+;base64,'), ''); encoded.replaceFirst(RegExp(r'data:image/[^;]+;base64,'), '');
//final imageBase64 = base64.decode(stripped); //final imageBase64 = base64.decode(stripped);
final imageName = input.files?.first?.name; final imageName = input.files?[0].name;
final imagePath = input.files?.first?.relativePath; final imagePath = input.files?[0].relativePath;
data.name = imageName; data.name = imageName;
data.data = stripped; data.data = stripped;
data.dataScheme = encoded; data.dataScheme = encoded;