import 'dart:convert'; import 'dart:typed_data'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:http/http.dart' as http; import 'package:cached_network_image/cached_network_image.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:skeleton_text/skeleton_text.dart'; class SkeletonImage extends StatefulWidget { final String imageUrl; final double width; final double height; final BorderRadius? borderRadius; final double? aspectRatio; const SkeletonImage({ Key? key, required this.imageUrl, this.width = 300, this.height = 140, this.borderRadius = DesignConfig.lowBorderRadius, this.aspectRatio, }) : super(key: key); @override State createState() => _SkeletonImageState(); } class _SkeletonImageState extends State { Uint8List? _bytes; @override void initState() { if (kIsWeb) _getImage(); super.initState(); } Future _getImage() async { final url = RequestHelper.baseUrl + widget.imageUrl; final storage = StorageService.webStorage; String? imageCache = storage['image-cache']; final Map data = imageCache == null ? {} : jsonDecode(imageCache); if (data.containsKey(url)) { _bytes = Uint8List.fromList( List.from(data[url]), ); } else { _bytes = (await http.get( Uri.parse(url), headers: {'Authorization': 'Bearer ${RequestService.token}'}, )) .bodyBytes; addImageToStorage(); } if (mounted) { setState(() {}); } } void addImageToStorage() { final storage = StorageService.webStorage; String? imageCache = storage['image-cache']; final Map data = imageCache == null ? {} : Map.from(jsonDecode(imageCache)); data.addAll({RequestHelper.baseUrl + widget.imageUrl: _bytes}); StorageService.webStorage.addAll({'image-cache': jsonEncode(data)}); } @override Widget build(BuildContext context) { return _aspectRatioGenerator( child: Builder(builder: (context) { if (kIsWeb) { return Builder( builder: (context) { if (_bytes == null || _bytes!.isEmpty) { return ShimmerPlaceholder( borderRadius: widget.borderRadius, ); } return ClipRRect( borderRadius: widget.borderRadius, child: Image.memory( _bytes!, width: widget.width, height: widget.height, fit: BoxFit.cover, ), ); }, ); } return CachedNetworkImage( httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'}, width: widget.width, height: widget.height, imageUrl: RequestHelper.baseUrl + widget.imageUrl, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( borderRadius: widget.borderRadius ?? DesignConfig.lowBorderRadius, image: DecorationImage( image: imageProvider, fit: BoxFit.cover, ), ), ), progressIndicatorBuilder: (context, url, progress) => SkeletonAnimation( shimmerColor: Theme.of(context).colorScheme.border, borderRadius: widget.borderRadius ?? DesignConfig.lowBorderRadius, child: Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.disabledBackground, borderRadius: widget.borderRadius, ), ), ), ); }), ); } Widget _aspectRatioGenerator({required Widget child}) => widget.aspectRatio == null ? SizedBox(child: child) : AspectRatio( aspectRatio: widget.aspectRatio!, child: child, ); }