Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[file_selector] Migrate Windows implementation from CPP to Dart #6568

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/file_selector/file_selector_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.0.0

* Migrates implementation from Cpp to Dart.

## 0.9.1+4

* Changes XTypeGroup initialization from final to const.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:flutter/material.dart';
/// then displays the selected directory in a dialog.
class GetDirectoryPage extends StatelessWidget {
/// Default Constructor
const GetDirectoryPage({Key? key}) : super(key: key);
const GetDirectoryPage({super.key});

Future<void> _getDirectoryPath(BuildContext context) async {
const String confirmButtonText = 'Choose';
Expand Down Expand Up @@ -58,7 +58,7 @@ class GetDirectoryPage extends StatelessWidget {
/// Widget that displays a text file in a dialog.
class TextDisplay extends StatelessWidget {
/// Creates a `TextDisplay`.
const TextDisplay(this.directoryPath, {Key? key}) : super(key: key);
const TextDisplay(this.directoryPath, {super.key});

/// The path selected in the dialog.
final String directoryPath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
/// Home Page of the application.
class HomePage extends StatelessWidget {
/// Default Constructor
const HomePage({Key? key}) : super(key: key);
const HomePage({super.key});

@override
Widget build(BuildContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void main() {
/// MyApp is the Main Application.
class MyApp extends StatelessWidget {
/// Default Constructor
const MyApp({Key? key}) : super(key: key);
const MyApp({super.key});

@override
Widget build(BuildContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
/// `openFiles`, then displays the selected images in a gallery dialog.
class OpenImagePage extends StatelessWidget {
/// Default Constructor
const OpenImagePage({Key? key}) : super(key: key);
const OpenImagePage({super.key});

Future<void> _openImageFile(BuildContext context) async {
const XTypeGroup typeGroup = XTypeGroup(
Expand Down Expand Up @@ -65,8 +65,7 @@ class OpenImagePage extends StatelessWidget {
/// Widget that displays an image in a dialog.
class ImageDisplay extends StatelessWidget {
/// Default Constructor.
const ImageDisplay(this.fileName, this.filePath, {Key? key})
: super(key: key);
const ImageDisplay(this.fileName, this.filePath, {super.key});

/// The name of the selected file.
final String fileName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
/// `openFiles`, then displays the selected images in a gallery dialog.
class OpenMultipleImagesPage extends StatelessWidget {
/// Default Constructor
const OpenMultipleImagesPage({Key? key}) : super(key: key);
const OpenMultipleImagesPage({super.key});

Future<void> _openImageFile(BuildContext context) async {
const XTypeGroup jpgsTypeGroup = XTypeGroup(
Expand Down Expand Up @@ -69,7 +69,7 @@ class OpenMultipleImagesPage extends StatelessWidget {
/// Widget that displays a text file in a dialog.
class MultipleImagesDisplay extends StatelessWidget {
/// Default Constructor.
const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key);
const MultipleImagesDisplay(this.files, {super.key});

/// The files containing the images.
final List<XFile> files;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:typed_data';

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

/// Screen that allows the user to select a text file using `openFile`, then
/// displays its contents in a dialog.
class OpenTextPage extends StatelessWidget {
/// Default Constructor
const OpenTextPage({Key? key}) : super(key: key);
const OpenTextPage({super.key});
adpinola marked this conversation as resolved.
Show resolved Hide resolved

Future<void> _openTextFile(BuildContext context) async {
const XTypeGroup typeGroup = XTypeGroup(
Expand All @@ -23,7 +25,14 @@ class OpenTextPage extends StatelessWidget {
return;
}
final String fileName = file.name;
final String fileContent = await file.readAsString();

// This behavior defaults works when reading files encoded using UTF-16 LE.
// If you have files encoded with UTF-8 you can simply use file.readAsString()
// For other encodings, consider using Encoding.getByName() method, to get your encoder
// before calling file.readAsString()
final Uint8List bytes = await file.readAsBytes();
final Uint16List utf16CodeUnits = bytes.buffer.asUint16List();
final String fileContent = String.fromCharCodes(utf16CodeUnits);

await showDialog<void>(
context: context,
Expand Down Expand Up @@ -62,8 +71,7 @@ class OpenTextPage extends StatelessWidget {
/// Widget that displays a text file in a dialog.
class TextDisplay extends StatelessWidget {
/// Default Constructor.
const TextDisplay(this.fileName, this.fileContent, {Key? key})
: super(key: key);
const TextDisplay(this.fileName, this.fileContent, {super.key});

/// The name of the selected file.
final String fileName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:flutter/material.dart';
/// then writes text to a file at that location.
class SaveTextPage extends StatelessWidget {
/// Default Constructor
SaveTextPage({Key? key}) : super(key: key);
SaveTextPage({super.key});

final TextEditingController _nameController = TextEditingController();
final TextEditingController _contentController = TextEditingController();
Expand All @@ -25,10 +25,12 @@ class SaveTextPage extends StatelessWidget {
return;
}
final String text = _contentController.text;
final Uint8List fileData = Uint8List.fromList(text.codeUnits);

// This behavior saves an Utf-16 LE encoded file.
final Uint16List fileData = Uint16List.fromList(text.codeUnits);
const String fileMimeType = 'text/plain';
final XFile textFile =
XFile.fromData(fileData, mimeType: fileMimeType, name: fileName);
final XFile textFile = XFile.fromData(fileData.buffer.asUint8List(),
mimeType: fileMimeType, name: fileName);
await textFile.saveTo(path);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ publish_to: 'none'
version: 1.0.0

environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.10.0"
sdk: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"

dependencies:
file_selector_platform_interface: ^2.2.0
file_selector_platform_interface: ^2.3.0
file_selector_windows:
# When depending on this package from a real application you should use:
# file_selector_windows: ^x.y.z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@
// found in the LICENSE file.

import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:win32/win32.dart';

import 'src/messages.g.dart';
import 'src/file_selector_api.dart';
import 'src/file_selector_dart/dialog_wrapper_factory.dart';
import 'src/file_selector_dart/file_dialog_controller_factory.dart';
import 'src/file_selector_dart/ifile_dialog_factory.dart';
import 'src/file_selector_dart/selection_options.dart';

/// An implementation of [FileSelectorPlatform] for Windows.
class FileSelectorWindows extends FileSelectorPlatform {
final FileSelectorApi _hostApi = FileSelectorApi();
/// Creates a new instance of [FileSelectorApi].
FileSelectorWindows()
: _hostApi = FileSelectorApi(
DialogWrapperFactory(
FileDialogControllerFactory(),
IFileDialogFactory(),
),
GetActiveWindow());

/// Creates a fake implementation of [FileSelectorApi] for testing purposes.
@visibleForTesting
FileSelectorWindows.useFakeApi(this._hostApi);

final FileSelectorApi _hostApi;

/// Registers the Windows implementation.
static void registerWith() {
Expand All @@ -21,14 +40,11 @@ class FileSelectorWindows extends FileSelectorPlatform {
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String?> paths = await _hostApi.showOpenDialog(
SelectionOptions(
allowMultiple: false,
selectFolders: false,
allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups),
),
initialDirectory,
confirmButtonText);
final List<String?> paths = _hostApi.showOpenDialog(
SelectionOptions(allowedTypes: _allowedXTypeGroups(acceptedTypeGroups)),
initialDirectory,
confirmButtonText,
);
adpinola marked this conversation as resolved.
Show resolved Hide resolved
return paths.isEmpty ? null : XFile(paths.first!);
}

Expand All @@ -38,14 +54,13 @@ class FileSelectorWindows extends FileSelectorPlatform {
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String?> paths = await _hostApi.showOpenDialog(
SelectionOptions(
final List<String?> paths = _hostApi.showOpenDialog(
SelectionOptions(
allowMultiple: true,
selectFolders: false,
allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups),
),
initialDirectory,
confirmButtonText);
allowedTypes: _allowedXTypeGroups(acceptedTypeGroups)),
initialDirectory,
confirmButtonText,
);
return paths.map((String? path) => XFile(path!)).toList();
}

Expand All @@ -56,15 +71,12 @@ class FileSelectorWindows extends FileSelectorPlatform {
String? suggestedName,
String? confirmButtonText,
}) async {
final List<String?> paths = await _hostApi.showSaveDialog(
SelectionOptions(
allowMultiple: false,
selectFolders: false,
allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups),
),
initialDirectory,
suggestedName,
confirmButtonText);
final List<String?> paths = _hostApi.showSaveDialog(
SelectionOptions(allowedTypes: _allowedXTypeGroups(acceptedTypeGroups)),
initialDirectory,
suggestedName,
confirmButtonText,
);
return paths.isEmpty ? null : paths.first!;
}

Expand All @@ -73,27 +85,23 @@ class FileSelectorWindows extends FileSelectorPlatform {
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String?> paths = await _hostApi.showOpenDialog(
SelectionOptions(
allowMultiple: false,
selectFolders: true,
allowedTypes: <TypeGroup>[],
),
initialDirectory,
confirmButtonText);
final List<String?> paths = _hostApi.showOpenDialog(
SelectionOptions(selectFolders: true, allowedTypes: <XTypeGroup>[]),
initialDirectory,
confirmButtonText,
);
return paths.isEmpty ? null : paths.first!;
}
}

List<TypeGroup> _typeGroupsFromXTypeGroups(List<XTypeGroup>? xtypes) {
List<XTypeGroup> _allowedXTypeGroups(List<XTypeGroup>? xtypes) {
return (xtypes ?? <XTypeGroup>[]).map((XTypeGroup xtype) {
if (!xtype.allowsAny && (xtype.extensions?.isEmpty ?? true)) {
throw ArgumentError('Provided type group $xtype does not allow '
'all files, but does not set any of the Windows-supported filter '
'categories. "extensions" must be non-empty for Windows if '
'anything is non-empty.');
}
return TypeGroup(
label: xtype.label ?? '', extensions: xtype.extensions ?? <String>[]);
return xtype;
}).toList();
}
Loading