Flutter响应式图片选择插件reactive_image_picker的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter响应式图片选择插件reactive_image_picker的使用

reactive_image_picker 是一个基于 image_picker 的封装,旨在与 reactive_forms 一起使用。这个插件允许你在Flutter应用中以响应式的方式选择和处理图片。

简介

reactive_image_picker 提供了一个方便的方式来在表单中集成图片选择功能,并且与 reactive_forms 完美结合,使得表单验证和数据绑定更加简单和高效。

示例代码

以下是一个完整的示例代码,展示了如何使用 reactive_image_picker 插件来实现图片选择功能:

// ignore_for_file: use_build_context_synchronously

import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:reactive_forms/reactive_forms.dart';
import 'package:reactive_image_picker/reactive_image_picker.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  Future<void> _photoDenied(BuildContext context) async =>
      await showDialog<void>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text('Photo access required'),
            content: const SingleChildScrollView(
              child: ListBody(
                children: <Widget>[
                  Text(
                    'Open settings to allow access',
                  ),
                ],
              ),
            ),
            actions: <Widget>[
              TextButton(
                child: const Text('Cancel'),
                onPressed: () => Navigator.of(context).pop(),
              ),
              TextButton(
                child: const Text('Settings'),
                onPressed: () async {
                  await AppSettings.openAppSettings();
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        },
      );

  Future<void> _cameraDenied(BuildContext context) async =>
      await showDialog<void>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: const Text('Camera access required'),
            content: const SingleChildScrollView(
              child: ListBody(
                children: <Widget>[
                  Text(
                    'Open settings to allow access',
                  ),
                ],
              ),
            ),
            actions: <Widget>[
              TextButton(
                child: const Text('Cancel'),
                onPressed: () => Navigator.of(context).pop(),
              ),
              TextButton(
                child: const Text('Settings'),
                onPressed: () async {
                  await AppSettings.openAppSettings();
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        },
      );

  @override
  Widget build(BuildContext context) {
    FormGroup buildForm() => fb.group({
          'input': FormControl<List<SelectedFile>>(validators: [
            Validators.required,
          ]),
        });

    return MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: Scaffold(
          appBar: AppBar(),
          body: SafeArea(
            child: SingleChildScrollView(
              physics: const BouncingScrollPhysics(),
              padding: const EdgeInsets.symmetric(
                horizontal: 20.0,
                vertical: 20.0,
              ),
              child: SafeArea(
                child: SingleChildScrollView(
                  physics: const BouncingScrollPhysics(),
                  padding: const EdgeInsets.symmetric(
                    horizontal: 20.0,
                    vertical: 20.0,
                  ),
                  child: ReactiveFormBuilder(
                    form: buildForm,
                    builder: (context, form, child) {
                      return Column(
                        children: [
                          ReactiveImagePicker(
                            formControlName: 'input',
                            decoration: const InputDecoration(
                                contentPadding: EdgeInsets.zero,
                                labelText: 'Image',
                                filled: false,
                                border: InputBorder.none,
                                enabledBorder: InputBorder.none,
                                disabledBorder: InputBorder.none,
                                helperText: ''),
                            preprocessError: (e) async {
                              if (e is PlatformException) {
                                switch (e.code) {
                                  case 'photo_access_denied':
                                    await _photoDenied(context);
                                    break;
                                  case 'camera_access_denied':
                                    await _cameraDenied(context);
                                    break;
                                }
                              }
                            },
                            selectedValueBuilder:
                                (context, value, _, handleChange) {
                              return Wrap(
                                runSpacing: 24,
                                spacing: 24,
                                children: value
                                    .map(
                                      (e) => e.map(
                                        video: (_) => Text("video"),
                                        image: (i) => SizedBox.square(
                                            dimension: 50,
                                            child: InkWell(
                                                onTap: () {
                                                  handleChange(
                                                    context,
                                                    null,
                                                  );
                                                },
                                                child: ImageView(image: i))),
                                      ),
                                    )
                                    .toList()
                                  ..add(IconButton(
                                    style: IconButton.styleFrom(
                                      minimumSize: Size.square(50),
                                    ),
                                    onPressed: () => handleChange(
                                      context,
                                      null,
                                    ),
                                    icon: const Icon(Icons.read_more),
                                  )),
                              );
                            },
                            inputBuilder: (onPressed) => Center(
                              child: IconButton(
                                style: IconButton.styleFrom(
                                  minimumSize: Size.square(100),
                                ),
                                onPressed: onPressed,
                                icon: const Icon(Icons.read_more),
                              ),
                            ),
                          ),
                          const SizedBox(height: 16),
                          ElevatedButton(
                            child: const Text('Sign Up'),
                            onPressed: () {
                              if (form.valid) {
                                // ignore: avoid_print
                                print(form.value);
                              } else {
                                form.markAllAsTouched();
                              }
                            },
                          ),
                        ],
                      );
                    },
                  ),
                ),
              ),
            ),
          ),
        ));
  }
}

代码说明

  1. 导入依赖:首先导入必要的包,包括 reactive_formsreactive_image_picker
  2. 主函数:定义了 main 函数并运行 MyApp
  3. 权限提示:定义了 _photoDenied_cameraDenied 方法用于处理权限被拒绝的情况。
  4. 构建表单:使用 ReactiveFormBuilder 构建表单,并添加一个 ReactiveImagePicker 组件。
  5. 图片选择器配置:配置 ReactiveImagePicker 的属性,如 formControlNamedecorationpreprocessErrorselectedValueBuilderinputBuilder
  6. 提交按钮:添加一个提交按钮,点击后验证表单并打印结果。

通过上述代码,你可以轻松地在你的Flutter应用中集成图片选择功能,并享受 reactive_forms 带来的便捷性和灵活性。

更多详细信息和文档,请参考 reactive_image_picker GitHub仓库 中的示例文件。


更多关于Flutter响应式图片选择插件reactive_image_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter响应式图片选择插件reactive_image_picker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用reactive_image_picker插件的详细代码案例。reactive_image_picker是一个用于从设备图库或相机中选择图片的响应式插件,它基于providerget_it等依赖项来管理状态。

第一步:添加依赖项

首先,你需要在pubspec.yaml文件中添加reactive_image_picker及其相关依赖项:

dependencies:
  flutter:
    sdk: flutter
  reactive_image_picker: ^x.y.z  # 替换为最新版本号
  provider: ^6.0.0  # 确保使用兼容的版本
  get_it: ^7.2.0  # 确保使用兼容的版本

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

第二步:设置Provider

创建一个新的Provider类来管理图片选择的状态。

// providers/image_picker_provider.dart
import 'package:flutter/material.dart';
import 'package:reactive_image_picker/reactive_image_picker.dart';
import 'package:get_it/get_it.dart';

class ImagePickerProvider with ChangeNotifier {
  ReactiveImagePicker _reactiveImagePicker = GetIt.I<ReactiveImagePicker>();

  List<File> _selectedImages = [];

  List<File> get selectedImages => _selectedImages;

  void selectImage() async {
    File? image = await _reactiveImagePicker.pickImage(source: ImageSource.gallery);
    if (image != null) {
      _selectedImages.add(image);
      notifyListeners();
    }
  }

  void clearImages() {
    _selectedImages.clear();
    notifyListeners();
  }
}

在应用的入口文件(通常是main.dart)中初始化GetIt容器,并将ReactiveImagePicker实例添加到容器中。

// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:get_it/get_it.dart';
import 'package:reactive_image_picker/reactive_image_picker.dart';
import 'providers/image_picker_provider.dart';

void main() {
  final getIt = GetIt.I;
  getIt.registerSingleton<ReactiveImagePicker>(ReactiveImagePicker());

  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => ImagePickerProvider()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

第三步:使用Provider在UI中展示图片

在你的UI组件中,使用ConsumerProvider.of来访问Provider的状态。

// screens/home_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:image/image.dart' as UiImage;

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final imagePickerProvider = Provider.of<ImagePickerProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Reactive Image Picker Demo'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () => imagePickerProvider.selectImage(),
            child: Text('Select Image'),
          ),
          ElevatedButton(
            onPressed: () => imagePickerProvider.clearImages(),
            child: Text('Clear Images'),
          ),
          SizedBox(height: 20),
          Expanded(
            child: GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                crossAxisSpacing: 4,
                mainAxisSpacing: 4,
              ),
              itemCount: imagePickerProvider.selectedImages.length,
              itemBuilder: (context, index) {
                File image = imagePickerProvider.selectedImages[index];
                return Image.file(image, fit: BoxFit.cover);
              },
            ),
          ),
        ],
      ),
    );
  }
}

完整代码结构

  • pubspec.yaml:包含依赖项
  • main.dart:应用的入口文件,初始化Provider和GetIt
  • providers/image_picker_provider.dart:包含ImagePickerProvider
  • screens/home_screen.dart:包含UI组件HomeScreen

这个示例展示了如何在Flutter项目中使用reactive_image_picker插件来选择和展示图片。确保你替换了reactive_image_pickerprovider等依赖项的版本号为实际可用的最新版本。

回到顶部