I'm trying to load multiple images (from assets or cache) into the pdf, but it does not work.
There are no errors, the pdf is created, but is corrupted somehow and does not want to open.
Everything is 100% if there is only 1 image, or if all the images are the same image.
What am I doing wrong?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(backgroundColor: Colors.red, title: Text('Images')),
body: Center(child: HomeScreen())));
}
}
class HomeScreen extends StatefulWidget {
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: <Widget>[
RaisedButton(
color: Colors.red,
onPressed: () async {
writeOnPdf();
await savePdf();
},
child: Text('Create Pdf'),
),
],
),
));
}
}
final pdf = pw.Document();
writeOnPdf() async {
var assetImage = PdfImage.file(
pdf.document,
bytes: (await rootBundle.load('assets/aa.png')).buffer.asUint8List(),
);
var otherImage = PdfImage.file(
pdf.document,
bytes: (await rootBundle.load('assets/out.png')).buffer.asUint8List(),
);
pdf.addPage(pw.MultiPage(
pageFormat: PdfPageFormat.a4,
margin: pw.EdgeInsets.all(32),
build: (pw.Context context) {
return <pw.Widget>[
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.center,
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: <pw.Widget>[
pw.ClipRect(
child: pw.Container(
width: 550,
height: 250,
child: pw.Image(assetImage),
),
),
]),
pw.ClipRect(
child: pw.Container(
width: 100,
height: 100,
child: pw.Image(otherImage),
),
)
];
}));
}
final bool isHTML = false;
Future savePdf() async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
File file = File('$tempPath/1234.pdf');
file.writeAsBytesSync(pdf.save());
print(tempDir.listSync());
}
It would be ideal if I could load one image from the assets and one image from the cache. But at the moment nothing works.
Any advice would be appreciated.
DavBfr gave the answer. "It's an issue with async/await."
Multiple Images #425]1
RaisedButton(
color: Colors.red,
onPressed: () async {
final pdf = await writeOnPdf();
await savePdf(pdf);
},
child: const Text('Create Pdf'),
),
try like that. its not influent way but it works for multiple images in same pdf page.
final ByteData bytes1 = await rootBundle.load('assets/images/logo.png');
final Uint8List img1 = bytes1.buffer.asUint8List();
final ByteData bytes2 = await rootBundle.load('assets/images/1.jpg');
final Uint8List img2 = bytes2.buffer.asUint8List();
final ByteData bytes3 = await rootBundle.load('assets/images/2.png');
final Uint8List img3 = bytes3.buffer.asUint8List();
pw.Image imgty(Uint8List img) {
return pw.Image(
pw.MemoryImage(
img,
),
fit: pw.BoxFit.contain);
}
and use them in pdf page
Container(
height: 111,
width: 111,
child: imgty(img1),)
Related
Iam trying to make a flutter windows application that can connect to a scanner device and get the image data from it, but i can't seem to find any packages that help with that so if anyone can give me the link of a library or a tutorial that can help with that.
Thanks in advance.
i have tried searching but all the packages i have found are directed to android and ios only.
Is this what you want?
I've ported the C++ code from https://github.com/twain/twain-samples and created an open source project https://github.com/yushulx/flutter_twain_scanner.
The plugin is available on https://pub.dev/packages/flutter_twain_scanner
The full code to scan documents from a connected scanner on Windows desktop:
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_twain_scanner/flutter_twain_scanner.dart';
import 'dart:ui' as ui;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _flutterTwainScannerPlugin = FlutterTwainScanner();
String? _documentPath;
List<String> _scanners = []; // Option 2
String? _selectedScanner;
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion = await _flutterTwainScannerPlugin.getPlatformVersion() ??
'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter TWAIN Scanner'),
),
body: Stack(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
MaterialButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
List<String>? scanners =
await _flutterTwainScannerPlugin
.getDataSources();
if (scanners != null) {
setState(() {
_scanners = scanners;
});
}
},
child: const Text('List Scanners')),
DropdownButton(
hint: Text(
'Select a scanner'), // Not necessary for Option 1
value: _selectedScanner,
onChanged: (newValue) {
setState(() {
_selectedScanner = newValue;
});
},
items: _scanners.map((location) {
return DropdownMenuItem(
child: new Text(location),
value: location,
);
}).toList(),
),
MaterialButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
if (_selectedScanner != null) {
int index =
_scanners.indexOf(_selectedScanner!);
String? documentPath =
await _flutterTwainScannerPlugin
.scanDocument(index);
setState(() {
_documentPath = documentPath;
});
}
},
child: const Text('Scan Document')),
]),
),
SizedBox(
height: 600,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: _documentPath == null
? Image.asset('images/default.png')
: Image.file(
File(_documentPath!),
fit: BoxFit.contain,
width: 600,
height: 600,
),
))
],
),
],
),
),
);
}
}
I am using https://pub.dev/packages/gallery_saver for saving compressed images in a folder named Image Resizer now I want to open that saved images folder
You can use the image_picker plugin in Flutter for opening the images from the device. It will provide an option to open the gallery through which you can open the folder of saved images.
You can get the image_picker plugin here.
below is the solution i did it
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
//import 'package:ext_storage/ext_storage.dart';
import 'ext_storage.dart';
class SavedFiles extends StatefulWidget {
#override
_SavedFilesState createState() => _SavedFilesState();
}
class _SavedFilesState extends State<SavedFiles> {
List<dynamic> listImagePath = List<dynamic>();
Future<String> _getPath() {
return ExtStorage.getExternalStoragePublicDirectory(ExtStorage.PICTURES);
}
_fetchFiles(Directory dir) {
List<dynamic> listImage = List<dynamic>();
dir.list().forEach((element) {
RegExp regExp =
new RegExp("\.(gif|jpe?g|tiff?|png|webp|bmp)", caseSensitive: false);
// Only add in List if path is an image
if (regExp.hasMatch('$element')) listImage.add(element);
setState(() {
listImagePath = listImage;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Saved Images'),backgroundColor: Colors.orange,),
body: Column(
children: [
Expanded(
child: FutureBuilder(
future: _getPath(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// print('data');
var dir = Directory(snapshot.data);
_fetchFiles(dir);
// return Text(snapshot.data);
return Container();
} else {
return Text("Loading");
}
},
),
),
// Expanded(child: Text('sss')),
Expanded(
flex: 19,
child: GridView.count(
primary: false,
padding: const EdgeInsets.all(5),
crossAxisSpacing: 2,
mainAxisSpacing: 2,
crossAxisCount: 3,
children: _getListImg(listImagePath),
),
)
],
)
);
}
List<Widget> _getListImg(List<dynamic> listImagePath) {
List<Widget> listImages = List<Widget>();
for (var imagePath in listImagePath) {
listImages.add(
Container(
padding: const EdgeInsets.all(8),
child: Image.file(imagePath, fit: BoxFit.cover),
),
);
}
return listImages;
}
}
I take a picture with phone's camera, then resize it and convert it to base64 string. However, after those manipulations, base64 string that I get doesn't seem to be valid.
I try to convert it back to image on this website https://codebeautify.org/base64-to-image-converter . After I click generate image, nothing happens, however sample on their website works. Tried on the other websites and still no luck.
My code:
import 'package:image/image.dart' as img;
import 'package:image_picker/image_picker.dart';
File _photo;
String photoBase64;
Future getImage(ImageSource source) async {
var photo = await ImagePicker.pickImage(source: source);
img.Image image = img.decodeImage(photo.readAsBytesSync());
img.Image imageResized = img.copyResize(image, width: 120);
setState(() {
_photo = photo;
List<int> imageBytes = imageResized.getBytes();
photoBase64 = base64Encode(imageBytes);
});
}
I tried base64UrlEncode() too, however the issue still remains. String I am trying to convert back to image is photoBase64. My goal is to send it in a body of a POST request later. What exactly am I doing wrong here?
Thank you
You can copy paste run full code below
You can use package https://pub.dev/packages/flutter_native_image to get resized image
imageResized = await FlutterNativeImage.compressImage(photo.path,
quality: 100, targetWidth: 120, targetHeight: 120);
...
List<int> imageBytes = imageResized.readAsBytesSync();
working demo
full code
import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'dart:convert';
import 'package:flutter_native_image/flutter_native_image.dart';
void main() => runApp(MyApp());
File _photo;
String photoBase64;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
File imageResized;
Future getImage(ImageSource source) async {
var photo = await ImagePicker.pickImage(source: source);
imageResized = await FlutterNativeImage.compressImage(photo.path,
quality: 100, targetWidth: 120, targetHeight: 120);
setState(() {
_photo = photo;
List<int> imageBytes = imageResized.readAsBytesSync();
photoBase64 = base64Encode(imageBytes);
print(photoBase64);
});
}
void _incrementCounter() {
getImage(ImageSource.camera);
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
imageResized == null ? Container() : Image.file(imageResized),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
my solution
Future getImage() async {
pickedFile =
await _picker.getImage(source: ImageSource.gallery, imageQuality: 50);
setState(() {
if (pickedFile != null) {
file = File(pickedFile!.path);
final bytes =
file!.readAsBytesSync();
img64 = base64Encode(bytes);
} else {
print('No image selected.');
}
});
}
I tried to find how to select and upload multiple images in flutter but most of the plugins are not working or I did not understand them well. I found little application but it selects and upload only one picture. How to change this code that user can select and upload multiple pictures or is there any other alternatives. Please write in details, i am freshman in coding. Thanks in advance.
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'dart:convert';
import 'package:http_parser/http_parser.dart';
import 'package:toast/toast.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Upload Demo',
theme: ThemeData(primarySwatch: Colors.pink),
home: ImageInput());
}
}
class ImageInput extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ImageInput();
}
}
class _ImageInput extends State<ImageInput> {
// To store the file provided by the image_picker
File _imageFile;
// To track the file uploading state
bool _isUploading = false;
String baseUrl = 'http://YOUR_IPV4_ADDRESS/flutterdemoapi/api.php';
void _getImage(BuildContext context, ImageSource source) async {
File image = await ImagePicker.pickImage(source: source);
setState(() {
_imageFile = image;
});
// Closes the bottom sheet
Navigator.pop(context);
}
Future<Map<String, dynamic>> _uploadImage(File image) async {
setState(() {
_isUploading = true;
});
// Find the mime type of the selected file by looking at the header bytes of the file
final mimeTypeData =
lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
// Intilize the multipart request
final imageUploadRequest =
http.MultipartRequest('POST', Uri.parse(baseUrl));
// Attach the file in the request
final file = await http.MultipartFile.fromPath('image', image.path,
contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
// Explicitly pass the extension of the image with request body
// Since image_picker has some bugs due which it mixes up
// image extension with file name like this filenamejpge
// Which creates some problem at the server side to manage
// or verify the file extension
imageUploadRequest.fields['ext'] = mimeTypeData[1];
imageUploadRequest.files.add(file);
try {
final streamedResponse = await imageUploadRequest.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> responseData = json.decode(response.body);
_resetState();
return responseData;
} catch (e) {
print(e);
return null;
}
}
void _startUploading() async {
final Map<String, dynamic> response = await _uploadImage(_imageFile);
print(response);
// Check if any error occured
if (response == null || response.containsKey("error")) {
Toast.show("Image Upload Failed!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
} else {
Toast.show("Image Uploaded Successfully!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
}
void _resetState() {
setState(() {
_isUploading = false;
_imageFile = null;
});
}
void _openImagePickerModal(BuildContext context) {
final flatButtonColor = Theme.of(context).primaryColor;
print('Image Picker Modal Called');
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150.0,
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Text(
'Pick an image',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
height: 10.0,
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Camera'),
onPressed: () {
_getImage(context, ImageSource.camera);
},
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Gallery'),
onPressed: () {
_getImage(context, ImageSource.gallery);
},
),
],
),
);
});
}
Widget _buildUploadBtn() {
Widget btnWidget = Container();
if (_isUploading) {
// File is being uploaded then show a progress indicator
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: CircularProgressIndicator());
} else if (!_isUploading && _imageFile != null) {
// If image is picked by the user then show a upload btn
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
child: Text('Upload'),
onPressed: () {
_startUploading();
},
color: Colors.pinkAccent,
textColor: Colors.white,
),
);
}
return btnWidget;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Upload Demo'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 40.0, left: 10.0, right: 10.0),
child: OutlineButton(
onPressed: () => _openImagePickerModal(context),
borderSide:
BorderSide(color: Theme.of(context).accentColor, width: 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.camera_alt),
SizedBox(
width: 5.0,
),
Text('Add Image'),
],
),
),
),
_imageFile == null
? Text('Please pick an image')
: Image.file(
_imageFile,
fit: BoxFit.cover,
height: 300.0,
alignment: Alignment.topCenter,
width: MediaQuery.of(context).size.width,
),
_buildUploadBtn(),
],
),
);
}
}
I used that package:
dependencies:
multi_image_picker: ^4.6.7
Ex:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error;
#override
void initState() {
super.initState();
}
Widget buildGridView() {
if (images != null)
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
else
return Container(color: Colors.white);
}
Future<void> loadAssets() async {
setState(() {
images = List<Asset>();
});
List<Asset> resultList;
String error;
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
);
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
if (error == null) _error = 'No Error Dectected';
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Add dependecy of image_picker:
image_picker: ^0.8.4+3
Then make a method for selectImages():
final ImagePicker imagePicker = ImagePicker();
List<XFile>? imageFileList = [];
void selectImages() async {
final List<XFile>? selectedImages = await
imagePicker.pickMultiImage();
if (selectedImages!.isNotEmpty) {
imageFileList!.addAll(selectedImages);
}
print("Image List Length:" + imageFileList!.length.toString());
setState((){});
}
Create a builder for showing selected Images:
return Scaffold(
appBar: AppBar(
title: Text('Multiple Images'),
),
body: SafeArea(
child: Column(
children: [
ElevatedButton(
onPressed: () {
selectImages();
},
child: Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: imageFileList!.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return Image.file(File(imageFileList![index].path),
fit: BoxFit.cover,);
}),
),
),
],
),
));
Complete Source code available in github link...
https://github.com/NishaJain24/multi_image_picker
Is it possible to edit image like to rotate and to add text over image. Is there any plugin for that? I need an editor for image to add texts with various fonts and colors. Thank you
you should use RepaintBoundary to capture it as a widget and to overlap widget use stack.
Look at this code how to capture widget as a image.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
GlobalKey _globalKey = new GlobalKey();
bool inside = false;
Uint8List imageInMemory;
Future<Uint8List> _capturePng() async {
try {
print('inside');
inside = true;
RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData.buffer.asUint8List();
// String bs64 = base64Encode(pngBytes);
// print(pngBytes);
// print(bs64);
print('png done');
setState(() {
imageInMemory = pngBytes;
inside = false;
});
return pngBytes;
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return RepaintBoundary(
key: _globalKey,
child: new Scaffold(
appBar: new AppBar(
title: new Text('Widget To Image demo'),
),
body: SingleChildScrollView(
child: Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'click the button below to capture image',
),
new RaisedButton(
child: Text('capture Image'),
onPressed: _capturePng,
),
inside ? CircularProgressIndicator()
:
imageInMemory != null
? Container(
child: Image.memory(imageInMemory),
margin: EdgeInsets.all(10))
: Container(),
],
),
),
)),
);
}
}