Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(share_plus): share XFile created using File.fromData() #1284

Merged
merged 10 commits into from Oct 24, 2022
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -6,6 +6,8 @@

import 'dart:io';

import 'package:file_selector/file_selector.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:share_plus/share_plus.dart';
import 'package:integration_test/integration_test.dart';
Expand All @@ -24,4 +26,13 @@ void main() {
testWidgets('Can launch shareWithResult', (WidgetTester tester) async {
expect(Share.shareWithResult('message', subject: 'title'), isNotNull);
});

testWidgets('Can shareXFile created using File.fromData()',
(WidgetTester tester) async {
final bytes = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8]);
final XFile file =
XFile.fromData(bytes, name: 'image.jpg', mimeType: 'image/jpeg');

expect(Share.shareXFiles([file], text: "example"), isNotNull);
});
}
32 changes: 32 additions & 0 deletions packages/share_plus/share_plus/example/lib/main.dart
Expand Up @@ -8,6 +8,7 @@ import 'dart:io';

import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:share_plus/share_plus.dart';

Expand Down Expand Up @@ -123,6 +124,17 @@ class DemoAppState extends State<DemoApp> {
);
},
),
const Padding(padding: EdgeInsets.only(top: 12.0)),
Builder(
builder: (BuildContext context) {
return ElevatedButton(
onPressed: () {
_onShareXFileFromAssets(context);
},
child: const Text('Share XFile from Assets'),
);
},
),
],
),
),
Expand Down Expand Up @@ -185,4 +197,24 @@ class DemoAppState extends State<DemoApp> {
content: Text("Share result: ${result.status}"),
));
}

void _onShareXFileFromAssets(BuildContext context) async {
final box = context.findRenderObject() as RenderBox?;
final scaffoldMessenger = ScaffoldMessenger.of(context);
final data = await rootBundle.load('assets/flutter_logo.png');
final buffer = data.buffer;
final result = await Share.shareXFiles(
[
XFile.fromData(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
name: 'flutter_logo.png',
mimeType: 'image/png'),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);

scaffoldMessenger.showSnackBar(SnackBar(
content: Text("Share result: ${result.status}"),
));
}
}
3 changes: 3 additions & 0 deletions packages/share_plus/share_plus/example/pubspec.yaml
Expand Up @@ -25,6 +25,9 @@ dev_dependencies:
flutter:
uses-material-design: true

assets:
- assets/flutter_logo.png

environment:
sdk: '>=2.12.0 <3.0.0'
flutter: ">=1.20.0"
Expand Up @@ -3,15 +3,19 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:io';

// Keep dart:ui for retrocompatiblity with Flutter <3.3.0
// ignore: unnecessary_import
import 'dart:ui';

import 'package:cross_file/cross_file.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart' show visibleForTesting;
import 'package:mime/mime.dart' show lookupMimeType;
import 'package:mime/mime.dart' show extensionFromMime, lookupMimeType;
import 'package:share_plus_platform_interface/share_plus_platform_interface.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

/// Plugin for summoning a platform share sheet.
class MethodChannelShare extends SharePlatform {
Expand Down Expand Up @@ -141,19 +145,58 @@ class MethodChannelShare extends SharePlatform {
String? subject,
String? text,
Rect? sharePositionOrigin,
}) {
final mimeTypes =
files.map((e) => e.mimeType ?? _mimeTypeForPath(e.path)).toList();
}) async {
final filesWithPath = await _getFiles(files);

final mimeTypes = filesWithPath
.map((e) => e.mimeType ?? _mimeTypeForPath(e.path))
.toList();

return shareFilesWithResult(
files.map((e) => e.path).toList(),
filesWithPath.map((e) => e.path).toList(),
mimeTypes: mimeTypes,
subject: subject,
text: text,
sharePositionOrigin: sharePositionOrigin,
);
}

/// if file doesn't contain path
/// then make new file in TemporaryDirectory and return with path
///
/// the system will automatically delete files in this
/// TemporaryDirectory as disk space is needed elsewhere on the device
Future<List<XFile>> _getFiles(List<XFile> files) async {
if (files.any((element) => element.path.isEmpty)) {
final newFiles = <XFile>[];

final String tempPath = (await getTemporaryDirectory()).path;

const uuid = Uuid();
for (final XFile element in files) {
if (element.path.isEmpty) {
final name = uuid.v4();

final extension =
extensionFromMime(element.mimeType ?? 'octet-stream');

final path = '$tempPath/$name.$extension';
final file = File(path);

await file.writeAsBytes(await element.readAsBytes());

newFiles.add(XFile(path));
} else {
newFiles.add(element);
}
}

return newFiles;
} else {
return files;
}
}

static String _mimeTypeForPath(String path) {
return lookupMimeType(path) ?? 'application/octet-stream';
}
Expand Down
Expand Up @@ -11,6 +11,8 @@ dependencies:
meta: ^1.7.0
mime: ^1.0.2
plugin_platform_interface: ^2.0.0
path_provider: ^2.0.11
uuid: ^3.0.6

dev_dependencies:
flutter_test:
Expand Down
Expand Up @@ -163,10 +163,7 @@ void main() {
() => SharePlatform.instance.shareFilesWithResult(['']),
throwsA(const TypeMatcher<AssertionError>()),
);
expect(
() => sharePlatform.shareXFiles([XFile('')]),
throwsA(const TypeMatcher<AssertionError>()),
);

verifyZeroInteractions(mockChannel);
});

Expand Down