Flutter图片表单字段插件image_form_field的使用

在Flutter中处理图片上传时,可以使用image_form_field插件来简化操作。此插件允许你在Form中集成图片上传功能,并提供了灵活的自定义选项。

使用方法

为了完全自定义照片上传字段,需要使用几个回调函数和类。通常情况下,你会将远程来源的照片与本地上传的照片混合在一起,因此一个适配层会非常有用:

class ImageInputAdapter {
  /// 初始化时只能从URL或文件中选择其一。
  ImageInputAdapter({
    this.file,
    this.url,
  }) :
    assert(file != null || url != null),
    assert(file != null && url == null),
    assert(file == null && url != null);

  /// 图片文件
  final File file;
  /// 远程图片的直接链接
  final String url;

  /// 渲染来自文件或远程源的图片。
  Widget widgetize() {
    if (file != null) {
      return Image.file(file);
    } else {
      return FadeInImage(
        image: NetworkImage(url),
        placeholder: AssetImage("assets/images/placeholder.png"),
        fit: BoxFit.contain,
      );
    }
  }
}

接下来,在Flutter的Form中使用ImageFormField

import 'package:image_form_field/image_form_field.dart';

ImageFormField<ImageInputAdapter>(
  previewImageBuilder: (_, ImageInputAdapter image) =>
    image.widgetize(),
  buttonBuilder: (_, int count) =>
    Container(
      child: Text(
        count == null || count < 1 ? "上传图片" : "继续上传"
      )
    ),
  initializeFileAsImage: (File file) =>
    ImageInputAdapter(file: file),
  initialValue: existingPhotoUrl == null ? null : [ImageInputAdapter(url: existingPhotoUrl)],
  // 即使`shouldAllowMultiple`为true,`images`始终是一个声明类型的列表(即`ImageInputAdapter`)。
  onSaved: (images) => _images = images,
)

完整的示例代码可以在这里找到。

参数说明

以下是ImageFormField的主要参数说明(T表示声明的显示类型,例如ImageFormField<T>):

名称 类型 是否必需 描述
previewImageBuilder Widget Function(BuildContext, T) 如何在上传按钮下方渲染图片
buttonBuilder Widget Function(BuildContext, [int]) 按钮的显示方式。不要使用FlatButton;按钮已经被包裹在一个GestureRecognizer
initializeFileAsImage T Function(File) 将上传转换为适配器类
controller ImageFieldController 直接访问当前显示或上传的图片
initialValue List 初始渲染时显示的图片;如果initialValueinitState或其他非传递方法中设置,则不要渲染字段直到值被设置
onSaved VoidCallback Function(List) 当表单保存时处理上传/远程图片
validator VoidCallback Function(List) 当表单验证时处理上传/远程图片
errorTextStyle TextStyle 控制字段无效时文本的显示方式;通常最好使用Theme.of(context).inputDecorationTheme.errorStyle
autoValidate bool 字段是否应自动验证(默认为false)
shouldAllowMultiple bool 字段是否允许多张图片上传(默认为true)

感谢

感谢AllGo为该项目提供了初始支持。


完整示例代码

以下是一个完整的示例代码,展示了如何使用image_form_field插件进行图片上传:

import 'package:flutter/material.dart';

import 'package:image_form_field/image_form_field.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

import 'upload_button.dart';
import 'image_input_adapter.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter 图片演示',
      home: new MyHomePage(),
    );
  }
}

class BlogImage {
  const BlogImage({
    @required this.storagePath,
    @required this.originalUrl,
    @required this.bucketName,
    this.id,
  });

  final String storagePath;
  final String originalUrl;
  final String bucketName;
  final String id;

  static String get collectionPath => "blogImages";

  create() {
    return Firestore.instance.collection(collectionPath).document().setData({
      "storagePath": storagePath,
      "originalUrl": originalUrl,
      "bucketName": bucketName,
    });
  }

  static Future<BlogImage> fromUrl(String url) async {
    final images = await Firestore.instance
        .collection(collectionPath)
        .where("originalUrl", isEqualTo: url)
        .getDocuments();

    if (images.documents.isNotEmpty) {
      final i = images.documents.first.data;

      return BlogImage(
        storagePath: i["storagePath"],
        originalUrl: i["originalUrl"],
        bucketName: i["bucketName"],
        id: images.documents.first.documentID,
      );
    }

    return null;
  }

  Future delete() async {
    FirebaseStorage.instance.ref().child(storagePath).delete();
    return Firestore.instance.collection(collectionPath).document(id).delete();
  }
}

class _UploadForm extends StatefulWidget {
  _UploadForm(this.existingImages);

  final List<BlogImage> existingImages;

  [@override](/user/override)
  State<StatefulWidget> createState() => _UploadFormState();
}

class _UploadFormState extends State<_UploadForm> {
  final _formKey = GlobalKey<FormState>();
  List<ImageInputAdapter> _images;

  void submit() {
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      var snackbarText = "上传成功";

      try {
        // 新图片
        _images?.where((i) => i.isFile)?.forEach((i) async {
          final photo = await i.save();

          BlogImage(
            storagePath: photo.refPath,
            originalUrl: photo.originalUrl,
            bucketName: photo.bucketName,
          ).create();
        });

        // 删除的图片
        widget.existingImages
            ?.where((r) => !_images.any((m) => m.url == r.originalUrl))
            ?.forEach((i) {
          BlogImage.fromUrl(i.originalUrl).then((b) => b?.delete());
        });
      } catch (e) {
        print(e);
        snackbarText = "无法保存,请稍后再试。";
      } finally {
        Scaffold.of(context).showSnackBar(SnackBar(content: Text(snackbarText)));
      }
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final bool shouldAllowMultiple = true;

    return Form(
      key: _formKey,
      child: ListBody(
        children: [
          ImageFormField<ImageInputAdapter>(
            shouldAllowMultiple: shouldAllowMultiple,
            onSaved: (val) => _images = val,
            initialValue: widget.existingImages
                .map((i) => ImageInputAdapter(url: i.originalUrl))
                .toList()
                .cast<ImageInputAdapter>(),
            initializeFileAsImage: (file) => ImageInputAdapter(
              file: UploadableImage(
                file,
                storagePath: "appImages",
              ),
            ),
            buttonBuilder: (_, count) =>
                PhotoUploadButton(count: count, shouldAllowMultiple: shouldAllowMultiple),
            previewImageBuilder: (_, image) => image.widgetize(),
          ),
          FlatButton(
            onPressed: submit,
            child: const Text("更新资料"),
          )
        ],
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(centerTitle: true, title: const Text("上传图片")),
      body: SingleChildScrollView(
        // 提供现有的图片作为第一个参数
        child: _UploadForm(List<BlogImage>()),
      ),
    );
  }
}

更多关于Flutter图片表单字段插件image_form_field的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片表单字段插件image_form_field的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


image_form_field 是一个用于 Flutter 的插件,它允许用户在表单中选择和上传图片。这个插件通常用于需要用户上传图片的场景,比如用户头像、产品图片等。

安装

首先,你需要在 pubspec.yaml 文件中添加 image_form_field 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  image_form_field: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

基本用法

以下是一个简单的示例,展示了如何使用 image_form_field 插件:

import 'package:flutter/material.dart';
import 'package:image_form_field/image_form_field.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Image Form Field Example'),
        ),
        body: ImageFormFieldExample(),
      ),
    );
  }
}

class ImageFormFieldExample extends StatefulWidget {
  @override
  _ImageFormFieldExampleState createState() => _ImageFormFieldExampleState();
}

class _ImageFormFieldExampleState extends State<ImageFormFieldExample> {
  final _formKey = GlobalKey<FormState>();
  File? _image;

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          ImageFormField(
            onSaved: (File? image) {
              _image = image;
            },
            validator: (File? image) {
              if (image == null) {
                return 'Please select an image';
              }
              return null;
            },
          ),
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                _formKey.currentState!.save();
                // 在这里处理图片上传逻辑
                if (_image != null) {
                  print('Image selected: ${_image!.path}');
                }
              }
            },
            child: Text('Submit'),
          ),
        ],
      ),
    );
  }
}

参数说明

  • onSaved: 当表单保存时调用的回调函数,返回用户选择的图片文件。
  • validator: 用于验证用户是否选择了图片。如果用户没有选择图片,可以返回一个错误消息。
  • decoration: 用于自定义 ImageFormField 的外观,比如提示文本、边框等。
  • initialValue: 设置初始的图片文件。
  • maxWidthmaxHeight: 设置图片的最大宽度和高度。
  • imageQuality: 设置图片的质量,范围是 0 到 100。

处理图片上传

onSaved 回调中,你可以获取用户选择的图片文件,并将其上传到服务器。你可以使用 httpdio 等库来处理文件上传。

void _uploadImage(File image) async {
  var request = http.MultipartRequest('POST', Uri.parse('https://example.com/upload'));
  request.files.add(await http.MultipartFile.fromPath('file', image.path));

  var response = await request.send();
  if (response.statusCode == 200) {
    print('Image uploaded successfully');
  } else {
    print('Failed to upload image');
  }
}
回到顶部