I want to have a feature in my Flutter App like Snapcodes on Snapchat. Basically custom “QR” Codes (they aren’t scannable to anything besides Snapchat so not really QR codes) with the app icon in them that launch a users profile when scanned in the app. I can make a simple version of this with plain QR codes using a pub package and Firebase Deeplinking but that isn’t what I want. What I think needs to be done is to create my own “qr” code generator that makes code holding a uid and then a way to decode them when scanned but I have no idea how to do that. Any ideas or pub packages that can do this?
Added for comment:
So I was looking through my old project and I got this ( The Idea of the source code below is that your taking a screenshot programmatically):
import 'dart:ui' as ui; //
final _renderObjectKey = new GlobalKey();
int randomDigit;
Timer _timer;
String userID;
void startTimer() {
//Generate random numbers attached to userID to make qrcode change periodically: improve on this!!!
var range = new Random();
_timer = new Timer.periodic(Duration(seconds: 10), (timer) {
setState(() {
randomDigit = range.nextInt(800) + 100;
});
});
}
#override
void initState() {
super.initState();
userID = auth.currentUser.uid; //if you are using firebaseauth
startTimer();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
......
// The widget below is placed inside the body of scaffold
RepaintBoundary(
key: _renderObjectKey,
child:Stack(
alignment: Alignment.center,
children:[
BarcodeWidget(
color: Color(0xFFF9F9F9),
barcode: Barcode.qrCode(
errorCorrectLevel:BarcodeQRCorrectionLevel.high,),
data: '$userID$randomDigit', // this could be uuid
width: height * 0.32, // you can adjust to your liking
height: height * 0.32,
) // BarcodeWidget,
Container( height: 50,width: 50,child:AssetImage('assets/icon.png') // you can do whatever here
])//Stack
)//Repaint boundary
This is where the sauce is:
For android:
void _takePhoto(String _dataString) async {
int randomDigit;
var range = new Random();
randomDigit = range.nextInt(1000000) + 1;
RenderRepaintBoundary boundary =
_renderObjectKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final file =
await new File('${tempDir.path}/image$randomDigit.png').create();
await file.writeAsBytes(pngBytes).then((value) {
GallerySaver.saveImage(value.path, albumName: 'Android Photo album')
.then((bool success) {});
});
}
For Ios:
Future<Uint8List> _getWidgetImage(String _dataString) async {
try {
RenderRepaintBoundary boundary =
_renderObjectKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final file = await new File('${tempDir.path}/image.png').create();
await file.writeAsBytes(pngBytes);
await Share.file(_dataString, '$_dataString.png', pngBytes, 'image/png');
var bs64 = base64Encode(pngBytes);
debugPrint(bs64.length.toString());
return pngBytes;
} catch (exception) {
print(exception);
}
}
Calling the functions:
_takePhoto('$userID$randomDigit');//Android in a textbutton your choice
_getWidgetImage('$userID$randomDigit');//IOS in a textbtuton
keep in mind that I have not been tracking the flutter dependencies I used in this project but I will include them from my pubspec.yaml file:
qr_flutter: ^3.2.0
qrcode: ^1.0.4
barcode: ^1.17.1
share: ^0.6.5+4
gallery_saver: ^2.0.1
path_provider: ^1.6.24
path: ^1.7.0
esys_flutter_share: ^1.0.2
Note: Copy and paste will not work. This will need adjustments.
Related
Hello guys I am new to flutter.
How can I take a screenshot from a widget, preview it to a new page and decide if I would like to save it in the gallery or not?
Here is my code
takeScreenShot(BuildContext context) async {
final path= join((await getTemporaryDirectory()).path,"${DateTime.now()}.png");
RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
var image = await boundary.toImage();
var byteData = await image.toByteData(format: ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
Navigator.push(context, MaterialPageRoute(builder: (builder)=>ScreenshotViewPage(path: path,)));
}
The global key is added to the root widget.
Below is a snippet of code from a function that uploads a generated QR code (using the qr_flutter package) to firebase storage; then gets the firebase storage url to save in a custom model that is uploaded to firebase firestore (not shown).
This works fine, however I want to upload a file that consists of the QR code bounded by title text above and address text below. (Essentially a Column with children [title, qrFile, address]).
My question is: How do I combine Text and my qrFile into a single image file that I can upload to firebase storage?
String qrString = 'qr_data_here';
final qrValidationResult = QrValidator.validate(
data: qrString,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
if (qrValidationResult.status == QrValidationStatus.valid) {
final qrCode = qrValidationResult.qrCode;
const String title = 'title_name_here';
final String address = 'address_here';
final painter = QrPainter.withQr(
qr: qrCode!,
color: const Color(0xFF000000),
gapless: true,
embeddedImageStyle: null,
embeddedImage: null,
);
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
final ts = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$tempPath/$ts.png';
// ui is from import 'dart:ui' as ui;
final picData =
await painter.toImageData(2048, format: ui.ImageByteFormat.png);
// writeToFile is seen in code snippet below
await writeToFile(
picData!,
path,
);
} else {
genericErrorDialog(context);
}
// qrStorage is a reference to a folder in firebase storage
await qrStorage.child('name_here').putFile(qrFile);
var url =
await qrStorage.child('name_here').getDownloadURL();
late File qrFile;
Future<void> writeToFile(ByteData data, String path) async {
final buffer = data.buffer;
qrFile = await File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
One solution is to use the screenshot package (https://pub.dev/packages/screenshot). This package has a function to save a widget as an image (without displaying it on screen) as shown below.
ScreenshotController screenshotController = ScreenshotController();
await screenshotController
.captureFromWidget(CustomWidget())
.then((capturedImage) async {
await do_something_with_capturedImage_here();
});
As it relates to my question specifically; Below is the code to generate a qr code, place it in a widget (needs some more formatting) with text, and then save the widget as an image file and upload to firebase.
String qrString = 'qr_data_here';
final qrValidationResult = QrValidator.validate(
data: qrString,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
if (qrValidationResult.status == QrValidationStatus.valid) {
final qrCode = qrValidationResult.qrCode;
const String title = 'title_name_here';
final String address = 'address_here';
final painter = QrPainter.withQr(
qr: qrCode!,
color: const Color(0xFF000000),
gapless: true,
embeddedImageStyle: null,
embeddedImage: null,
);
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
final ts = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$tempPath/$ts.png';
// ui is from import 'dart:ui' as ui;
final picData =
await painter.toImageData(2048, format: ui.ImageByteFormat.png);
// writeToFile is seen in code snippet below
await writeToFile(
picData!,
path,
);
await screenshotController
.captureFromWidget(Column(
children: [
Text(title),
Image.file(qrFile),
Text(address),
],
))
.then((capturedImage) async {
await widgetToImageFile(capturedImage);
});
} else {
genericErrorDialog(context);
}
// qrStorage is a reference to a folder in firebase storage
await qrStorage.child('name_here').putFile(fullQrFile);
var url =
await qrStorage.child('name_here').getDownloadURL();
ScreenshotController screenshotController = ScreenshotController();
late File qrFile;
late File fullQrFile;
Future<void> writeToFile(ByteData data, String path) async {
final buffer = data.buffer;
qrFile = await File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
Future<void> widgetToImageFile(
Uint8List capturedImage,
) async {
Directory newTempDir = await getTemporaryDirectory();
String newTempPath = newTempDir.path;
final newTs = DateTime.now().millisecondsSinceEpoch.toString();
String path = '$newTempPath/$newTs.png';
fullQrFile = await File(path).writeAsBytes(capturedImage);
}
I want to take a screenshot of my widget. For that, I am using the RepaintBoundary widget and to save the screenshot I use the image_gallery_saver plugin. But after saving the image it is not showing to the gallery. How to solve this issue?
class Utils {
static Future capture(GlobalKey key) async {
DateTime now;
now = DateTime.now();
if (key == null) return null;
final RenderRepaintBoundary boundary =
key.currentContext.findRenderObject();
final image = await boundary.toImage(pixelRatio: 3.0);
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final pngByte = byteData.buffer.asUint8List();
// print(pngByte);
if (!(await Permission.storage.status.isGranted)) {
await Permission.storage.request();
}
final result = await ImageGallerySaver.saveImage(
Uint8List.fromList(pngByte),
quality: 90,
name:
'Screeshoot_${now.day.toString()}${now.hour.toString()}${now.minute.toString()}${now.second.toString()}');
return result;
// return pngByte;
}
}
I have done the same in my app but I have used the GallerySaver package instead.
First I have created a method that returns me the path where the image is located and then I use GallerySaver to save the image file.
Future<String> getImagePath() async {
RenderRepaintBoundary boundary = _renderQRandImageWidgetKey.currentContext.findRenderObject();
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
final tempDirectory = (await getTemporaryDirectory()).path;
final String filePath = '$tempDirectory/MyQRCode.png';
File imgFile = new File('$filePath');
await imgFile.writeAsBytes(pngBytes);
return filePath;
}
_saveImage(String userName) async {
try {
String filePath = await getImagePath();
bool isImageSaved = await GallerySaver.saveImage(filePath, albumName:"AlbumName");
SnackBar _snackbar = SnackBar(
content: isImageSaved ? Text('Image saved in gallery') : Text("There was an error while saving image"),
duration: const Duration(seconds: 1),
);
_scaffoldKey.currentState.showSnackBar(_snackbar);
} catch (exception) {
print("Error $exception");
SnackBar _snackbar = SnackBar(
content: Text('Something went wrong'),
duration: const Duration(seconds: 1),
);
_scaffoldKey.currentState.showSnackBar(_snackbar);
}
There is the way to save the widget into transparent png and save it into gallery?
Thanks in advance.
Flutter draws every single pixel, so you can easily convert your widget to an image.
You need to follow these steps:
Edit -- first import path_provider link to pupspec.yaml and then follow this steps
Create a GlobalKey
final _globalKey = GlobalKey();
Create Uint8List
Uint8List pngBytes;
Wrap your widget with RepaintBoundary widget & pass in the key
RepaintBoundary(
key: _globalKey,
child: YourWidget(),
),
Create a method to convert your widget to image:
Future<void> _capturePng() async {
try {
final RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
final image = await boundary.toImage(pixelRatio: 2.0); // image quality
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
pngBytes = byteData.buffer.asUint8List();
} catch (e) {
print(e);
}
}
Convert your image to a file so that you can save it in your app
Future<File> convertImageToFile(Uint8List image) async {
final file = File(
'${(await
getTemporaryDirectory()).path}/${DateTime.now().millisecondsSinceEpoch}.png');
await file.writeAsBytes(image);
return file;
}
I am new to flutter, i am building an app where i need to convert the file(image) generated after using image_picker package to asset image to use in the app.
example code as follows, which creates file(Image)
final Function onSelectImage;
ImageInput(this.onSelectImage);
File _storedImage;
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
if (imageFile == null) {
return;
}
setState(() {
_storedImage = imageFile;
});
final appDir = await syspaths.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage = await imageFile.copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
}
Thanks in advance
You can create an image variable which you can rever to and update when you selected the image.
See the following code:
final Function onSelectImage;
ImageInput(this.onSelectImage);
File _storedImage;
Image _tempImage;
Future<void> _takePicture() async {
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
if (imageFile == null) {
return;
}
setState(() {
_storedImage = imageFile;
});
final appDir = await syspaths.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage = await imageFile.copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
setState(() {
_tempImage = imageFile;
});
}
#override
Widget build(BuildContext context) {
return _tempImage == null ? Container(child:null) : Image(image: _tempImage);
}