Flutter文件选择插件reactive_file_picker的使用

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

Flutter文件选择插件reactive_file_picker的使用

reactive_file_picker 是一个围绕 file_picker 插件的包装器,旨在与 reactive_forms 一起使用。它提供了一种方便的方式来处理文件选择,并将其集成到表单中。

安装

首先,在 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  reactive_file_picker: ^最新版本
  reactive_forms: ^最新版本

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

示例代码

以下是一个完整的示例代码,展示了如何在 Flutter 应用中使用 reactive_file_picker 插件。

main.dart

import 'package:flutter/material.dart';
import 'package:reactive_file_picker/reactive_file_picker.dart';
import 'package:reactive_forms/reactive_forms.dart';
import 'package:file_picker/file_picker.dart';

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

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

  FormGroup buildForm() => fb.group({
        'multiImage': FormControl<MultiFile<String>>(
          value: MultiFile<String>.fromFiles(
            [
              'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg',
              // just to test `fromFiles` factory
              null,
            ],
          ),
        ),
      });

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('Reactive File Picker Example')),
        body: 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: [
                    Container(
                      constraints: const BoxConstraints(minHeight: 0, maxHeight: 300),
                      child: ReactiveFilePicker<String>(
                        formControlName: 'multiImage',
                        filePickerBuilder: (pickImage, files, onChange) {
                          final items = [
                            ...files.files
                                .asMap()
                                .map((key, value) => MapEntry(
                                    key,
                                    ListTile(
                                      onTap: () {
                                        onChange(files.copyWith(
                                            files: List<String>.from(files.files)..removeAt(key)));
                                      },
                                      leading: const Icon(Icons.delete),
                                      title: FileListItem(value).build(context),
                                    )))
                                .values,
                            ...files.platformFiles
                                .asMap()
                                .map((key, value) => MapEntry(
                                      key,
                                      ListTile(
                                        onTap: () {
                                          onChange(files.copyWith(
                                              platformFiles: List<PlatformFile>.from(files.platformFiles)..removeAt(key)));
                                        },
                                        leading: const Icon(Icons.delete),
                                        title: PlatformFileListItem(value).build(context),
                                      ),
                                    ))
                                .values,
                          ];

                          return Column(
                            children: [
                              Expanded(
                                child: ListView.builder(
                                  itemCount: items.length,
                                  itemBuilder: (_, i) {
                                    return items[i];
                                  },
                                ),
                              ),
                              ElevatedButton(
                                onPressed: pickImage,
                                child: const Text("Pick images"),
                              ),
                            ],
                          );
                        },
                        decoration: const InputDecoration(
                          labelText: 'Multi image picker',
                          border: OutlineInputBorder(),
                          helperText: '',
                        ),
                      ),
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      child: const Text('Submit'),
                      onPressed: () {
                        if (form.valid) {
                          print(form.value);
                        } else {
                          form.markAllAsTouched();
                        }
                      },
                    ),
                  ],
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}

abstract class ListItem {
  Widget build(BuildContext context);
}

class FileListItem extends ListItem {
  final String url;

  FileListItem(this.url);

  @override
  Widget build(context) {
    return Text(url);
  }
}

class PlatformFileListItem extends ListItem {
  final PlatformFile platformFile;

  PlatformFileListItem(this.platformFile);

  @override
  Widget build(context) {
    return Text(platformFile.path ?? '');
  }
}

代码解释

  1. 导入依赖

    import 'package:flutter/material.dart';
    import 'package:reactive_file_picker/reactive_file_picker.dart';
    import 'package:reactive_forms/reactive_forms.dart';
    import 'package:file_picker/file_picker.dart';
    
  2. 构建表单

    FormGroup buildForm() => fb.group({
          'multiImage': FormControl<MultiFile<String>>(
            value: MultiFile<String>.fromFiles(
              [
                'https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg',
                null,
              ],
            ),
          ),
        });
    
  3. 创建主应用

    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: Scaffold(
            appBar: AppBar(title: const Text('Reactive File Picker Example')),
            body: 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: [
                        // 文件选择器
                        Container(
                          constraints: const BoxConstraints(minHeight: 0, maxHeight: 300),
                          child: ReactiveFilePicker<String>(
                            formControlName: 'multiImage',
                            filePickerBuilder: (pickImage, files, onChange) {
                              // 构建文件列表
                              final items = [
                                ...files.files
                                    .asMap()
                                    .map((key, value) => MapEntry(
                                        key,
                                        ListTile(
                                          onTap: () {
                                            onChange(files.copyWith(
                                                files: List<String>.from(files.files)..removeAt(key)));
                                          },
                                          leading: const Icon(Icons.delete),
                                          title: FileListItem(value).build(context),
                                        )))
                                    .values,
                                ...files.platformFiles
                                    .asMap()
                                    .map((key, value) => MapEntry(
                                          key,
                                          ListTile(
                                            onTap: () {
                                              onChange(files.copyWith(
                                                  platformFiles: List<PlatformFile>.from(files.platformFiles)..removeAt(key)));
                                            },
                                            leading: const Icon(Icons.delete),
                                            title: PlatformFileListItem(value).build(context),
                                          ),
                                        ))
                                    .values,
                              ];
    
                              return Column(
                                children: [
                                  Expanded(
                                    child: ListView.builder(
                                      itemCount: items.length,
                                      itemBuilder: (_, i) {
                                        return items[i];
                                      },
                                    ),
                                  ),
                                  ElevatedButton(
                                    onPressed: pickImage,
                                    child: const Text("Pick images"),
                                  ),
                                ],
                              );
                            },
                            decoration: const InputDecoration(
                              labelText: 'Multi image picker',
                              border: OutlineInputBorder(),
                              helperText: '',
                            ),
                          ),
                        ),
                        const SizedBox(height: 16),
                        ElevatedButton(
                          child: const Text('Submit'),
                          onPressed: () {
                            if (form.valid) {
                              print(form.value);
                            } else {
                              form.markAllAsTouched();
                            }
                          },
                        ),
                      ],
                    );
                  },
                ),
              ),
            ),
          ),
        );
      }
    }
    
  4. 定义文件列表项

    abstract class ListItem {
      Widget build(BuildContext context);
    }
    
    class FileListItem extends ListItem {
      final String url;
    
      FileListItem(this.url);
    
      @override
      Widget build(context) {
        return Text(url);
      }
    }
    
    class PlatformFileListItem extends ListItem {
      final PlatformFile platformFile;
    
      PlatformFileListItem(this.platformFile);
    
      @override
      Widget build(context) {
        return Text(platformFile.path ?? '');
      }
    }
    

通过以上步骤,你可以在 Flutter 应用中使用 reactive_file_picker 插件来实现文件选择功能,并将其集成到表单中。希望这个示例对你有所帮助!


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

1 回复

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


当然,下面是一个关于如何在Flutter应用中使用reactive_file_picker插件来选择文件的代码示例。这个插件允许用户从设备中选择文件(如图片、文档等),并以响应式编程的方式处理文件选择结果。

首先,确保你已经在pubspec.yaml文件中添加了reactive_file_picker依赖:

dependencies:
  flutter:
    sdk: flutter
  reactive_file_picker: ^x.y.z  # 请替换为最新版本号

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

接下来,在你的Flutter项目中,你可以按照以下步骤使用reactive_file_picker

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:reactive_file_picker/reactive_file_picker.dart';
import 'package:get/get.dart';  // 如果你使用GetX进行状态管理,否则可以省略
  1. 创建控制器
final _picker = FilePickerController();
  1. 在UI中使用文件选择器
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('File Picker Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () async {
                  // 打开文件选择器
                  final result = await _picker.pickFiles(
                    type: FileType.any,
                    allowMultiple: true,
                  );

                  if (result != null) {
                    // 处理选择的文件
                    result.files.forEach((file) {
                      print(file.path);
                      print(file.name);
                      print(file.bytes!.lengthInBytes);
                      // 你可以在这里添加更多处理逻辑,比如上传文件等
                    });
                  }
                },
                child: Text('Select Files'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
  1. 在应用启动时初始化控制器(如果你使用GetX进行状态管理,可以在控制器类中初始化):
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await _picker.initialize();  // 初始化文件选择器控制器
  runApp(MyApp());
}

注意:如果你不使用GetX或其他状态管理库,你可以在MyApp类的initState方法中初始化控制器,但请确保在UI组件构建之前完成初始化。

  1. 清理资源(可选,但推荐):

在应用关闭或不再需要文件选择器时,你可以调用dispose方法来清理资源:

@override
void dispose() {
  _picker.dispose();
  super.dispose();
}

在这个例子中,我们创建了一个简单的Flutter应用,其中包含一个按钮,用户点击该按钮时会打开一个文件选择器对话框。用户可以选择一个或多个文件,选择的文件信息将被打印到控制台。

请确保在实际应用中处理文件路径和文件数据时遵守相关的隐私和安全最佳实践。

回到顶部