Get to know when CacheNetworkImage successfully show network image - flutter

I am using CacheNetworkImage for displaying an image, I have a use case like I have to show loading on the whole Container until the image is not rendered. Is there any way I can get to know that my image is successfully rendered on screen?
This is what I have done so far:
return CachedNetworkImage(
fit: BoxFit.fill,
imageUrl: url,
errorWidget: (a, b, c) => const Center(child: Icon(Icons.error_outline)),
progressIndicatorBuilder: (context, _, DownloadProgress progress) {
getProgressStatus(progress);
return Shimmer(color: Colors.grey, child: const SizedBox.expand());
},
);
void getProgressStatus(DownloadProgress loadingStatus) {
if (loadingStatus.downloaded == loadingStatus.totalSize) {
scheduleMicrotask(() {
setState(() {
isLoaded = true;
});
});
return;
}
scheduleMicrotask(() {
setState(() {
isLoaded = false;
});
});
}

By using progressIndicatorBuilder property of CachedNetworkImage you can manage it,
progressIndicatorBuilder: (context, url, downloadProgress) =>
Container(
margin: EdgeInsets.only(
top: 100.h,
bottom: 100.h
),
child: CircularProgressIndicator(
value: downloadProgress.progress,
color: AppColors.lightBlack)),
)

You can use placeholder until the image is loaded. This is how you can use it.
CachedNetworkImage(
placeholder: placeholderWidgetFn() as Widget Function(
BuildContext, String)?,
imageUrl: imgURL,
width: 300,
height: (MediaQuery.of(context).size.height) / 2.5,
),
Widget? Function(BuildContext, String) placeholderWidgetFn() =>
(_, s) => placeholderWidget();
Widget placeholderWidget() =>
Image.asset('images/placeholder.jpg', fit: BoxFit.cover);

Usecase: I have a tile in which i have added CacheNetworkImage & I want to add the PlaceHolder on the whole tile until that specific image not loads.
See this image for what I want to achieve
I have achieve this using Stack widget.
Stack(
children: [
MyTile(
image: CacheNetworkImage(imageUrl)
),
SizedBox(
width: double.infinity,
height: ///Same as your lower container
child CacheNetworkImage(
imageUrl: imageUrl,
fit: BoxFit.fill,
color: Colors.transparent,
colorBlendMode: BlendMode.clear,
placeholder: (context, _) {
return MyPlaceHolder(...)
},
)
],
),
Note: Image url for both the child's will be exactly same

Related

Display image in dialog - the return type 'Future<Uint8List> isn't a Widget

I'm going round in circles with this and would appreciate a fresh viewpoint.
I have the following button, which when tapped must display an image. The image data is fetched from the backend (this part works fine).
IconButton(
icon: Icon(
Icons.attach_email_rounded,
size: 32.0,
),
color: Colors.grey,
onPressed: () async {
await showDialog(
context: context,
builder: (_) => showAttachment( // <-- error here
appstate['arg1'],
appstate['arg2'],
appstate['arg3]),
);
},
)
Function:
Future<Dialog> showAttachment(arg1, arg2, arg3) async {
Uint8List attachmentData;
await getAttachment(arg1, arg2, arg3).then(
(value) => {
attachmentData = value,
},
);
return Dialog(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.memory(attachmentData).image,
fit: BoxFit.cover,
),
),
),
);
}
However, I'm getting the error The return type 'Future<Dialog>' isn't a 'Widget', as required by the closure's context.
Googling around has revealed I need to use FutureBuilder, but I'm not sure how to incorporate that in the above closure code.
I'd appreciate pointers.
Thanks
The builder functions for showDialog is synchronous, so in order to use a future we'll have to nest a FutureBuilder into the Dialog and keep the containing function synchronous.
I've adapted your code sample to demonstrate:
Dialog showAttachment(arg1, arg2, arg3) {
return Dialog(
child: FutureBuilder<Uint8List>(
future: getAttachment(arg1, arg2, arg3),
builder: (context, snapshot) {
return snapshot.hasData
? Container(
width: 200,
height: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.memory(snapshot.data!).image,
fit: BoxFit.cover,
),
),
)
: const CircularProgressIndicator();
},
),
);
}
It's important to remember that, unless you provide the optional initial data argument for the FutureBuilder, the first time the builder is called, the snapshot won't have data: so we should check that.
Also not shown above is handling errors; which is done in the same way as checking if the snapshot has data.
FutureBuilder Documentation

Show picture from assets or from files Flutter

I want to show a picture inside a CircleAvatar, if the user never inserted a picture before then the default picture "empty_profile.png" will appear, otherwise if the user already inserted a picture from gallery or camera then the last one will be shown.
This is the code :
File _file = null;
File variable declared at the beginning.
Future<void> changeImage() async { //This will change the picture
File tmp = await imgFromGallery();
setState(() {
_file = tmp;
});
return;
}
The function above will change _file value to the file picked from gallery.
Widget myAvatar() {
return GestureDetector(
onTap: null,
child: CircleAvatar(
radius: 55,
backgroundColor: Color(0xffFDCF09),
child: CircleAvatar(
radius: 50,
child: Container(
child: _file == null
? AssetImage("empty_profile.png")
: FileImage(_file),
),
),
),
);
}
Finally if file is still null then the asset image is loaded, otherwise if a new picture is choosen then FileImage(_file) will show the picked image.
I have a lots of error because I don't know very well how to handle files, their paths and show images...Can you explain me how I should do?
To include static images in your Flutter project, place them inside the "assets/images" folder. Then, make sure to add this folder to your pubspec.yml:
flutter:
assets:
- assets/images/
Next, you may have errors related to trying to render an AssetImage inside a CircleAvatar. To render the file as an Image widget instead, use Image.asset(<path>). Your example could be written as:
Widget myAvatar() {
return GestureDetector(
onTap: null,
child: CircleAvatar(
radius: 55,
backgroundColor: Color(0xffFDCF09),
child: _file == null
? Image.asset("assets/images/empty_profile.png")
: _file
),
);
}
Finally, a great resource for user-selected images is the image_picker library: https://pub.dev/packages/image_picker. For example, a "Select from Gallery" button could invoke the following code, which allows the user to crop the image and saves it as an Image widget:
PickedFile image = await picker.getImage(
source: ImageSource.gallery, // to select from camera, use ImageSource.camera
maxHeight: 1024,
maxWidth: 1024,
imageQuality: 50
);
try {
File croppedImage = await ImageCropper.cropImage( // use platform-native image cropping
sourcePath: image.path,
cropStyle: CropStyle.circle,
maxWidth: 512,
maxHeight: 512
);
setState(() { // refresh state to render new profile image
_file = Image.file(croppedImage)
})
} catch (err) {
print(err)
}
You can use CachedNetworkImage PlugIn - Update other details as per your need. This code show Network Images, if not available will show Assets Image.
new Container(
width: 140.0,
height: 140.0,
child: ClipOval(
child: CachedNetworkImage(
imageUrl: _profile == null ? "" : Api.baseURL + _profile,
placeholder: (context, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => Image.asset(
"img/user.png",
fit: BoxFit.cover,
height: 140.0,
width: 140.0,
),
fit: BoxFit.cover,
height: 140.0,
width: 140.0,
),
),
),

Flutter: why I'm getting only 3 results in api call using chopper?

when call the api i get only 3 results when I use chopper but when use normal http package than I get more result
my chopper service file and I have generated the *.chopper.dart file
import 'package:chopper/chopper.dart';
part 'chopper_api_service.chopper.dart';
#ChopperApi(baseUrl: 'https://newsapi.org/v2')
abstract class ChopperApiService extends ChopperService {
#Get(path: '/top-headlines')
Future<Response> getNews({
#Query('apiKey') String apiKey = 'secret',
#Query('category') String category = 'health',
#Query('country') String country = 'in'
});
static ChopperApiService create() {
final client = ChopperClient(
baseUrl: 'https://newsapi.org/v2',
services: [
_$ChopperApiService(),
],
converter: JsonConverter(),
);
return _$ChopperApiService(client);
}
}
In UI where I'm trying to the result,
import 'dart:convert';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:chopper/chopper.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import '../../../constants/url.dart';
import '../models/chopper_api_service.dart';
class ChopperNewsCard extends StatefulWidget {
#override
_ChopperNewsCardState createState() => _ChopperNewsCardState();
}
class _ChopperNewsCardState extends State<ChopperNewsCard> {
ChopperApiService chopperApiService;
Future<Response> apiResponse;
#override
void initState() {
super.initState();
chopperApiService = ChopperApiService.create();
apiResponse = chopperApiService.getNews();
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return FutureBuilder<Response>(
future: apiResponse,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final news = jsonDecode(snapshot.data.bodyString);
print(news); //<-----printing it
return Container(
height: height * 0.37,
width: double.infinity,
child: ListView.builder(
itemCount: news.length,
physics: AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (context, index) {
return Container(
width: width * 0.70,
padding: EdgeInsets.only(right: width * 0.05),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
elevation: 3,
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CachedNetworkImage(
imageUrl:
news['articles'][index]['urlToImage'] == null
? Url.noImage
: news['articles'][index]['urlToImage'],//<--- this
fit: BoxFit.cover,
width: double.infinity,
height: height * 0.2,
placeholder: (context, url) =>
Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) =>
Icon(Icons.error_outline_sharp),
),
),
Padding(
padding: EdgeInsets.only(
right: width * 0.03,
left: width * 0.03,
top: width * 0.03),
child: Text(
news['articles'][index]['title'],
maxLines: 4,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
],
),
),
);
},
),
);
} else {
return Center(
child: Lottie.asset('assets/lottie/loading.json',
height: width * 0.5, width: width * 0.5),);
}
},
);
}
}
when print it also shows only 3 results but in the totalResults category it says 70,
{status: ok, totalResults: 70, articles: [{source: {id: null, name: Deseret News}, author: Herb Scribner, title: Why COVID symptoms still appears if you take zinc, vitamin c - Deseret News, description: Do vitamin C and zinc help fight off COVID-19? A new sutdy says that’s not the case., url: https://www.deseret.com/u-s-world/2021/2/18/22288048/covid-19-symptoms-zinc-vitamin-c, urlToImage: https://cdn.vox-cdn.com/thumbor/6Vl9l5InMVmP9-Oqu_WVvgcThYw=/0x147:2510x1461/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/22294628/AP20357531088811.jpg, publishedAt: 2021-02-19T04:00:00Z, content: A new study suggests that vitamin C and zinc dont help fight off COVID-19, even when theyre taken at high doses.
Whats going on?
The study published in mid-February in JAMA Network Open found that … [+1522 chars]}, {source: {id: google-news, name: Google News}, author: null, title: Sask. health-care worker dies after positive COVID-19 test - CBC News: The National, description: null, url:<…>
now do I need add something in the query parameters to get more results
By default, newsapi shows 20 results per page. You can control that by using pageSize and page params.
Top headlines documentation
print(news); have limited output and will not print the entire map.
Use the following print statement to check how many object do you have in the articles list: print('Articles count: ${news['articles'].length}');
You need to change this code in order to get the item count:
child: ListView.builder(
itemCount: news['articles'].length,

page curl effect using pdf file

I have a PDF file and I would like to reproduce a page curl effect like in this package page:
https://pub.flutter-io.cn/packages/page_turn
I tried using this page_turn plugin and it takes list of widgets that will display in order. I tried using native_pdf_renderer plugin to render the pdf and display on each page, but when I do this, the pages are blank. but if I remove from the PageTurn widget, it works.
import 'package:flutter/material.dart';
import 'package:native_pdf_renderer/native_pdf_renderer.dart';
import 'package:page_turn/page_turn.dart';
class TestScreen extends StatelessWidget {
final _controller = GlobalKey<PageTurnState>();
Future<PdfPageImage> getPageImage() async {
final document = await PdfDocument.openAsset('assets/pdfs/222.pdf');
final page = await document.getPage(6);
final pageImage = await page.render(
width: page.width,
height: page.height,
format: PdfPageFormat.JPEG,
);
await page.close();
return pageImage;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder<PdfPageImage>(
future: getPageImage(),
builder: (context, snapshot) {
print(snapshot.data);
if (snapshot.data == null) return CircularProgressIndicator();
// This works:
// return Image(
// image: MemoryImage(snapshot.data.bytes),
// );
//This makes all my pages blank
return PageTurn(
key: _controller,
backgroundColor: Colors.white,
children: <Widget>[
Image(
image: MemoryImage(snapshot.data.bytes),
),
Image(
image: MemoryImage(snapshot.data.bytes),
),
Image(
image: MemoryImage(snapshot.data.bytes),
),
],
);
},
)),
);
}
}
after reading the lib code i see there is widget to show image pre-renderd
you should use PageTurnImage instead Image like this
PageTurn(
key: _controller,
backgroundColor: Colors.white,
children: <Widget>[
PageTurnImage(
image: MemoryImage(snapshot.data.bytes),
),
PageTurnImage(
image: MemoryImage(snapshot.data.bytes),
),
PageTurnImage(
image: MemoryImage(snapshot.data.bytes),
),
],
);
try it and give me a feedback and up vote if it work

How to select index and delete it's respectively data from API in flutter?

I'm getting images from API and show them into grid view but the requirement is that I press long on any index of the image,a selected icon should be visible on that index image.
but the problem is that when I press long at any index, the selected icon is visible on all indexes.
ScreenShot:
to resolve this, I made model class, in which there are datatype
first is boolean variable(isSelected) for each index, another is for PhotoDetails which is fetching from API, but unable to handle it with FutureBuilder, because it rebuilds the build method when I performed setState and isSelected becomes false.
Code:
Model class:
class Photos{
PhotoDetail photoDetail;
bool isSelected;
Photos({this.photoDetail, this.isSelected});
}
FutureBuilder:
Expanded(
child: FutureBuilder<PhotoModel>(
future: _photoApi.getPhotosByUserList(
token: widget.tokenId,
contactId: widget.userContent.id,
),
builder:(BuildContext context, AsyncSnapshot<PhotoModel> snapshot){
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError){
return Center(child: new Text('Error: ${snapshot.error}'));
}
List<Photos> photos =[];
snapshot.data.content.forEach((element) {
photos.add(
Photos(
isSelected: false,
photoDetail: element
)
);
});
print("photos photos photos length:${photos.length}");
return photos.length>0?
sliverGridWidget(context,photos)
:Container(
alignment: Alignment.center,
child: Text("Empty"),
);
}
)
)
Images in grid view:
Widget sliverGridWidget(BuildContext context, List<Photos> listPhotoDetail){
return StaggeredGridView.countBuilder(
padding: const EdgeInsets.all(8.0),
crossAxisCount: 6,
itemCount: listPhotoDetail.length,
itemBuilder: (context, index){
return InkWell(
onLongPress: (){
setState(() {
enable = true;
print("iinnndexxxxxxx:$index");
// listPhotoDetail[index].isSelected = true;
});
},
child: Container(
alignment: Alignment.bottomRight,
decoration: BoxDecoration(
color:Colors.grey[100],
image: DecorationImage(
image: NetworkImage(listPhotoDetail[index].photoDetail.image.fileUrl),
fit: BoxFit.cover
)
),
child:enable?
Image.asset('assets/icons/selected.png')
:Container()
),
);
},
staggeredTileBuilder: (index)=> view ?StaggeredTile.count(6,6):StaggeredTile.count(2,2),
mainAxisSpacing: 8.0,
crossAxisSpacing:8.0,
);
}
To solve it try to use a specific key for every image