This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”
Content abstract
This article describes how to complete the upload of the image in The Flutter and how to submit the form once the upload is successful. The knowledge points involved are as follows:
- Image selection plugin
wechat_assets_picker
The use of. - Image Select iOS and Android app permissions configuration.
- Image selection component encapsulation.
- Encapsulation of the picture upload interface.
- Add and edit the page image upload implementation.
Image selection plugin
There are many image selection plug-ins for Flutter, including the official Image_picker, multi_image_picker (based on the 2.0 version multi_image_picker2), etc. In order to find the right image picker plugin, I searched several and found a copy of wechat_assets_picker plugin. It has good ratings and Github Star. Let’s try it out first.
Permission to apply for
First on a simple demo, direct call:
final List<AssetEntity> assets = await AssetPicker.pickAssets(context);
Copy the code
The result discovers flash to retreat!! Is there a bug in the plugin?
Oh, I remember! Forgot to set the image access permission! IOS added the following to the Runner info.plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>Get pictures and use photo album to take pictures for uploading dynamic pictures.</string>
Copy the code
Android in the app/profile/AndroidManifest. XML and app/debug/AndroidManifest. XML to add the following content:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
Copy the code
Run again, perfect! That’s how we run demos, isn’t it?
The UI modification
We changed the dynamic adding and editing mode to select the picture. The original input box was not available, so we need to change it to select the picture. Considering that the picture selection will be frequently used, we encapsulate a common single picture selection component.
static Widget imagePicker(
String formKey,
ValueChanged<String> onTapped, {
File imageFile,
String imageUrl,
double width = 80.0.double height = 80.0, {})return GestureDetector(
child: Container(
margin: EdgeInsets.all(10),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.grey[300],
border: Border.all(width: 0.5, style: BorderStyle.solid),
borderRadius: BorderRadius.all(Radius.circular(4.0)), ), child: _getImageWidget(imageFile, imageUrl, width, height), width: width, height: height, ), onTap: () { onTapped(); }); }static Widget _getImageWidget(
File imageFile, String imageUrl, double width, double height) {
if(imageFile ! =null) {
return Image.file(
imageFile,
fit: BoxFit.cover,
width: width,
height: height,
);
}
if(imageUrl ! =null) {
return CachedNetworkImage(
imageUrl: imageUrl,
fit: BoxFit.cover,
width: width,
height: height,
);
}
return Icon(Icons.add_photo_alternate);
}
Copy the code
The placeholder image for the image selection component may be from the network or from a file, so it is treated differently. File images are shown first, followed by web images, and an icon to add images is displayed if neither is available.
Dart also needs to be adjusted accordingly, including receiving image parameters, image processing functions, and changing the image text box to the image selection component. When the component is clicked, the assetPicker. pickAssets method provided by the wechat_Assets_Picker plug-in is called. The maximum number of optional images is 1.
List<Widget> _getForm(BuildContext context) {
List<Widget> widgets = [];
formData.forEach((key, formParams) {
widgets.add(FormUtil.textField(key, formParams['value'],
controller: formParams['controller']????null,
hintText: formParams['hintText']????' ',
prefixIcon: formParams['icon'],
onChanged: handleTextFieldChanged,
onClear: handleClear));
});
widgets.add(FormUtil.imagePicker(
'imageUrl',
() {
_pickImage(context);
},
imageFile: imageFile,
imageUrl: imageUrl,
));
widgets.add(ButtonUtil.primaryTextButton(
buttonName,
handleSubmit,
context,
width: MediaQuery.of(context).size.width - 20));return widgets;
}
void _pickImage(BuildContext context) async {
final List<AssetEntity> assets =
await AssetPicker.pickAssets(context, maxAssets: 1);
if (assets.length > 0) {
File file = await assets[0].file; handleImagePicked(file); }}Copy the code
How about seeing what happens? It looks like everything’s fine. Now let’s see how to upload.
Image upload
The picture upload and get interface has been completed before, you can first pull the latest background code: the background code based on ExpressJs. The interface addresses are as follows:
- Single picture upload interface address:http://localhost:3900/api/upload/image, Post request, field name
image
And return to the picture file after successid
. - Image acquisition interface:http://localhost:3900/api/upload/image/:id, using picture files
id
You can get the image file stream.
Dio provides a FormData method to upload files. The example code is as follows:
// Upload a single file
var formData = FormData.fromMap({
'name': 'wendux'.'age': 25.'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt')}); response =await dio.post('/info', data: formData);
// Upload multiple files
FormData.fromMap({
'files': [
MultipartFile.fromFileSync('./example/upload.txt', filename: 'upload.txt'),
MultipartFile.fromFileSync('./example/upload.txt', filename: 'upload.txt')]});Copy the code
We can use this way to complete the image upload. Image uploads belong to a common service, so we create a new file upload_service.dart to manage all upload interfaces. Currently, there is only one way to upload a single file, which is to get the file path from the image file and build the MultipartFile object, as shown below.
import 'dart:io';
import 'package:dio/dio.dart';
class UploadService {
static const String uploadBaseUrl = 'http://localhost:3900/api/upload/';
static Future uploadImage(String key, File file) async {
FormData formData =
FormData.fromMap({key: await MultipartFile.fromFile(file.path)});
var result = await Dio().post(uploadBaseUrl + 'image', data: formData);
returnresult; }}Copy the code
The next step is to handle the submit event. The add and edit logic is a little different here:
- Check whether the image file is empty. If the image file is empty, the system prompts you to upload the file.
- When editing, itself is a Url of the original data, if the image file is empty, there is no need to upload the image to the background, there is no need to upload the original Url of the image (the background code only stores the ID of the image file, by the front-end splicing complete address). If the image file is not empty, the data needs to be submitted to the background. If you do this better and save on back-end requests, you can compare the new form data to the original table data, and you don’t need to submit the data request if it hasn’t changed.
When submitting, we need to upload the picture first. After the picture is successfully uploaded, we will put the id of the picture file into the submitted form data in the submit new or update interface. The commit code when added looks like this:
_handleSubmit() async {
// Other form validation
if (_imageFile == null) {
Dialogs.showInfo(this.context, 'Images cannot be empty');
return;
}
EasyLoading.showInfo('Just a moment, please... ', maskType: EasyLoadingMaskType.black);
try {
String imageId;
var imageResponse = await UploadService.uploadImage('image', _imageFile);
if (imageResponse.statusCode == 200) {
imageId = imageResponse.data['id'];
}
if (imageId == null) {
Dialogs.showInfo(this.context, 'Image upload failed');
return;
}
Map<String.String> newFormData = {};
_formData.forEach((key, value) {
newFormData[key] = value['value'];
});
// Add the image ID to the submission form
newFormData['imageUrl'] = imageId;
// Omit the submit code
}
// ...
// Omit the exception handling code
}
Copy the code
The final result
The following figure shows the situation when adding and editing. Due to too much test data before, all dynamic data has been uniformly cleared this time. See my other column about MongoDB operations: MongoDB Is not professional. The code has been submitted to: Sample code related to the Flutter network, be sure to pull up the latest background code and start the background before running, welcome to communicate in the comments section. From the effect, you can see that the interface will be updated only after updating the content again. In the next article, we will introduce the details page and complete the synchronization between the parent and child pages.