I have been trying to add text on top of an image and then allow the user to share the image. I'm having two issues that I can't seem to figure out.
The text is overflowing off the screen and not wrapping in the TextPainter when adding to Canvas.
I have been trying to share the image using a FloatingActionButton. The issue I am having is sharing an actual image and not image string. I have been using the esys_flutter_share package to try and achieve it but I get an error. I really just want to share the Image that I wrote the text on top of.
Unhandled Exception: type 'Image' is not a subtype of type 'String'
.
Any help is greatly appreciated.
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/services.dart' show rootBundle;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#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> {
var _imageUrl = 'https://imageurl.png';
var _img;
var nimg;
#override
void initState() {
_showImg();
super.initState();
}
#override
Widget build(BuildContext context) {
var widget =
_img != null ? Image.memory(_img) : Text('pleace click button');
return Scaffold(
appBar: AppBar(),
body: Center(child: widget),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final ByteData bytes = await rootBundle.load(nimg);
await Share.file('esys image', '$nimg', bytes.buffer.asUint8List(), 'image/png');
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
_showImg() async{
var uri = Uri.parse(_imageUrl);
var httpClient = HttpClient();
var request = await httpClient.getUrl(uri);
var response = await request.close();
var imageData = <int>[];
await response.forEach((data) async {
imageData.addAll(data);
});
ui.Image image =
await decodeImageFromList(Uint8List.fromList(imageData));
var pictureRecorder = ui.PictureRecorder();
var canvas = Canvas(pictureRecorder);
var paint = Paint();
paint.isAntiAlias = true;
var src = Rect.fromLTWH(
0.0, 0.0, image.width.toDouble(), image.height.toDouble());
var dst = Rect.fromLTWH(
0.0, 0.0, image.width.toDouble(), image.height.toDouble());
canvas.drawRect(Rect.fromLTWH(0.0, 0.0, 200.0, 200.0), paint);
canvas.drawImageRect(image, src, dst, paint);
//Add text on image
TextSpan span = new TextSpan(
style: new TextStyle(color: Colors.white, fontSize: 150.0,
fontFamily: 'Roboto'), text: "Here is some great text to put on top");
TextPainter tp = new TextPainter(
text: span, textDirection: TextDirection.ltr, textAlign: TextAlign.center);
tp.layout();
tp.paint(canvas, new Offset(image.width/2 - image.width/2 /2, image.height/2 - image.height/2 /3));
var pic = pictureRecorder.endRecording();
ui.Image img = await pic.toImage(image.width, image.height);
var byteData = await img.toByteData(format: ui.ImageByteFormat.png);
var buffer = byteData.buffer.asUint8List();
//Assign image to be shared
nimg = img;
//Set the image as the child in the body
setState(() {
_img = buffer;
});
}
}
The easiest way to share image with text:
Create a Stack and put Image in it, positioned the Text widget anywhere you want on the image.
Wrap Stack with RepaintBoundary
Screenshot with
RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
Image image = await boundary.toImage();
Share with esys_flutter_share
PS. Of course you can also use image packages to add text to image.
esys_flutter_share is not having capability of sharing both data type at a same time. I have faced this issue and fixed by taking this package in my code and editing it's methods that allows us to share both data type. Here is the edited package files.
How to take packages in local code?
Checkout this answer
The updated code url is this, download it and follow above answer.
Related
I have developed an image uploading API and called it an async function I need a spinner to make load until it fetches the result and after fetching the result it has to show the response received in an alert box
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
My whole flutter code is here
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool _isInAsyncCall = false;
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
inAsyncCall: _isInAsyncCall,
// demo of some additional parameters
opacity: 0.5,
progressIndicator: CircularProgressIndicator(),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
modal_progress_hud library helped lot on making this task
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_progress_hud/flutter_progress_hud.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool showSpinner = false;
uploadImage() async {
setState(() {
showSpinner = true;
});
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send();
http.Response res = await http.Response.fromStream(response);
print("reached");
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
showSpinner = false;
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
);
}
}
How can I improving the aliasing of Image objects painted on a scaled Canvas? For example, for the following:
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Future<ui.Image> loadUiImage(String imageAssetPath) async {
final ByteData data = await rootBundle.load(imageAssetPath);
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final image = await loadUiImage("assets/thierry.png");
runApp(MyApp(image));
}
class MyApp extends StatelessWidget {
final ui.Image image;
const MyApp(this.image, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
elevation: 0,
),
body: Container(
height: 300,
width: 300,
child: CustomPaint(
painter: CanvasPainter(this.image),
),
),
));
}
}
class CanvasPainter extends CustomPainter {
final ui.Image image;
CanvasPainter(this.image);
#override
void paint(Canvas canvas, Size size) {
canvas.save();
canvas.scale(size.width / image.width);
canvas.drawImage(
image,
Offset.zero,
ui.Paint()
..isAntiAlias = true
..filterQuality = ui.FilterQuality.high);
canvas.restore();
}
#override
bool shouldRepaint(CanvasPainter oldDelegate) => true;
}
This is what I see in the emulator:
If I scale the image in an image viewer to similar dimensions, I see this:
The original image is 5814 x 3828.
Notice that the long horizontal lines are more aliased in the version displayed on the emulator. If I don't scale, I don't see this level of aliasing.
To get the best image quality for my scenario, I had to use filterQuality = ui.FilterQuality.medium. According to the documentation, "when scaling down, medium provides the best quality especially when scaling an image to less than half its size". In my case the scale factor is less than half, so that explains why quality is better with medium.
For reference, here's what the above image looks like with medium:
Is there a way to resize an image without having it previously written to storage?
I am using a pdf library that for the images needs the bytes of it.
What I do is get image with an http.get and I get the bytes to put it in the pdf.
The problem is that I need to resize the image BEFORE putting it in the pdf.
The only thing I have is the url of the image or the uint8list
Response response = await http.get(imageUrl);
Uint8List imgBytes = response.bodyBytes;
Later:
Image(
PdfImage.file(pdf.document,
bytes: imageBytes)
),
Pdf lib I use: https://pub.dev/packages/pdf
You can copy paste run full code below
You can use ui.instantiateImageCodec and specify targetHeight and targetWidth
You can see output image size become smaller after resize
code snippet
String imageUrl = 'https://picsum.photos/250?image=9';
http.Response response = await http.get(imageUrl);
originalUnit8List = response.bodyBytes;
ui.Image originalUiImage = await decodeImageFromList(originalUnit8List);
ByteData originalByteData = await originalUiImage.toByteData();
print('original image ByteData size is ${originalByteData.lengthInBytes}');
var codec = await ui.instantiateImageCodec(originalUnit8List,
targetHeight: 50, targetWidth: 50);
var frameInfo = await codec.getNextFrame();
ui.Image targetUiImage = frameInfo.image;
ByteData targetByteData =
await targetUiImage.toByteData(format: ui.ImageByteFormat.png);
print('target image ByteData size is ${targetByteData.lengthInBytes}');
targetlUinit8List = targetByteData.buffer.asUint8List();
output of working demo
I/flutter (17023): original image ByteData size is 250000
I/flutter (17023): target image ByteData size is 4060
working demo
full code
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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> {
Uint8List targetlUinit8List;
Uint8List originalUnit8List;
void _resizeImage() async {
String imageUrl = 'https://picsum.photos/250?image=9';
http.Response response = await http.get(imageUrl);
originalUnit8List = response.bodyBytes;
ui.Image originalUiImage = await decodeImageFromList(originalUnit8List);
ByteData originalByteData = await originalUiImage.toByteData();
print('original image ByteData size is ${originalByteData.lengthInBytes}');
var codec = await ui.instantiateImageCodec(originalUnit8List,
targetHeight: 50, targetWidth: 50);
var frameInfo = await codec.getNextFrame();
ui.Image targetUiImage = frameInfo.image;
ByteData targetByteData =
await targetUiImage.toByteData(format: ui.ImageByteFormat.png);
print('target image ByteData size is ${targetByteData.lengthInBytes}');
targetlUinit8List = targetByteData.buffer.asUint8List();
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
originalUnit8List == null
? Container()
: Image.memory(originalUnit8List),
targetlUinit8List == null
? Container()
: Image.memory(targetlUinit8List),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _resizeImage,
tooltip: 'Resize',
child: Icon(Icons.add),
),
);
}
}
I'm trying to use firebase_ml_vision with image_picker.
Here's the code:
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
void main() => runApp(MyHomePage());
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FirebaseVisionImage visionImage;
final BarcodeDetector barcodeDetector = FirebaseVision.instance.barcodeDetector();
final TextRecognizer textRecognizer = FirebaseVision.instance.textRecognizer();
final ImageLabeler labeler = FirebaseVision.instance.imageLabeler(
ImageLabelerOptions(confidenceThreshold: 0.75),
);
final FaceDetector faceDetector = FirebaseVision.instance.faceDetector();
void detections() async {
final VisionText visionText = await textRecognizer.processImage(visionImage);
}
File _image;
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
var something = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
this.visionImage = FirebaseVisionImage.fromFile(something);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Picker Example'),
),
body: Center(
child: _image == null
? Text('No image selected.')
: Image.file(_image),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
tooltip: 'Pick Image',
child: Icon(Icons.add_a_photo),
),
);
}
}
But I am unable to use the File datatype and this is the error I get:
Error
Here's what I get when I run flutter doctor:
Doctor
And here's what I get when I run flutter --version:
Version
I'm very new to Flutter so please let me know if you need anything else.
Thank you!
File type is in dart:io, which you are missing. Add import 'dart:io'; to the top of the file.
I am trying to create a new Image from two existing images using Canvas
one Img from asset ("asset image")
one Img from network
To achieve that first problem is to draw "asset image" on Canvas using drawImage.. this is where I am facing the problem.
drawCircle is working fine, But for using drawImage as per following code, it is outputting blank image.
I am new to using Canvas and experimenting, any help appreciated..
Complete code..
import 'package:flutter/material.dart';
//import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:async';
//import 'dart:io';
import 'package:flutter/services.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Image _image;
ui.Image imagetoDraw;
#override
void initState() {
super.initState();
_image = new Image.network(
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png',
);
getImageFromAsset();
}
getImageFromAsset() async {
imagetoDraw = await load('images/loading.png');
print('...getImageFromAsset done');
}
Future<ui.Image> load(String asset) async {
ByteData data = await rootBundle.load(asset);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
_generateImage() {
_generate().then((val) => setState(() {
_image = val;
}));
}
Future<Image> _generate() async {
ui.PictureRecorder recorder = new ui.PictureRecorder();
Canvas c = new Canvas(recorder);
var rect = new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
c.clipRect(rect);
final paint = new Paint();
paint.strokeWidth = 2.0;
paint.color = const Color(0xFF333333);
paint.style = PaintingStyle.fill;
final offset = new Offset(50.0, 50.0);
// c.drawCircle(offset, 40.0, paint);
c.drawImage(imagetoDraw, offset, paint);
var picture = recorder.endRecording();
final pngBytes = await picture
.toImage(100, 100)
.toByteData(format: ui.ImageByteFormat.png);
var image = Image.memory(pngBytes.buffer.asUint8List());
return image;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_image,
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _generateImage,
tooltip: 'Generate',
child: new Icon(Icons.add),
),
);
}
}
The image asset path seems to be the problem. I can not say that for sure, as There is no pubspec.yaml asset declarations here.
Let's assume that you have added assets in pubspec.yaml as below:
assets:
- assets/images/image_01.png
- assets/images/image_02.jpg
In that case, you need to specify the path of asset 'assets/images/image_01.png'.
Means the exact path that is defined inside the pubspec.yaml file.
i.e. In your case, imagetoDraw = await load('assets/images/image_01.png');
Tip: You can directly use Image.asset('assets/images/image_01.png'); to get the image from assets.
The source I have referred to: Load Image from assets in flutter
I hope this helps.