page curl effect using pdf file - flutter

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

Related

Get to know when CacheNetworkImage successfully show network image

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

Flutter display gif from Uint8List

I need to display gif in my Flutter application. From the backend I get the gif as an Uint8List list from the response. Can you help me please how can I display this in the screen?
My code is here:
widget.session
.get('/api/caff/getCaff/' + widget.gifId.toString())
.then((response) async {
if (response.statusCode == 200) {
Uint8List bytes = response.bodyBytes;
_gifFile = File.fromRawPath(bytes); // tried this but didn't work
} else {
CaffToast.showError(
'Something went wrong! Please check your network connection!');
}
});
And I tried to display it as a file image but it didnt work:
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
_gifFile == null ? Container() : Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(_gifFile!))),
),
],
),
);
}
Do you have any suggestions how can I solve this problem?
There's no need to write your data to a file. The image data is already in memory so just display it. Just use a MemoryImage image provider instead
I'm not sure how you're getting the data from your network call to your build method so I'm using placeholders, but just do it the same was that you did when you were using a file.
_bytes = response.bodyBytes;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
_gifFile == null ? Container() : Container(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(_bytes!))),
),
],
),
);
}

Flutter FutureProvider Value Not Updating In Builder Method

The Problem
I am building a basic app in Flutter that gets the user's location and displays nearby places in a swipe-card format similar to Tinder. I managed to implement geolocation however when using FutureProvider/Consumer I'm experiencing a weird bug where the user's relative distance to the place is overwritten with the first distance value in the card deck. Although I am new to flutter and the Provider package, I believe there is a simple fix to this.
Side note: After searching around on Google, I attempted to use FutureProvider.value() to prevent the old value from updating but had no luck.
Thank you in advance for any assistance or direction!
A Quick Demo
Packages Used
card_swipe.dart
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:provider/provider.dart';
import 'package:swipe_stack/swipe_stack.dart';
import '../services/geolocator_service.dart';
import '../models/place.dart';
class CardSwipe extends StatelessWidget {
#override
Widget build(BuildContext context) {
final _currentPosition = Provider.of<Position>(context);
final _placesProvider = Provider.of<Future<List<Place>>>(context);
final _geoService = GeoLocatorService();
return FutureProvider(
create: (context) => _placesProvider,
child: Scaffold(
backgroundColor: Colors.grey[300],
body: (_currentPosition != null)
? Consumer<List<Place>>(
builder: (_, places, __) {
return (places != null)
? Column(
children: [
SizedBox(height: 10.0),
Container(
margin: EdgeInsets.only(top: 120.0),
height: 600,
child: SwipeStack(
children: places.map((place) {
return SwiperItem(builder:
(SwiperPosition position,
double progress) {
return FutureProvider(
create: (context) =>
_geoService.getDistance(
_currentPosition.latitude,
_currentPosition.longitude,
place.geometry.location.lat,
place.geometry.location.lng),
child: Consumer<double>(
builder: (_, distance, __) {
return (distance != null)
? Center(
child: Card(
child: Container(
height: 200,
width: 200,
child: Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Text(place.name),
Text(
'${(distance / 1609).toStringAsFixed(3)} mi'), // convert meter to mi
],
),
),
),
),
)
: Container();
}),
);
});
}).toList(),
visibleCount: 3,
stackFrom: StackFrom.Top,
translationInterval: 6,
scaleInterval: 0.03,
onEnd: () => debugPrint("onEnd"),
onSwipe: (int index, SwiperPosition position) =>
debugPrint("onSwipe $index $position"),
onRewind:
(int index, SwiperPosition position) =>
debugPrint("onRewind $index $position"),
),
),
],
)
: Center(
child: CircularProgressIndicator(),
);
},
)
: Center(
child: CircularProgressIndicator(),
),
),
);
}
}
geolocator_service.dart
import 'package:geolocator/geolocator.dart';
class GeoLocatorService {
final geolocator = Geolocator();
Future<Position> getLocation() async {
return await geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
locationPermissionLevel: GeolocationPermission.location,
);
}
Future<double> getDistance(
double startLat, double startLng, double endLat, double endLng) async {
return await geolocator.distanceBetween(startLat, startLng, endLat, endLng);
}
}
place.dart
Quick note: Place class does import a custom class called geometry.dart however this is purely for structuring the Place object and I'm certain it doesn't affect the bug. Therefore, it has been omitted.
import './geometry.dart';
class Place {
final String name;
final Geometry geometry;
Place(this.name, this.geometry);
Place.fromJson(Map<dynamic, dynamic> parsedJson)
: name = parsedJson['name'],
geometry = Geometry.fromJson(
parsedJson['geometry'],
);
}
You have to add a key to the SwiperItem with some unique value (like the name of the place) since currently flutter thinks that the widget has stayed the same so the Consumer gets the state of the old topmost widget.
By adding the key you tell flutter that you removed the topmost widget and the new topmost is in fact the second widget

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

state change not prompting ondidchange

I have a page which is connected to appstate and viewmodel ,
what i'm doing in this page is picking an image from gallery and then sending it to the server , when i send i prompt a message to the user so he can see the loading ,
when the upload epic ends there is an action than goes to the reducer which change the state so the ui in the page will change ,
the reducer receives it and return a state , but the onDidChange not prompt right after ,
here's some code:
page build method :
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, AddRefundViewModel>(
converter: addRefundConverter,
onInitialBuild: (AddRefundViewModel vm) => {vm.doInitializeDto()},
builder: (context, vm) {
return MaterialApp(
title: 'Test',
home: Scaffold(
body: Column(
children: <Widget>[
Container(
child: new Column(
children: <Widget>[
RefundHeader1(context, vm),
RefundHeader(context, vm),
],
),
decoration: new BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.bottomLeft,
colors: [primary, primaryGradient]),
),
width: MediaQuery.of(context).size.width * 1,
height: MediaQuery.of(context).size.width * 0.4,
),
//Text(vm.c),
refundRowSum(vm),
refundRowDate(context, vm),
refundRowCurrent(context, vm),
refundRowRefund(context, vm),
refundRowAtm(context, vm),
Expanded(child: Container(
child: GestureDetector(onTap: () {
//print("tap");}
// myFocusNode.dispose();},
if (myFocusNode != null) {
FocusScope.of(context).requestFocus(new FocusNode());
}
}),
)),
proceedMessage(context, vm),
],
),
bottomNavigationBar: BottomAppBar(
child: proceedButton(context, vm),
),
resizeToAvoidBottomPadding: false,
));
},
onDidChange: (vm) {
print('---!!!!-----<<<<<<<<<<<<<< onDidChange >>>>>>>>>>>-----!!!------');
if (vm.addRefundDto.tmpFileStat == TmpFileStat.BadExt) {
showSendReceiptDialog2(context, TmpFileStat.BadExt);
vm.doInitTmpFileStat();
} else if (vm.addRefundDto.tmpFileStat == TmpFileStat.TooLarge) {
showSendReceiptDialog2(context, TmpFileStat.TooLarge);
vm.doInitTmpFileStat();
}
else if (vm.addRefundDto.uploadRefundStatus ==
RefundUpload.Success ||vm.addRefundDto.uploadRefundStatus ==
RefundUpload.Started ||
vm.addRefundDto.uploadRefundStatus == RefundUpload.Failed) {
Navigator.pop(context);
showSendReceiptDialog(context, vm.addRefundDto.uploadRefundStatus);
}
},
);}
reducer :
case GotRefund:
print('----=== GotRefund add new refund reducer ===-----');
AddRefundDto refundDto = state.refundDto;
refundDto.uploadRefundStatus=RefundUpload.Success;
state.refundDto= refundDto;
return new AddRefundState(
fileDto: null,
byteImage: null,
refundDto: state.refundDto,
);
ADDED ViewModel :
import 'package:flutter/widgets.dart';
import 'package:iai/models/add_refund_dto.dart';
import 'package:iai/store/ui/ui_state.dart';
import 'package:iai/theme/images.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:iai/models/currency.dart';
class AddRefundViewModel {
bool fileAttachment;
DateTime pickedDate;
double amount;
bool isAtm;
bool canProceed;
AddRefundDto addRefundDto;
//final List<String> refundList;
final Map<String, AssetImage> refundList;
final Map<String, IconData> currencyList;
final List<Currency> currencyList2;
Function saveCurrentViewModel;
Function sendDtoToServer;
Function openGallery;
Function openCamera;
Function doInitializeDto;
Function doRemoveTmpImage;
Function doInitTmpFileStat;
final Function search;
final String searchTerm;
final bool showSearch;
AddRefundViewModel({
this.openCamera,
this.openGallery,
this.addRefundDto,
this.search,
this.searchTerm,
this.showSearch,
this.doInitializeDto,
this.doRemoveTmpImage,
this.doInitTmpFileStat,
this.refundList,
this.sendDtoToServer,
this.saveCurrentViewModel,
this.pickedDate,
this.amount,
this.currencyList,
this.isAtm,
this.canProceed,
this.currencyList2,
this.fileAttachment,
});
}
thanks to everyone that can contribute
Can you also post your AddRefundViewModel class in here? I think the on onDidChange is called based on those variables.