Skip to content

Commit

Permalink
feat(share_plus): share XFile created using File.fromData() (#1284)
Browse files Browse the repository at this point in the history
Co-authored-by: Miguel Beltran <m@beltran.work>
  • Loading branch information
dipakp2726 and miquelbeltran committed Oct 24, 2022
1 parent dfa653d commit 4ce5575
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 9 deletions.
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,5 +25,8 @@ dev_dependencies:
flutter:
uses-material-design: true

assets:
- assets/flutter_logo.png

environment:
sdk: '>=2.12.0 <3.0.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

0 comments on commit 4ce5575

Please sign in to comment.