I'm trying to use dart's mirror API to dynamically invoke a function.
How can I obtain the result that's returned from the doWork method when invoking it via an InstanceMirror
class MyData {
String someString;
}
class MyService {
Future<MyData> doWork() async {
print('doing work');
return await Future(() => MyData()..someString = 'my result');
}
}
void main() async {
var instance = MyService();
var mirrror = reflect(instance);
var result = mirrror.invoke(#doWork, []);
}
I can see that "doing work" gets printed to the console so I know it's being invoked, but I'm struggling to interpret the result from the invoke function.
The value are inside the InstanceMirror in the property reflectee. So something like this:
import 'dart:mirrors';
class MyData {
String? someString;
}
class MyService {
Future<MyData> doWork() async {
print('doing work');
return await Future(() => MyData()..someString = 'my result');
}
}
void main() async {
var instance = MyService();
var mirrror = reflect(instance);
var result = mirrror.invoke(#doWork, <dynamic>[]);
var resultValue = await (result.reflectee as Future<MyData>);
print(resultValue.someString); // my result
}
I would like to know how to pick an Image from the users computer into my flutter web app for upload
Using dart:html package directly in Flutter is not recommended.
Instead, use this package: https://pub.dev/packages/file_picker.
Example of how to use in Flutter Web:
class FileUploadButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('UPLOAD FILE'),
onPressed: () async {
var picked = await FilePicker.platform.pickFiles();
if (picked != null) {
print(picked.files.first.name);
}
},
);
}
}
Note that FilePickerResult.path is not supported in Flutter Web.
I tried the code below and it worked.
first import 'dart:html';
// variable to hold image to be displayed
Uint8List uploadedImage;
//method to load image and update `uploadedImage`
_startFilePicker() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
final file = files[0];
FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
setState(() {
uploadedImage = reader.result;
});
});
reader.onError.listen((fileEvent) {
setState(() {
option1Text = "Some Error occured while reading the file";
});
});
reader.readAsArrayBuffer(file);
}
});
}
now just any Widget, like a button and call the method _startFilePicker()
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
class FileUploadWithHttp extends StatefulWidget {
#override
_FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}
class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
PlatformFile objFile = null;
void chooseFileUsingFilePicker() async {
//-----pick file by file picker,
var result = await FilePicker.platform.pickFiles(
withReadStream:
true, // this will return PlatformFile object with read stream
);
if (result != null) {
setState(() {
objFile = result.files.single;
});
}
}
void uploadSelectedFile() async {
//---Create http package multipart request object
final request = http.MultipartRequest(
"POST",
Uri.parse("Your API URL"),
);
//-----add other fields if needed
request.fields["id"] = "abc";
//-----add selected file with request
request.files.add(new http.MultipartFile(
"Your parameter name on server side", objFile.readStream, objFile.size,
filename: objFile.name));
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
//------Button to choose file using file picker plugin
RaisedButton(
child: Text("Choose File"),
onPressed: () => chooseFileUsingFilePicker()),
//------Show file name when file is selected
if (objFile != null) Text("File name : ${objFile.name}"),
//------Show file size when file is selected
if (objFile != null) Text("File size : ${objFile.size} bytes"),
//------Show upload utton when file is selected
RaisedButton(
child: Text("Upload"), onPressed: () => uploadSelectedFile()),
],
),
);
}
}
I've tested this package and was very happy with the result imagePickerWeb it returns 3 different types it can be in the form of Image(widget for preview), byte, File(upload)
then you can use this to get the values
html.File _cloudFile;
var _fileBytes;
Image _imageWidget;
Future<void> getMultipleImageInfos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
if (mediaFile != null) {
setState(() {
_cloudFile = mediaFile;
_fileBytes = mediaData.data;
_imageWidget = Image.memory(mediaData.data);
});
}
Uploading to firebase
don't forget to add this to your index.html
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-storage.js"></script>
Uploading to firebase
import 'package:firebase/firebase.dart' as fb;
uploadToFirebase(File file) async {
final filePath = 'temp/${DateTime.now()}.png';//path to save Storage
try {
fb
.storage()
.refFromURL('urlFromStorage')
.child(filePath)
.put(file);
} catch (e) {
print('error:$e');
}
}
See the documentation of the package if you still have problems
The accepted answer is indeed outdated. Like jnt suggested, https://pub.dev/packages/file_picker is a handy package, when it comes to implementing an image upload using Flutter Web.
The problem I was facing is to get a base64 representation of an image, since I was using it to store images in Firestore. As we know, dart:io is not supported on Flutter Web and throws Unsupported operation: _Namespace error. Hence, using File and reading file's bytes was not an option. Luckily, the package provides API to convert the uploaded image to Uint8List. Here is my implementation:
import 'package:file_picker/file_picker.dart';
...
FilePickerResult? pickedFile;
...
void chooseImage() async {
pickedFile = await FilePicker.platform.pickFiles();
if (pickedFile != null) {
try {
setState(() {
logoBase64 = pickedFile!.files.first.bytes;
});
} catch (err) {
print(err);
}
} else {
print('No Image Selected');
}
}
In case you need to display the local image right away, use Image.memory.
Image.memory(logoBase64!);
i have this problem too;
i use https://pub.dev/packages/file_picker but in flutter web path not suppor;
you should to use bytes;
i save file bytes in var _fileBytes and use in request;
var request = http.MultipartRequest('POST', Uri.parse('https://.....com'));
request.headers.addAll(headers);
request.files.add(
http.MultipartFile.fromBytes(
'image',
await ConvertFileToCast(_fileBytes),
filename: fileName,
contentType: MediaType('*', '*')
)
);
request.fields.addAll(fields);
var response = await request.send();
function ConvertFileToCast:
ConvertFileToCast(data){
List<int> list = data.cast();
return list;
}
it`s work for me :)
if anyone is wondering how to get it working on mobile and web :
var bytes;
await file!.files.first.readStream!
.map((asciiValue) => bytes = asciiValue)
.toList();
FormData body;
final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "file");
MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
body.files.add(imageEntry);
I can share the way I upload image to AWS s3 from flutter web recently.
May not exact match the case who is looking for answer here but I think it may inpired others somehow.
First I try to use amplify_storage_s3 package but it not support for Flutter Web yet for now. So I use basic http post instead.
The packages I use:
file_picker: For web, FileUploadInputElement (from html package) may do the same thing but I think using this package can make thing simpler.
dio: I'm not sure why I cannot use http's MultipartFile successfully so I use this instead. (maybe someone can provide a version using http package)
mine: transfer extension to mimetype
Code example:
import 'package:flutter/material.dart';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
class FileUploader extends StatelessWidget {
const FileUploader({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 1. Pick an image file
final filePicked = await FilePicker.platform.pickFiles();
if (filePicked != null) {
final file = filePicked.files.single; // PlatformFile
final mimeType = lookupMimeType(file.name) ?? '';
/// 2. Get presigned data somewhere
const url = 'https://s3.amazonaws.com/......';
final fields = {
'bucket': '...',
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': '...',
'X-Amz-Date': '...',
'Policy': '...',
'X-Amz-Signature': '...',
'x-amz-meta-userid': '...',
'Content-Type': mimeType,
'file': dio.MultipartFile.fromBytes(file.bytes ?? []),
};
/// 3. Send file to AWS s3
final formData = dio.FormData.fromMap(fields);
await dio.Dio().post(url, data: formData);
}
},
child: const Icon(Icons.upload),
),
);
}
}
Here is my working code to upload using dio. I use dio because it has a callback progress function.
class _FileUploadViewState extends State<FileUploadView> {
#override
void initState() {
super.initState();
}
FilePickerResult? result;
PlatformFile? file;
Response? response;
String? progress;
String? percentage;
Dio dio = Dio();
selectFile() async {
result =
await FilePicker.platform.pickFiles(type: FileType.any, withData: true);
if (result != null) {
file = result?.files.single;
}
//print(file?.name);
//print(file?.bytes?.length);
//print(file?.size);
//print(file?.extension);
//print(file?.path);
setState(() {});
}
Future<void> uploadFile(BuildContext context, User user) async {
final navigator = Navigator.of(context);
final storage = FlutterSecureStorage();
String? token = await storage.read(key: 'jwt');
final formData = FormData.fromMap(
{
'file': MultipartFile.fromBytes(file?.bytes as List<int>,
filename: file?.name)
},
);
dio.options.headers['content-Type'] = 'application/octet-stream';
dio.options.headers["authorization"] = "Bearer $token";
response = await dio.post(
user.fileUrl,
data: formData,
onSendProgress: (int sent, int total) {
percentage = (sent / total * 100).toStringAsFixed(2);
progress = "$sent Bytes of $total Bytes - $percentage % uploaded";
setState(
() {},
);
},
);
if (response!.statusCode == 200) {
....
My go code for the server looks like this,
if err := r.ParseMultipartForm(64 << 20); err != nil {
log.Println("error processing multipart form")
log.Println(err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
String empName;
Future<List> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
empName = dataUser[0]['name'];
return null;
}
How to display the variable "empName" in line 2 to line 70 "child: Text('')"
Full code on Pastebin
Try this way.. make pojo class for response data like this way..
class UserData {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
UserData({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory UserData.fromJson(Map<String, dynamic> json) {
return new UserData(
albumId: json['albumId'],
id: json['id'],
title: json['title'],
url: json['url'],
thumbnailUrl: json['thumbnailUrl']);
}
}
make method for api call..
Future<UserData> fetchData() async {
var result = await get('https://jsonplaceholder.typicode.com/photos');
if (result.statusCode == 200) {
return UserData.fromJson(json.decode(result.body));
} else {
// If that response was not OK, throw an error.
throw Exception('Failed to load post');
}
}
after that make global object that fetch data..
Future<UserData> userDataList;
on Button click ..
userDataList = fetchData();
after that you want to print data..
userDataList.then((userData){
print(userData.title);
});
First of all you getUserData() function never returns anything. It seems like you only need the name so this function could look like this:
Future<String> getUserData() async{
final response = await http.post("http://172.16.161.34:8080/ebs/cfs/android_test_app/accessfile.php?q=getUserData",body:{
"emp_id": widget.empId,
});
var dataUser = jsonDecode(response.body);
return dataUser[0]['name'];
}
Then to set the empName variable you should use setState().
So change your afterFirstLayout() method to this:
#override
void afterFirstLayout(BuildContext context) async {
// Calling the same function "after layout" to resolve the issue.
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
}
Also you seem to want to reload the name once you press the IconButton.
So you might want to override your code with this:
IconButton(icon: Icon(Icons.shopping_cart),
onPressed:() {
getUserData().then( (userName) {
setState(() {
empName = userName;
});
});
},
),
I have an int value passed from the first screen to the second screen and I can have my value without any problem...My problem is that I want to add my recieved value to astring to complete and start working with my api which reqires to add the imported value of the first screen...Iam trapped between the recieved int which I can not change it to a static, and the api string which reqires the added value to be a static
the second Screen:
class CatsNews extends StatefulWidget {
#override
_CatsNewsState createState() => new _CatsNewsState();
}
class _CatsNewsState extends State<CatsNews> {
int _id ;
String root = "http://api.0mr.net";
String url = "/api/GetCategoryById/$_id";
#override
List data;
Future<String> getDrawerItem() async {
var response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Cat Screen'),
),
body: Center()
I get the value through the shared preferences and it works fine if it was inside Widget build(BuildContext context)
#override
void initState() {
getIntPrefrence().then(updatId);
super.initState();
this.getDrawerItem();
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
UPDATE:
I have added the Srting url to the initstate() and the code is as foloows:
class _CatsNewsState extends State<CatsNews> {
#override
List data;
int _id ;
var response;
String root = "http://-api.0mr.net";
String url ;
Future<String> getDrawerItem() async {
response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$_id'),
),
body: Center()
}
Future<int> getIntPrefrence() async {
SharedPreferences pref = await SharedPreferences.getInstance();
int id = pref.getInt("id");
return id;
}
#override
void initState() {
super.initState();
getIntPrefrence().then(updatId);
updateurl();
this.getDrawerItem();
//initResponse();
}
updateurl() {
setState(() {
this.url= "$root/api/CategoryId/$_id";
});
}
void updatId(int id) {
setState(() {
this._id = id;
});
}
}
the previous issue was solved by adiing the String to the initstate(), but the updated values does not add to the String and deals witht the imported int id as null or zero,however the int id works fine and shows the imported value in any widget inside the Widget build(BuildContext context)
The offending code is probably
var response = await http.get(...
You can't have arbitrary code in a field initializer which is the part after = in above code.
Dart has a strict order in object creation steps.
Field initializers are executed before the constructor initialization list and before the constructor initialization list of super classes.
Only after all the constructor initialization lists of all super classes are executed, the constructor bodies are executed and only from then on is it allowed to access this, the instance of your class being created.
Your code accesses this (implicitly) before that and at this point object initialization isn't done and therefore access to this prohibited to prevent undefined bahavior.
Static members are safe to access, because they don't depend on object initialization. They are ready to use without an instance entirely.
What the error message is telling you is that your initializer is trying to do things that are not possible at this point and you need to move the code somewhere else.
The field initialization code can be moved to the constructor initialization list. This is usually done if the field is supposed to be final,
or to the constructor body, or to a method - as shown below.
var response;
#override
void initState() {
super.initState();
_initResponse();
}
void _initResponse() async {
response = await http.get(
Uri.encodeFull(url), headers: {"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
Because initState does not allow async we move the code to another method (_initResponse) that we call from initState.
the solution of my problem was adding both the imported value and the String which I want to edit according to it inside the method which will start my api request as follows:
void initState() {
super.initState();
this.getDrawerItem();
}
Future<String> getDrawerItem() async {
int _id ;
String url;
SharedPreferences pref = await
SharedPreferences.getInstance();
_id = (pref.getInt("id")?? 0);
url = "http://gomhuriaonline-api.0mr."
"net/api/GetCategoryById/$_id";
print(url);
response = await http
.get(Uri.encodeFull(url), headers:
{"Accept": "application/json"});
setState(() {
var respondbody = json.decode(response.body);
data = respondbody["StoriesCategory"];
});
}
}
The following code was posted as the new handling for openCursor. Could someone please advise if the following code should work now (Dart r18915), and what is the "values" variable?
store.openCursor(autoAdvance: true).listen(
(cursor) => values.add(onCursor(cursor)),
onDone: () => completer.complete(values),
onError: (e) => completer.completeError(e));
return completer.future;
I'm not sure what your onCursor() function is doing/calling. I'm assuming your values variable is a list.
I do something similar myself but with a callback instead of a future/completer. You only have a small function fragment here. I'm going to flush it out some to hopefully add some details:
// Accept the indexeddb Database, and return a future which
// will provide the list of values from 'someKey'
Future<List<String>> getSomeKeyValues(Database db) {
var values = new List<String>();
var completer = new Completer();
var store = db.transaction('DB_STORE', 'readwrite').objectStore('DB_STORE');
store.openCursor(autoAdvance: true).listen((cursor) {
if(cursor != null && cursor.value != null) {
// Note cursor.value is actually a Map<String, String>
// of collection's key and value
values.add(cursor.value['someKey']); // get the value key 'someKey'
}
}, onDone: () => completer.complete(values),
onError: (e) => completer.completeError(e));
return completer.future;
}
We would then call this function something like this:
getSomeKeyValues(myDatabase).then((results) {
for(var value in results) {
print(value);
}
}