diff --git a/example/integration_test/downloader_integration_test.dart b/example/integration_test/downloader_integration_test.dart index ad38e00d..149dd60a 100644 --- a/example/integration_test/downloader_integration_test.dart +++ b/example/integration_test/downloader_integration_test.dart @@ -1434,53 +1434,51 @@ void main() { expect(result['json'], equals({'field1': 1})); }); - testWidgets('post DownloadTask with post is Map', - (widgetTester) async { - final task = DownloadTask( - url: postTestUrl, - urlQueryParameters: {'request-type': 'post-json'}, - filename: postFilename, - headers: { - 'Header1': 'headerValue1', - 'content-type': 'application/json' - }, - post: {"field1": 1}); - final path = + testWidgets('post DownloadTask with post is Map', (widgetTester) async { + final task = DownloadTask( + url: postTestUrl, + urlQueryParameters: {'request-type': 'post-json'}, + filename: postFilename, + headers: { + 'Header1': 'headerValue1', + 'content-type': 'application/json' + }, + post: {"field1": 1}); + final path = join((await getApplicationDocumentsDirectory()).path, task.filename); - expect((await FileDownloader().download(task)).status, - equals(TaskStatus.complete)); - final result = jsonDecode(await File(path).readAsString()); - print(result); - expect(result['args']['request-type'], equals('post-json')); - expect(result['headers']['Header1'], equals('headerValue1')); - expect(result['data'], equals('{"field1":1}')); - // confirm the server side interpreted this as JSON - expect(result['json'], equals({'field1': 1})); - }); - - testWidgets('post DownloadTask with post is List', - (widgetTester) async { - final task = DownloadTask( - url: postTestUrl, - urlQueryParameters: {'request-type': 'post-json'}, - filename: postFilename, - headers: { - 'Header1': 'headerValue1', - 'content-type': 'application/json' - }, - post: ['apple', 'orange']); - final path = + expect((await FileDownloader().download(task)).status, + equals(TaskStatus.complete)); + final result = jsonDecode(await File(path).readAsString()); + print(result); + expect(result['args']['request-type'], equals('post-json')); + expect(result['headers']['Header1'], equals('headerValue1')); + expect(result['data'], equals('{"field1":1}')); + // confirm the server side interpreted this as JSON + expect(result['json'], equals({'field1': 1})); + }); + + testWidgets('post DownloadTask with post is List', (widgetTester) async { + final task = DownloadTask( + url: postTestUrl, + urlQueryParameters: {'request-type': 'post-json'}, + filename: postFilename, + headers: { + 'Header1': 'headerValue1', + 'content-type': 'application/json' + }, + post: ['apple', 'orange']); + final path = join((await getApplicationDocumentsDirectory()).path, task.filename); - expect((await FileDownloader().download(task)).status, - equals(TaskStatus.complete)); - final result = jsonDecode(await File(path).readAsString()); - print(result); - expect(result['args']['request-type'], equals('post-json')); - expect(result['headers']['Header1'], equals('headerValue1')); - expect(result['data'], equals('["apple","orange"]')); - // confirm the server side interpreted this as JSON - expect(result['json'], equals(['apple', 'orange'])); - }); + expect((await FileDownloader().download(task)).status, + equals(TaskStatus.complete)); + final result = jsonDecode(await File(path).readAsString()); + print(result); + expect(result['args']['request-type'], equals('post-json')); + expect(result['headers']['Header1'], equals('headerValue1')); + expect(result['data'], equals('["apple","orange"]')); + // confirm the server side interpreted this as JSON + expect(result['json'], equals(['apple', 'orange'])); + }); testWidgets('post DownloadTask with post is invalid type', (widgetTester) async { @@ -1575,14 +1573,14 @@ void main() { }); testWidgets('post request with post is Map', (widgetTester) async { - final request = Request( - url: postTestUrl, - urlQueryParameters: {'request-type': 'post-json'}, - headers: { - 'Header1': 'headerValue1', - 'content-type': 'application/json' - }, - post: {'field': 1}); + final request = Request(url: postTestUrl, urlQueryParameters: { + 'request-type': 'post-json' + }, headers: { + 'Header1': 'headerValue1', + 'content-type': 'application/json' + }, post: { + 'field': 1 + }); final response = await FileDownloader().request(request); expect(response.statusCode, equals(200)); final result = jsonDecode(response.body); @@ -1594,14 +1592,15 @@ void main() { }); testWidgets('post request with post is List', (widgetTester) async { - final request = Request( - url: postTestUrl, - urlQueryParameters: {'request-type': 'post-json'}, - headers: { - 'Header1': 'headerValue1', - 'content-type': 'application/json' - }, - post: ['apple', 'orange']); + final request = Request(url: postTestUrl, urlQueryParameters: { + 'request-type': 'post-json' + }, headers: { + 'Header1': 'headerValue1', + 'content-type': 'application/json' + }, post: [ + 'apple', + 'orange' + ]); final response = await FileDownloader().request(request); expect(response.statusCode, equals(200)); final result = jsonDecode(response.body); @@ -1718,10 +1717,12 @@ void main() { taskStatusCallback: statusCallback, taskProgressCallback: progressCallback); expect( - await FileDownloader().enqueue(uploadTask.copyWith( - fields: {'field1': 'value1', 'field2': 'check\u2713', - 'field3': '{"a":"b"}', 'field4': '["1", "2"]'}, - updates: Updates.statusAndProgress)), + await FileDownloader().enqueue(uploadTask.copyWith(fields: { + 'field1': 'value1', + 'field2': 'check\u2713', + 'field3': '{"a":"b"}', + 'field4': '["1", "2"]' + }, updates: Updates.statusAndProgress)), isTrue); await statusCallbackCompleter.future; expect(statusCallbackCounter, equals(3)); @@ -3342,22 +3343,36 @@ void main() { group('Task functions', () { test('baseDirectoryPath', () async { for (final baseDirectoryEnum in BaseDirectory.values) { - final task = - DownloadTask(url: workingUrl, baseDirectory: baseDirectoryEnum); + final subdirName = + (Platform.isWindows && baseDirectoryEnum == BaseDirectory.root) + ? 'C:\\' + : ''; + final task = DownloadTask( + url: workingUrl, + baseDirectory: baseDirectoryEnum, + directory: subdirName); final path = await task.filePath(); final dir = dirname(path); print('For $baseDirectoryEnum the dir is $dir'); - expect(await Task.baseDirectoryPath(baseDirectoryEnum), equals(dir)); + if (Platform.isWindows && baseDirectoryEnum == BaseDirectory.root) { + expect(dir, equals('C:\\')); + } else { + expect(await Task.baseDirectoryPath(baseDirectoryEnum), equals(dir)); + } } }); test('split with filePath parameter', () async { for (final subdirName in ['something', '']) { for (final baseDirectoryEnum in BaseDirectory.values) { + final modifiedSubdirName = + (Platform.isWindows && baseDirectoryEnum == BaseDirectory.root) + ? 'C:\\$subdirName' + : subdirName; final task = DownloadTask( url: workingUrl, baseDirectory: baseDirectoryEnum, - directory: subdirName); + directory: modifiedSubdirName); final path = await task.filePath(); print('Testing path $path'); final (baseDirectory, directory, filename) = @@ -3372,10 +3387,14 @@ void main() { test('split with File parameter', () async { for (final subdirName in ['something', '']) { for (final baseDirectoryEnum in BaseDirectory.values) { + final modifiedSubdirName = + (Platform.isWindows && baseDirectoryEnum == BaseDirectory.root) + ? 'C:\\$subdirName' + : subdirName; final task = DownloadTask( url: workingUrl, baseDirectory: baseDirectoryEnum, - directory: subdirName); + directory: modifiedSubdirName); final path = await task.filePath(); print('Testing path $path'); final (baseDirectory, directory, filename) = diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt index 930d2071..903f4899 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp index b25e363e..955ee303 100644 --- a/example/windows/runner/flutter_window.cpp +++ b/example/windows/runner/flutter_window.cpp @@ -31,6 +31,11 @@ bool FlutterWindow::OnCreate() { this->Show(); }); + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + return true; } diff --git a/lib/src/task.dart b/lib/src/task.dart index c7c811a0..6d40cb4f 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -360,6 +360,10 @@ sealed class Task extends Request implements Comparable { } /// Returns the path to the directory represented by [baseDirectory] + /// + /// On Windows, if [baseDirectory] is .root, returns the empty string + /// because the drive letter is required to be included in the directory + /// path static Future baseDirectoryPath(BaseDirectory baseDirectory) async { Directory? externalStorageDirectory; Directory? externalCacheDirectory; @@ -391,7 +395,9 @@ sealed class Task extends Request implements Comparable { (BaseDirectory.applicationLibrary, true) => Directory(p.join(externalStorageDirectory!.path, 'Library')) }; - return baseDir.absolute.path; + return (Platform.isWindows && baseDirectory == BaseDirectory.root) + ? '' + : baseDir.absolute.path; } /// Extract the baseDirectory, directory and filename from @@ -412,19 +418,20 @@ sealed class Task extends Request implements Comparable { // try to match the start of the absoluteDirectory to one of the // directories represented by the BaseDirectory enum. // Order matters, as some may be subdirs of others - final testSequence = Platform.isAndroid || Platform.isLinux - ? [ - BaseDirectory.temporary, - BaseDirectory.applicationLibrary, - BaseDirectory.applicationSupport, - BaseDirectory.applicationDocuments - ] - : [ - BaseDirectory.temporary, - BaseDirectory.applicationSupport, - BaseDirectory.applicationLibrary, - BaseDirectory.applicationDocuments - ]; + final testSequence = + Platform.isAndroid || Platform.isLinux || Platform.isWindows + ? [ + BaseDirectory.temporary, + BaseDirectory.applicationLibrary, + BaseDirectory.applicationSupport, + BaseDirectory.applicationDocuments + ] + : [ + BaseDirectory.temporary, + BaseDirectory.applicationSupport, + BaseDirectory.applicationLibrary, + BaseDirectory.applicationDocuments + ]; for (final baseDirectoryEnum in testSequence) { final baseDirPath = await baseDirectoryPath(baseDirectoryEnum); final (match, directory) = _contains(baseDirPath, absoluteDirectoryPath); @@ -451,7 +458,9 @@ sealed class Task extends Request implements Comparable { /// [dirPath] should not contain a filename - if it does, it is returned /// as part of the subdir. static (bool, String) _contains(String baseDirPath, String dirPath) { - final match = RegExp('^$baseDirPath/?(.*)').firstMatch(dirPath); + final escapedBaseDirPath = + '$baseDirPath${Platform.pathSeparator}?'.replaceAll(r'\', r'\\'); + final match = RegExp('^$escapedBaseDirPath(.*)').firstMatch(dirPath); return (match != null, match?.group(1) ?? ''); }