Web调用设备信息和服务
12147device_info_plus
用于获取当前设备信息。
//pubspec.yaml 中声明 device_info_plus
dependencies:
device_info_plus: ^3.2.2
//DeviceInfoUtil.dart 中引入包
import 'package:device_info_plus/device_info_plus.dart';
//实例化DeviceInfoPlugin,获取特定平台的设备信息
getDeviceInfo() async {
if (Platform.isAndroid) {
return _readAndroidBuildData(await deviceInfoPlugin.androidInfo);
} else if (...) {
//do something
}
}
Map<String, dynamic> _readAndroidBuildData(AndroidDeviceInfo build) {
return <String, dynamic>{
'version.securityPatch': build.version.securityPatch,
'version.sdkInt': build.version.sdkInt,
'version.release': build.version.release,
'version.previewSdkInt': build.version.previewSdkInt,
...
};
}
//项目初始化的时候获取设备信息
//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/JianghuConfigHandler.dart
Future<void> jianghuInit() async {
Constants.deviceInfo ??= await DeviceInfoUtil().getDeviceInfo();
}
package_info
用于查询有关应用程序包的信息。
//pubspec.yaml 中声明 package_info
dependencies:
package_info:
//main.dart中引入package_info
import 'package:package_info/package_info.dart';
//使用
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appName = packageInfo.appName;
String packageName = packageInfo.packageName;
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
preferences
用于持久化简单数据数据存储。
//pubspec.yaml 中声明 prefs
dependencies:
prefs: ^3.4.0+1
//main.dart中引入prefs
import 'package:prefs/prefs.dart';
//使用
await Prefs.init();
Constants.defaultHostUrl = Prefs.getString("homeUrl");
权限申请
用于请求权限并检查其状态。
//pubspec.yaml 中声明
dependencies:
permission_handler: ^9.2.0
//引入
import 'package:permission_handler/permission_handler.dart';
//使用
PermissionStatus storageStatus = await Permission.storage.status;
if (storageStatus != PermissionStatus.granted) {
throw '无法存储图片,请先授权!';
}
复制
用于将文本复制到剪贴板并从剪贴板粘贴。
//pubspec.yaml 中声明
dependencies:
clipboard: ^0.1.3
//引入
import 'package:clipboard/clipboard.dart';
//复制到剪贴板
FlutterClipboard.copy('hello flutter friends').then(( value ) => print('copied'));
//从剪贴板粘贴
FlutterClipboard.paste().then((value) {
// Do what ever you want with the value.
setState(() {
field.text = value;
pasteValue = value;
});
});
文件选择
用于查找文件系统上常用位置。
//pubspec.yaml 中声明
dependencies:
path_provider: ^2.0.9
//引入
import 'package:path_provider/path_provider.dart' as path_provider;
//使用
final Directory tempDir = await path_provider.getTemporaryDirectory();
final Directory appDocumentsDir = await path_provider.getApplicationDocumentsDirectory();
final Directory? downloadsDir = await path_provider.getDownloadsDirectory();
通知栏
用于显示本地通知。
//pubspec.yaml 中声明
dependencies:
flutter_local_notifications: ^9.6.0
//引入
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
//封装
class Notification {
final FlutterLocalNotificationsPlugin np = FlutterLocalNotificationsPlugin();
// main 初始化
init() async {
var android = const AndroidInitializationSettings("@mipmap/ic_launcher");
var ios = const IOSInitializationSettings();
await np.initialize(InitializationSettings(android: android, iOS: ios));
}
//发送消息
void send(String title, String body, {int? notificationId, String? params}) {
var androidDetails = const AndroidNotificationDetails(
'channelId',
'channelName',
importance: Importance.max,
priority: Priority.high,
);
//ios配置
var iosDetails = const IOSNotificationDetails();
var details = NotificationDetails(android: androidDetails, iOS: iosDetails);
np.show(notificationId ?? DateTime.now().millisecondsSinceEpoch >> 10,
title, body, details,
payload: params);
}
}
//使用
await notification.init();
notification.send("title", "content", params: json.encode(params));
音频播放
- 音乐播放器后台播放的入口点
//main.dart
@pragma('vm:entry-point')
Future<void> playerBackgroundService() async {
WidgetsFlutterBinding.ensureInitialized();
print("start playerBackgroundService");
runBackgroundService(
config: const Config(pauseWhenTaskRemoved: false, enableCache: false),
imageLoadInterceptor: BackgroundInterceptors.loadImageInterceptor,
playUriInterceptor: BackgroundInterceptors.playUriInterceptor,
playQueueInterceptor: QuietPlayQueueInterceptor(),
);
}
@pragma('vm:entry-point')
是一个Dart语言提供的元数据注解(Metadata Annotation)。它的作用是告诉Dart编译器,这些被注解的方法、变量、常量等应该被编译器当做程序的入口点(Entry Point)来生成可执行文件。
在Dart中,当我们运行一个程序时,Dart虚拟机需要知道程序的入口点(即从哪个方法开始执行),然后才能开始执行完整的程序。但有时我们的程序中可能包含了很多不是入口点的方法和变量,如果不使用@pragma('vm:entry-point')
注解,编译器可能会把这些打上“无用代码”标签,从而在编译或者优化时被标记为需要被移除的无用代码。这样的话,这些方法或变量就不能作为程序的入口点了。
- 播放
//方法定义:/jianghuAppBrowser/jianghuBrowser/lib/player/player_handler.dart
void playAudio(BuildContext context, Map result) {
//do something...
}
//引入包
import 'package:music_player/music_player.dart';
//实现
/jianghuAppBrowser/jianghuBrowser/lib/player/music_context.dart
转发
转发到其他 App
- 分享信息到其他APP方法定义
//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewHandler.dart
Future<void> shareMsgToOtherApp(Map<dynamic, dynamic> result, BuildContext context) async {
bool isText = result['msg']['messageContentType'] == 'text';
String msgContent = result['msg']['messageContent'];
if (Constants.hostUri == null) {
return;
}
if (!isText) {
Map msgContentMap = jsonDecode(msgContent);
String fileUrl = "${Constants.hostUri!.scheme}://${Constants.hostUri!.host}:${Constants.hostUri!.port}/${Constants.appId.value}/upload${msgContentMap['downloadPath']}";
String filename = msgContentMap['filename'];
String? fileFullPath = await DownloadUtil().downloadFile(fileUrl, filename, onReceiveProgress: (count, total) {
Utils.webToast("正在保存图片${(count / total * 100).toStringAsFixed(0)}%", 'loading');
});
if (fileFullPath == null) {
return;
}
Constants.isSkipAppResumed = true;
OpenFilex.share(fileFullPath);
} else {
FlutterClipboard.copy(msgContent).then((value) {
EasyLoading.showToast('内容已复制: $msgContent');
});
Constants.isSkipAppResumed = true;
OpenFilex.shareText(msgContent);
}
}
- 安卓端功能实现
//参考
/jianghuAppBrowser/jianghuBrowser/android/app/src/main/kotlin/org/fsll/jianghu_browser/MainActivity.kt
//配置:/jianghuAppBrowser/jianghuBrowser/android/app/src/main/AndroidManifest.xml
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
- ios端功能实现
待补充
接收转发
- 注册Android原生事件,接受其他app的分享
// 代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/JianghuConfigHandler.dart
void createEventChannelListen() {
Constants.channel = const EventChannel('share_from_other_app');
Constants.shareStreamSubscription = Constants.channel!.receiveBroadcastStream().listen((dynamic event) async {
if (event.runtimeType.toString() == 'String' && (event as String).contains('reg done')) {
} else {
print("share_from_other_app event ${event.keys} fileName:${event['fileName']} fileType:${event['fileType']}");
try {
// Uint8List temp = (event['bytes'].buffer as ByteBuffer).asUint8List();
// // 转换为 Uint8List
// debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> share_from_other_app Uint8List: ${temp}');
// 测试写入文件,回头删掉
Directory savePath = Directory("${(await getExternalCacheDirectories())!.first.path}/${Constants.appId.value}");
if (!savePath.existsSync()) {
savePath.createSync();
}
String fileName = event['fileName'];
if (!fileName.contains('.')) {
if (event['fileType'].contains('image/')) {
fileName = "$fileName.jpg";
}
if (event['fileType'].contains('video/')) {
fileName = "$fileName.mp4";
}
if (event['fileType'].contains('audio/')) {
fileName = "$fileName.mp3";
}
}
String fullPath = "${savePath.path}/$fileName";
File file = File(fullPath);
var raf = file.openSync(mode: FileMode.write);
await raf.writeFrom(event['bytes']);
await raf.close();
// 暂存流,当用户点击某个聊天/好友/群组的时候,发送流到web端,进行流的上传并且转发
FileUtil.sendFileToWebByStream(
actionTime: "${DateTime.now().millisecondsSinceEpoch}", filePath: fullPath, fileName: fileName, delete: true);
} catch (error) {
debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> share_from_other_app error: $error');
}
}
// TODO 接受的内容发送到webview, 便于发送到某个用户
}, onError: (dynamic error) {
debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> share_from_other_app Received error: ${error.message}');
}, cancelOnError: true);
}
- 安卓端功能实现
//参考
/jianghuAppBrowser/jianghuBrowser/android/app/src/main/kotlin/org/fsll/jianghu_browser/MainActivity.kt
//注册通道,eventSink 给flutter端发送信息
EventChannel(binaryMessenger, "share_from_other_app").apply
//流程
onNewIntent -> handlerSendIntent -> 使用eventSink 给flutter端发送信息 -》flutter收到消息,给web端发消息,-> web 弹窗选择好友或房间 -》 回调给flutter =》 如果是图片则保存到本地,如果是文字直接发送 -》web端接收到消息,sendmessage
IOS 安卓端功能实现
待补充
保存图片到相册
用于将图片保存到图库中。
//pubspec.yaml 中声明
dependencies:
image_gallery_saver: ^1.7.1
//引入
import 'package:image_gallery_saver/image_gallery_saver.dart';
//使用
final saveResult = await ImageGallerySaver.saveImage(Uint8List.fromList(File(fullPath).readAsBytesSync()),
name: "${DateTime.now().millisecondsSinceEpoch}_${result['name']}", isReturnImagePathOfIOS: Platform.isIOS);
if (saveResult == null || saveResult == '') {
Utils.webToast("保存失败", 'fail');
} else {
Utils.webToast("保存到相册成功", 'success');
}