Android
android development
app development
dart
dart language
development
flutter app development
flutter development
flutter web
flutter web upload
iOS
php
symfony
upload files flutter web
Upload files to server with Flutter Web
Icons made by DinosoftLabs from www.flaticon.com
1. Start Visual Studio Code. Go to menu “View -> Command
Palette” and select the “Flutter: New Project” option:
2. Create a folder called widgets inside the lib folder and
create a web_upload.dart file. This is where we are going to work:
3. Also, create a folder apps, and inside it create a
main_app.dart file:
4. Remove anything
from the main.dart file, import the main_app.dart file and call your main app:
import 'package:flutter/material.dart';
import 'package:web_upload/apps/main_app.dart';
void main() => runApp(UploadApp());
5. This is great! Our Flutter Web App is coming to life! It
might look like a lot of files/steps but this will be a structure of our app.
Open the main_app.dart and add a stateless widget that will return our main
widget app:
import 'package:flutter/material.dart';
import 'package:web_upload/widgets/web_upload.dart';
class UploadApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
fontFamily: 'Quicksand',
primarySwatch: Colors.purple,
),
home: FileUploadApp(),
);
}
}
6. Open the web_upload.dart widget and create the FileUploadApp Stateful widget (a form will be in it). Copy the following code, and as you can see, we
are returning a SafeArea with a Form in the body:
import 'package:flutter/material.dart';
class FileUploadApp extends StatefulWidget {
@override
createState() => _FileUploadAppState();
}
class _FileUploadAppState extends State {
@override
Widget build(BuildContext context) {
// TODO: implement build
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('A Flutter Web file picker'),
),
body: Container(
child: new Form(
child: Padding(
padding: const EdgeInsets.only(top: 16.0, left: 28),
child: new Container(
width: 350,
child: Column(
children: [
]
)
),
),
),
),
),
);
} }
8. Inside our widget, create a better widget structure by
adding a column, and inside it a Widget, which will hold the upload button:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
])
9. Add a material button, which will be used to open up a dialog to select a file:
MaterialButton(
color: Colors.pink,
elevation: 8,
highlightElevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
textColor: Colors.white,
child: Text('Select a file'),
onPressed: () {},
),
10. In order to be able to select a file in Flutter Web,
import the HTML library, Async library, Convert library and also the Typed Data
library:
import 'dart:html' as html;
import 'dart:typed_data';
import 'dart:async';
import 'dart:convert';
import 'package:http_parser/http_parser.dart';
11. Create an async function startWebFilePicker and call the
File Upload Input Element:
startWebFilePicker() async {
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.multiple = true;
uploadInput.draggable = true;
uploadInput.click();
}
12. Go back to the material button created in step 9, and in the onPressed event, call the
startWebFilePicker function:
onPressed: () {
startWebFilePicker();
},
14. If you start the Web App right now, and click on the select file button, a
file picker dialog will open:
15.So far, what we have done will only open the dialog but
will not select the file. Let’s create a List variable that will
hold the file as indexable collection of objects with a length, and also a
fixed-length list variable:
List<int> _selectedFile;
Uint8List _bytesData;
Uint8List _bytesData;
16. Now, go back to the startWebFilePicker async function,
and add an onChange listener
in which we will get the file content as data URL:
uploadInput.onChange.listen((e) {
final files = uploadInput.files;
final file = files[0];
final reader = new html.FileReader();
});
17. Create a _handleResult function which will be called in
the startWebFilePicker’s onChange listener so that it can convert the file as
set is as bytes data:
void _handleResult(Object result) {
setState(() {
_bytesData = Base64Decoder().convert(result.toString().split(",").last);
_selectedFile = _bytesData;
});
}
Note: With this method, to convert from Base64 to byte data, you MUST use .tostring().split(",").last otherwise you WILL NOT be able to send the file.
18. Inside the startWebFilePicker function, add a reader to load
the file and call the data handler (our _handleResult function):
reader.onLoadEnd.listen((e) {
_handleResult(reader.result);
});
reader.readAsDataUrl(file);
19. The file selection is now ready. Let’s prepare our app
to send the file to the server. First, prepare the form by creating a global
form key:
GlobalKey _formKey = new GlobalKey();
20. In the form, add the global key to the form and also set
the autovalidation to true:
child: new Form(
autovalidate: true,
key: _formKey,
21. Below the Upload button, add a Divider and another
button that will call a function to send the file to the server on its
onPressed event:
Divider(color: Colors.teal,),
RaisedButton(
color: Colors.purple,
elevation: 8.0,
textColor: Colors.white,
onPressed: () {
makeRequest();
},
child: Text('Send file to server'),
),
22. Create a future async function called makeRequest (added
to the send button in step 21):
Future makeRequest() async {
}
22. Before getting into the makeRequest function, you have
to use the HTTP library. Open your pubspecs.yaml file and add it as a
dependency:
http: ^0.12.0+2
23. Import the library in the web_upload.dart:
import 'package:http/http.dart' as http;
24. Now, in the makeRequest function, set your API url in a
variable:
var url = Uri.parse("http://192.168.23.10/upload_api/web/app_dev.php/api/save-file/");
25. Create a request
variable which will have the MultiPart Request:
var request = new http.MultipartRequest("POST", url);
26. Alright, this part is tricky since there almost no
documentation about how to handle files with Flutter Web. In step 17 the file
was converted from base64 to a stream of bytes, and this was done because this
is the only way right now to upload files. To send the file you need to use
request.files.add and also use the HTTP library from step 22 like this:
request.files.add(await http.MultipartFile.fromBytes(
'file', _selectedFile,
contentType: new MediaType('application', 'octet-stream'),
filename: "file_up"));
http.MultipartFile.fromBytes
No, you cannot use dart:io, and you cannot use .fromPath
You must specify the content type. You could either detect
the mime type of the file or just do it the KISS way like I did above
Note: I’m using a hard-coded word as the filename, you can
extract the name in the startWebFilePicker function
27. Finally send the form and wait for a response:
request.send().then((response) {
print("test");
print(response.statusCode);
if (response.statusCode == 200) print("Uploaded!");
});
28. Finally, add a simple dialog to let the user know the
file was uploaded:
showDialog(
barrierDismissible: false,
context: context,
child: new AlertDialog(
title: new Text("Details"),
//content: new Text("Hello World"),
content: new SingleChildScrollView(
child: new ListBody(
children: [
new Text("Upload successfull"),
],
),
),
actions: [
new FlatButton(
child: new Text('Aceptar'),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => UploadApp()),
(Route<dynamic> route) => false,
);
},
),
],
));
You can get the source code here: https://github.com/rjcalifornia/web_upload
If you want to know how the files are being handled in the backend, I used Symfony and MySQL.
Get the source code for the API here: https://github.com/rjcalifornia/file-upload-api
12 comments
Nice tutorial, help me a lot, thx.
Please create tutorial and submit to YOUTUBE.
Hi, great blog. Could you please show my how we extract the file name and type? That is the only thing I'm struggling with now. Thank you.
Hello there,
Thanks. Your blog is really helpful.
But when I try to upload a big file, the line
_bytesData = Base64Decoder().convert(result.toString().split(",").last)
is easily crashed.
My way is read from ArrayBuffer instead of DataUrl
reader.readAsDataUrl(file) -> reader.readAsArrayBuffer(file)
And when handling the result, I will cash it to Uint8List.
_selectedFile = result as Uint8List;
Think it is better than converting to string and split it.
Thanks. I was looking for this 2 days.
thanks a ton for sharing! Worked well.
thanks a lot. it works.
thank you so much for your sharing.
hope you all the best in your life too.
Thanks for sharing. However, Do you have the instructions on how to set up the backend?
I try to set up the backend and got the following error. "failed to open stream: No such file or directory in".
Do you know why?
contact me at camaronbrowne@gmail.com
Hi, Can someone give me PHP code example before this => move_uploaded_file($_FILES["image"]["tmp_name"], basename($_FILES["image"]["name"])).
You are a life saver. I have been searching online for 3 days, and this is the only tutorial that really helped me. Sharing is caring ! Be blessed!
Post a Comment