完成视频日志、监控模块的UI
7
star_lock/images/bumble_bee_captions.vtt
Normal file
@ -0,0 +1,7 @@
|
||||
WEBVTT
|
||||
|
||||
00:00:00.200 --> 00:00:01.750
|
||||
[ Birds chirping ]
|
||||
|
||||
00:00:02.300 --> 00:00:05.000
|
||||
[ Buzzing ]
|
||||
BIN
star_lock/images/icon_round_white_unSelet.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
star_lock/images/icon_test20231113.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
star_lock/images/main/icon_lockDetail_messageReminding.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringCloseVoice.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringDeletVideo.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringEditVoice.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringOpenVoice.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringScreenshot.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringTalkback.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringUnlock.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
star_lock/images/main/icon_lockDetail_monitoringvoiceFrist.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
star_lock/images/main/icon_lockDetail_videoLog.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
@ -84,11 +84,17 @@ import 'main/lockDetail/lcokSet/wirelessKeyboard/addWirelessKeyboard/addWireless
|
||||
import 'main/lockDetail/lcokSet/wirelessKeyboard/addWirelessKeyboardScreenNotLightOn/addWirelessKeyboardScreenNotLightOn_page.dart';
|
||||
import 'main/lockDetail/lcokSet/wirelessKeyboard/seletWirelessKeyboard/seletWirelessKeyboard_page.dart';
|
||||
import 'main/lockDetail/lcokSet/wirelessKeyboard/wirelessKeyboardList/wirelessKeyboard_page.dart';
|
||||
import 'main/lockDetail/monitoring/monitoring/lockMonitoring_page.dart';
|
||||
import 'main/lockDetail/monitoring/monitoringRealTimeScreen/monitoringRealTimeScreen_page.dart';
|
||||
import 'main/lockDetail/otherTypeKey/addFingerprint/addFingerprint/addFingerprint_page.dart';
|
||||
import 'main/lockDetail/otherTypeKey/addFingerprint/addFingerprintTip/addFingerprintTip_page.dart';
|
||||
import 'main/lockDetail/otherTypeKey/addICCard/addICCard_page.dart';
|
||||
import 'main/lockDetail/otherTypeKey/otherTypeKeyChangeDate/otherTypeKeyChangeDate_page.dart';
|
||||
import 'main/lockDetail/otherTypeKey/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_page.dart';
|
||||
import 'main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart';
|
||||
import 'main/lockDetail/videoLog/videoLog/videoLog_page.dart';
|
||||
import 'main/lockDetail/videoLog/videoLogDetail/videoLogDetail_page.dart';
|
||||
import 'main/lockDetail/videoLog/videoLogDownLoad/videoLogDownLoad_page.dart';
|
||||
import 'main/lockMian/demoMode/demoModeLockDetail/demoModeLockDetail_page.dart';
|
||||
import 'main/lockMian/demoMode/demoModeLockSet/demoModeLockSet_page.dart';
|
||||
import 'mine/about/about_page.dart';
|
||||
@ -371,6 +377,12 @@ abstract class Routers {
|
||||
static const coerceFingerprintPage = '/coerceFingerprintPage'; //胁迫指纹
|
||||
static const lowBatteryReminderPage = '/lowBatteryReminderPage'; //低电量提醒
|
||||
static const coerceFingerprintListPage = '/coerceFingerprintListPage'; //指纹列表
|
||||
static const lockMonitoringPage = '/LockMonitoringPage'; //监控
|
||||
static const monitoringRealTimeScreenPage = '/MonitoringRealTimeScreenPage'; //实时画面
|
||||
static const videoLogPage = '/VideoLogPage'; // 视频日志
|
||||
static const editVideoLogPage = '/EditVideoLogPage'; // 编辑视频日志
|
||||
static const videoLogDetailPage = '/VideoLogDetailPage'; // 视频日志详情
|
||||
static const videoLogDownLoadPage = '/VideoLogDownLoadPage'; // 视频下载列表
|
||||
}
|
||||
|
||||
abstract class AppRouters {
|
||||
@ -911,6 +923,24 @@ abstract class AppRouters {
|
||||
page: () => const LowBatteryReminderPage()),
|
||||
GetPage(
|
||||
name: Routers.coerceFingerprintListPage,
|
||||
page: () => const CoerceFingerprintListPage())
|
||||
page: () => const CoerceFingerprintListPage()),
|
||||
GetPage(
|
||||
name: Routers.lockMonitoringPage,
|
||||
page: () => const LockMonitoringPage()),
|
||||
GetPage(
|
||||
name: Routers.monitoringRealTimeScreenPage,
|
||||
page: () => const MonitoringRealTimeScreenPage()),
|
||||
GetPage(
|
||||
name: Routers.videoLogPage,
|
||||
page: () => const VideoLogPage()),
|
||||
GetPage(
|
||||
name: Routers.editVideoLogPage,
|
||||
page: () => const EditVideoLogPage()),
|
||||
GetPage(
|
||||
name: Routers.videoLogDetailPage,
|
||||
page: () => const VideoLogDetailPage()),
|
||||
GetPage(
|
||||
name: Routers.videoLogDownLoadPage,
|
||||
page: () => const VideoLogDownLoadPage()),
|
||||
];
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ class BlueManage{
|
||||
} else {
|
||||
_scanDevices.add(device);
|
||||
}
|
||||
|
||||
scanResultCallBack(_scanDevices);
|
||||
}
|
||||
}else{
|
||||
@ -94,6 +95,7 @@ class BlueManage{
|
||||
} else {
|
||||
_scanDevices.add(device);
|
||||
}
|
||||
// print("_scanDevices:$_scanDevices");
|
||||
scanResultCallBack(_scanDevices);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ class SenderConfiguringWifiCommand extends SenderProtocol {
|
||||
String? ssid;
|
||||
String? password;
|
||||
int? numberOfServers;
|
||||
int? listOfServers;
|
||||
List<int>? listOfServers;
|
||||
List<int>? token;
|
||||
int? needAuthor;
|
||||
List<int>? publicKey;
|
||||
@ -76,7 +76,7 @@ class SenderConfiguringWifiCommand extends SenderProtocol {
|
||||
subData.add(numberOfServers!);
|
||||
|
||||
// listOfServers
|
||||
subData.add(listOfServers!);
|
||||
subData.addAll(listOfServers!);
|
||||
|
||||
// token
|
||||
// subData.addAll(token!);
|
||||
|
||||
@ -3,7 +3,6 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:encrypt/encrypt.dart' as ddd;
|
||||
|
||||
List<String> changeIntListToStringList(List<int> list){
|
||||
List<String> strList = [];
|
||||
|
||||
@ -252,6 +252,12 @@ class CommandReciverManager {
|
||||
reply = SenderReferEventRecordTimeReply.parseData(commandType, data);
|
||||
}
|
||||
break;
|
||||
// case 50:
|
||||
// {
|
||||
// // wifi配网
|
||||
// reply = SenderConfiguringWifiReply.parseData(commandType, data);
|
||||
// }
|
||||
// break;
|
||||
case 51:
|
||||
{
|
||||
// wifi配网结果
|
||||
|
||||
@ -516,7 +516,7 @@ class IoSenderManage {
|
||||
required String? ssid,
|
||||
required String? password,
|
||||
required int? numberOfServers,
|
||||
required int? listOfServers,
|
||||
required List<int>? listOfServers,
|
||||
required List<int>? token,
|
||||
required int? needAuthor,
|
||||
required List<int>? publicKey,
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
|
||||
import 'package:network_info_plus/network_info_plus.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'package:star_lock/tools/toast.dart';
|
||||
|
||||
import '../../../../blue/blue_manage.dart';
|
||||
import '../../../../blue/io_protocol/io_configuringWifi.dart';
|
||||
@ -24,6 +25,23 @@ class ConfiguringWifiLogic extends BaseGetXController{
|
||||
var entity = await ApiRepository.to.getWifiLockServiceIpAndPort();
|
||||
if(entity.errorCode! == 0){
|
||||
state.configuringWifiEntity.value = entity;
|
||||
|
||||
// var serversList = <int>[];
|
||||
// for(int i = 0; i<state.configuringWifiEntity.value.data!.serviceList!.length; i++){
|
||||
// var item = state.configuringWifiEntity.value.data!.serviceList![i];
|
||||
// var itemList = item.serviceIp!.split(".");
|
||||
// itemList.forEach((element) {
|
||||
// serversList.add(int.parse(element));
|
||||
// });
|
||||
// // serversList.add(int.parse(item.port!));
|
||||
//
|
||||
// double typeDouble = int.parse(item.port!) / 256;
|
||||
// int type1 = typeDouble.toInt();
|
||||
// int type2 = int.parse(item.port!) % 256;
|
||||
// serversList.add(type1);
|
||||
// serversList.add(type2);
|
||||
// }
|
||||
// print("serversListserversList:$serversList");
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,26 +49,15 @@ class ConfiguringWifiLogic extends BaseGetXController{
|
||||
late StreamSubscription<Reply> _replySubscription;
|
||||
void _initReplySubscription() {
|
||||
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((reply) async {
|
||||
// 配置wifi
|
||||
// WIFI配网结果
|
||||
if(reply is SenderConfiguringWifiReply) {
|
||||
_replySenderConfiguringWifi(reply);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 开门数据解析
|
||||
// WIFI配网结果
|
||||
Future<void> _replySenderConfiguringWifi(Reply reply) async {
|
||||
var privateKey = await Storage.getStringList(saveBluePrivateKey);
|
||||
List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
|
||||
|
||||
var signKey = await Storage.getStringList(saveBlueSignKey);
|
||||
List<int> signKeyDataList = changeStringListToIntList(signKey!);
|
||||
|
||||
var tokenData = reply.data.sublist(2, 6);
|
||||
var saveStrList = changeIntListToStringList(tokenData);
|
||||
print("openDoorToken:$tokenData");
|
||||
Storage.setStringList(saveBlueToken, saveStrList);
|
||||
|
||||
int status = reply.data[6];
|
||||
print("status:$status");
|
||||
|
||||
@ -58,20 +65,48 @@ class ConfiguringWifiLogic extends BaseGetXController{
|
||||
case 0x00:
|
||||
//成功
|
||||
print("${reply.commandType}数据解析成功");
|
||||
|
||||
Toast.show(msg: "配网成功");
|
||||
break;
|
||||
case 0x06:
|
||||
//无权限
|
||||
print("${reply.commandType}需要鉴权");
|
||||
|
||||
IoSenderManage.senderOpenLock(
|
||||
keyID: "1",
|
||||
var privateKey = await Storage.getStringList(saveBluePrivateKey);
|
||||
List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
|
||||
|
||||
var publicKey = await Storage.getStringList(saveBluePublicKey);
|
||||
List<int> publicKeyDataList = changeStringListToIntList(publicKey!);
|
||||
|
||||
var tokenData = reply.data.sublist(7, 10);
|
||||
var saveStrList = changeIntListToStringList(tokenData);
|
||||
print("openDoorToken:$tokenData");
|
||||
Storage.setStringList(saveBlueToken, saveStrList);
|
||||
|
||||
var serversList = <int>[];
|
||||
for(int i = 0; i<state.configuringWifiEntity.value.data!.serviceList!.length; i++){
|
||||
var item = state.configuringWifiEntity.value.data!.serviceList![i];
|
||||
var itemList = item.serviceIp!.split(".");
|
||||
itemList.forEach((element) {
|
||||
serversList.add(int.parse(element));
|
||||
});
|
||||
|
||||
double typeDouble = int.parse(item.port!) / 256;
|
||||
int type1 = typeDouble.toInt();
|
||||
int type2 = int.parse(item.port!) % 256;
|
||||
serversList.add(type1);
|
||||
serversList.add(type2);
|
||||
}
|
||||
|
||||
IoSenderManage.senderConfiguringWifiCommand(
|
||||
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
|
||||
userID: await Storage.getUid(),
|
||||
openMode: 1,
|
||||
openTime: DateTime.now().millisecondsSinceEpoch,
|
||||
ssid: state.wifiNameController.text,
|
||||
password: state.wifiPWDController.text,
|
||||
numberOfServers: state.configuringWifiEntity.value.data!.serviceNum,
|
||||
listOfServers: serversList,
|
||||
token: tokenData,
|
||||
needAuthor: 1,
|
||||
signKey: signKeyDataList,
|
||||
publicKey: publicKeyDataList,
|
||||
privateKey: getPrivateKeyList,
|
||||
);
|
||||
|
||||
@ -109,13 +144,28 @@ class ConfiguringWifiLogic extends BaseGetXController{
|
||||
print("openDoorTokenPubToken:$getTokenList");
|
||||
|
||||
// print("millisecondsSinceEpoch/1000:${DateTime.now().millisecondsSinceEpoch~/1000}");
|
||||
var serversList = <int>[];
|
||||
for(int i = 0; i<state.configuringWifiEntity.value.data!.serviceList!.length; i++){
|
||||
var item = state.configuringWifiEntity.value.data!.serviceList![i];
|
||||
var itemList = item.serviceIp!.split(".");
|
||||
itemList.forEach((element) {
|
||||
serversList.add(int.parse(element));
|
||||
});
|
||||
|
||||
double typeDouble = int.parse(item.port!) / 256;
|
||||
int type1 = typeDouble.toInt();
|
||||
int type2 = int.parse(item.port!) % 256;
|
||||
serversList.add(type1);
|
||||
serversList.add(type2);
|
||||
}
|
||||
|
||||
IoSenderManage.senderConfiguringWifiCommand(
|
||||
keyID: "1",
|
||||
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
|
||||
userID: await Storage.getUid(),
|
||||
ssid: state.wifiNameController.text,
|
||||
password: state.wifiPWDController.text,
|
||||
numberOfServers: state.configuringWifiEntity.value.data!.serviceNum,
|
||||
listOfServers: 0,
|
||||
listOfServers: serversList,
|
||||
token: getTokenList,
|
||||
needAuthor: 1,
|
||||
publicKey: publicKeyDataList,
|
||||
|
||||
@ -36,7 +36,7 @@ class _ConfiguringWifiPageState extends State<ConfiguringWifiPage> {
|
||||
configuringWifiTFWidget(TranslationLoader.lanKeys!.wifiPassward!.tr, TranslationLoader.lanKeys!.pleaseEnterWifiPwd!.tr, state.wifiPWDController),
|
||||
SizedBox(height: 50.h,),
|
||||
SubmitBtn(btnName: TranslationLoader.lanKeys!.sure!.tr, onClick: () {
|
||||
|
||||
logic.senderConfiguringWifiAction();
|
||||
}),
|
||||
],
|
||||
));
|
||||
@ -51,14 +51,14 @@ class _ConfiguringWifiPageState extends State<ConfiguringWifiPage> {
|
||||
leftTitel: titleStr,
|
||||
rightTitle: "",
|
||||
isHaveRightWidget: true,
|
||||
rightWidget: getTFWidget(rightTitle)),
|
||||
rightWidget: getTFWidget(rightTitle, controller)),
|
||||
Container(height: 10.h),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 接受者信息输入框
|
||||
Widget getTFWidget(String tfStr) {
|
||||
Widget getTFWidget(String tfStr, TextEditingController controller) {
|
||||
return Container(
|
||||
height: 50.h,
|
||||
width: 300.w,
|
||||
@ -68,7 +68,7 @@ class _ConfiguringWifiPageState extends State<ConfiguringWifiPage> {
|
||||
child: TextField(
|
||||
//输入框一行
|
||||
maxLines: 1,
|
||||
// controller: _controller,
|
||||
controller: controller,
|
||||
autofocus: false,
|
||||
textAlign: TextAlign.end,
|
||||
decoration: InputDecoration(
|
||||
|
||||
@ -3,15 +3,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../lockSet/lockSetInfo_entity.dart';
|
||||
import 'configuringWifiEntity.dart';
|
||||
|
||||
class ConfiguringWifiState{
|
||||
var configuringWifiEntity = ConfiguringWifiEntity().obs;
|
||||
var lockSetInfoData = LockSetInfoData().obs;
|
||||
var lockBasicInfo = LockBasicInfo().obs;
|
||||
|
||||
TextEditingController wifiNameController = TextEditingController();
|
||||
TextEditingController wifiPWDController = TextEditingController();
|
||||
ConfiguringWifiState() {
|
||||
// wifiNameController.text = emailOrPhone.value;
|
||||
var map = Get.arguments;
|
||||
lockSetInfoData.value = map["lockSetInfoData"];
|
||||
lockBasicInfo.value = lockSetInfoData.value.lockBasicInfo!;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -421,14 +420,20 @@ class _LockSetPageState extends State<LockSetPage> with RouteAware {
|
||||
() => Visibility(
|
||||
visible:
|
||||
state.lockFeature.value.wifi == 1 ? true : false,
|
||||
child: CommonItem(
|
||||
child:
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.wifiDistributionNetwork!.tr,
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
Get.toNamed(Routers.configuringWifiPage);
|
||||
})),
|
||||
Get.toNamed(Routers.configuringWifiPage,
|
||||
arguments: {
|
||||
'lockSetInfoData':
|
||||
state.lockSetInfoData.value
|
||||
});
|
||||
})
|
||||
),
|
||||
),
|
||||
// Obx(() =>
|
||||
// 锁时间
|
||||
|
||||
@ -81,16 +81,16 @@ class _MsgNotificationPageState extends State<MsgNotificationPage> {
|
||||
Get.toNamed(Routers.nDaysUnopenedPage);
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 20.h,
|
||||
),
|
||||
Obx(() => CommonItem(
|
||||
leftTitel: '离家开门',
|
||||
rightTitle: "",
|
||||
isHaveLine: false,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, height: 50.h, child: _switch(1)))),
|
||||
// SizedBox(
|
||||
// height: 20.h,
|
||||
// ),
|
||||
// Obx(() => CommonItem(
|
||||
// leftTitel: '离家开门',
|
||||
// rightTitle: "",
|
||||
// isHaveLine: false,
|
||||
// isHaveRightWidget: true,
|
||||
// rightWidget:
|
||||
// SizedBox(width: 60.w, height: 50.h, child: _switch(1)))),
|
||||
SizedBox(
|
||||
height: 20.h,
|
||||
),
|
||||
|
||||
@ -72,7 +72,10 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
height: 1.sh - ScreenUtil().statusBarHeight * 2,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [topWidget(), Expanded(child: bottomWidget())],
|
||||
children: [
|
||||
topWidget(),
|
||||
Expanded(child: bottomWidget())
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -114,7 +117,7 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
SizedBox(
|
||||
width: 1.sw - 120.w * 2,
|
||||
child: Center(
|
||||
child: Text(
|
||||
@ -124,22 +127,15 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
))),
|
||||
Positioned(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"100%",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp, color: AppColors.darkGrayTextColor),
|
||||
),
|
||||
SizedBox(width: 2.w),
|
||||
Image.asset(
|
||||
'images/main/icon_main_cell.png',
|
||||
width: 30.w,
|
||||
height: 24.w,
|
||||
),
|
||||
SizedBox(width: 30.w),
|
||||
],
|
||||
))
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text("100%", style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor)),
|
||||
SizedBox(width: 2.w),
|
||||
Image.asset('images/main/icon_main_cell.png', width: 30.w, height: 24.w),
|
||||
SizedBox(width: 30.w),
|
||||
],
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30.h),
|
||||
@ -383,7 +379,10 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
bottomItem('images/main/icon_catEyes.png', '监控', () {
|
||||
// Navigator.pushNamed(context, Routers.otherTypeKeyListPage,
|
||||
// arguments: 1);
|
||||
Toast.show(msg: "功能暂未开放");
|
||||
// Toast.show(msg: "功能暂未开放");
|
||||
Get.toNamed(Routers.lockMonitoringPage, arguments: {
|
||||
"lockId": widget.lockListInfoItemEntity.lockId
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -402,11 +401,12 @@ class _LockDetailPageState extends State<LockDetailPage>
|
||||
arguments: {"keyInfo": widget.lockListInfoItemEntity});
|
||||
}),
|
||||
// 视频日志
|
||||
bottomItem('images/main/icon_main_set.png', "视频日志", () {
|
||||
bottomItem('images/main/icon_lockDetail_videoLog.png', "视频日志", () {
|
||||
//视频日志
|
||||
Get.toNamed(Routers.videoLogPage);
|
||||
}),
|
||||
// 消息提醒
|
||||
bottomItem('images/main/icon_main_set.png', "消息提醒", () {
|
||||
bottomItem('images/main/icon_lockDetail_messageReminding.png', "消息提醒", () {
|
||||
Get.toNamed(Routers.msgNotificationPage);
|
||||
}),
|
||||
// 设置
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
|
||||
import '../../../../tools/baseGetXController.dart';
|
||||
import 'lockMonitoring_state.dart';
|
||||
|
||||
class LockMonitoringLogic extends BaseGetXController {
|
||||
final LockMonitoringState state = LockMonitoringState();
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../appRouters.dart';
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import 'lockMonitoring_logic.dart';
|
||||
|
||||
|
||||
class LockMonitoringPage extends StatefulWidget {
|
||||
const LockMonitoringPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LockMonitoringPage> createState() => _LockMonitoringPageState();
|
||||
}
|
||||
|
||||
class _LockMonitoringPageState extends State<LockMonitoringPage> {
|
||||
final logic = Get.put(LockMonitoringLogic());
|
||||
final state = Get.find<LockMonitoringLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
Stack(
|
||||
// alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Image.asset("images/icon_test20231113.png", width: 1.sw, height: 1.sh, fit: BoxFit.cover),
|
||||
Positioned(
|
||||
top: ScreenUtil().statusBarHeight + 30.h,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 30.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(30.h)
|
||||
),
|
||||
padding: EdgeInsets.all(10.w),
|
||||
child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/icon_left_black.png"),),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 10.w,
|
||||
child: Container(
|
||||
width: 1.sw - 30.w*2,
|
||||
// height: 300.h,
|
||||
margin: EdgeInsets.all(30.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xC83C3F41),
|
||||
borderRadius: BorderRadius.circular(20.h)
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 20.h),
|
||||
bottomTopBtnWidget(),
|
||||
SizedBox(height: 20.h),
|
||||
bottomBottomBtnWidget(),
|
||||
SizedBox(height: 20.h),
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget bottomTopBtnWidget(){
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// 打开关闭声音
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
state.isOpenVoice.value = !state.isOpenVoice.value;
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Obx(() => Image(
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
image: state.isOpenVoice.value
|
||||
? const AssetImage("images/main/icon_lockDetail_monitoringCloseVoice.png")
|
||||
: const AssetImage("images/main/icon_lockDetail_monitoringOpenVoice.png")
|
||||
)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 60.w),
|
||||
// 截图
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenshot.png")),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 60.w),
|
||||
// 录制
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenRecording.png")),
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget bottomBottomBtnWidget(){
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringTalkback.png", "点击对讲", Colors.white,(){
|
||||
|
||||
}),
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringUnlock.png", "长按开锁", AppColors.mainColor,(){
|
||||
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor, Function() onClick) {
|
||||
var wh = 80.w;
|
||||
return GestureDetector(
|
||||
onTap: onClick,
|
||||
child: SizedBox(
|
||||
height: 140.h,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: wh,
|
||||
height: wh,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular((wh+ 10.w*2)/2)
|
||||
),
|
||||
padding: EdgeInsets.all(20.w),
|
||||
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
|
||||
),
|
||||
SizedBox(height: 20.w),
|
||||
Expanded(child: Text(name, style: TextStyle(fontSize: 20.sp, color: Colors.white), textAlign: TextAlign.center))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class LockMonitoringState {
|
||||
var isOpenVoice = false.obs;
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'monitoringRealTimeScreen_state.dart';
|
||||
|
||||
class MonitoringRealTimeScreenLogic extends BaseGetXController{
|
||||
MonitoringRealTimeScreenState state = MonitoringRealTimeScreenState();
|
||||
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import 'monitoringRealTimeScreen_logic.dart';
|
||||
|
||||
class MonitoringRealTimeScreenPage extends StatefulWidget {
|
||||
const MonitoringRealTimeScreenPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MonitoringRealTimeScreenPage> createState() => _MonitoringRealTimeScreenPageState();
|
||||
}
|
||||
|
||||
class _MonitoringRealTimeScreenPageState extends State<MonitoringRealTimeScreenPage> {
|
||||
final logic = Get.put(MonitoringRealTimeScreenLogic());
|
||||
final state = Get.find<MonitoringRealTimeScreenLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.mainBackgroundColor,
|
||||
appBar: TitleAppBar(
|
||||
barTitle: "实时播放",
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor,
|
||||
),
|
||||
body: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.asset("images/icon_test20231113.png", width: 1.sw, height: 1.sw/5*4, fit: BoxFit.cover),
|
||||
middleWidget(),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: 60.h,
|
||||
child: bottomBtnWidget()
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget middleWidget(){
|
||||
return Container(
|
||||
color: Colors.grey,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
// 打开关闭声音
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
state.isOpenVoice.value = !state.isOpenVoice.value;
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Obx(() => Image(
|
||||
width: 40.w,
|
||||
height: 40.w,
|
||||
image: state.isOpenVoice.value
|
||||
? const AssetImage("images/main/icon_lockDetail_monitoringCloseVoice.png")
|
||||
: const AssetImage("images/main/icon_lockDetail_monitoringOpenVoice.png")
|
||||
)),
|
||||
),
|
||||
),
|
||||
// SizedBox(width: 60.w),
|
||||
// 截图
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenshot.png")),
|
||||
),
|
||||
),
|
||||
// SizedBox(width: 60.w),
|
||||
// 录制
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
|
||||
},
|
||||
child: Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(5.w),
|
||||
child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringScreenRecording.png")),
|
||||
),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget bottomBtnWidget(){
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringTalkback.png", "点击对讲", Colors.white, (){
|
||||
|
||||
}),
|
||||
SizedBox(width: (1.sw - 80.w)/3),
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringUnlock.png", "长按开锁", AppColors.mainColor, (){
|
||||
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor, Function() onClick) {
|
||||
var wh = 80.w;
|
||||
return GestureDetector(
|
||||
onTap: onClick,
|
||||
child: SizedBox(
|
||||
height: 140.h,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: wh,
|
||||
height: wh,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular((wh+ 10.w*2)/2)
|
||||
),
|
||||
padding: EdgeInsets.all(20.w),
|
||||
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
|
||||
),
|
||||
SizedBox(height: 20.w),
|
||||
Expanded(child: Text(name, style: TextStyle(fontSize: 20.sp, color: Colors.black), textAlign: TextAlign.center))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MonitoringRealTimeScreenState{
|
||||
var isOpenVoice = false.obs;
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => afterFirstLayout(context));
|
||||
}
|
||||
|
||||
void afterFirstLayout(BuildContext context);
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'video_player_control.dart';
|
||||
|
||||
class ControllerWidget extends InheritedWidget {
|
||||
final String title;
|
||||
final GlobalKey<VideoPlayerControlState> controlKey;
|
||||
final Widget child;
|
||||
final VideoPlayerController controller;
|
||||
final bool videoInit;
|
||||
|
||||
ControllerWidget(
|
||||
{Key? key,
|
||||
required this.controlKey,
|
||||
required this.child,
|
||||
required this.controller,
|
||||
required this.videoInit,
|
||||
required this.title
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
//定义一个便捷方法,方便子树中的widget获取共享数据
|
||||
static ControllerWidget? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<ControllerWidget>();
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(InheritedWidget oldWidget) {
|
||||
// TODO: implement updateShouldNotify
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:screen/screen.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'controller_widget.dart';
|
||||
import 'video_player_control.dart';
|
||||
import 'video_player_pan.dart';
|
||||
|
||||
enum VideoPlayerType { network, asset, file }
|
||||
|
||||
class VideoPlayerUI extends StatefulWidget {
|
||||
VideoPlayerUI.network({
|
||||
Key? key,
|
||||
required String this.url, // 当前需要播放的地址
|
||||
required this.width, // 播放器尺寸(大于等于视频播放区域)
|
||||
required this.height,
|
||||
this.title = '', // 视频需要显示的标题
|
||||
}) : type = VideoPlayerType.network,
|
||||
super(key: key);
|
||||
|
||||
VideoPlayerUI.asset({
|
||||
Key? key,
|
||||
required String dataSource, // 当前需要播放的地址
|
||||
this.width = double.infinity, // 播放器尺寸(大于等于视频播放区域)
|
||||
this.height = double.infinity,
|
||||
this.title = '', // 视频需要显示的标题
|
||||
}) : type = VideoPlayerType.asset,
|
||||
url = dataSource,
|
||||
super(key: key);
|
||||
|
||||
const VideoPlayerUI.file({
|
||||
Key? key,
|
||||
required File file, // 当前需要播放的地址
|
||||
this.width = double.infinity, // 播放器尺寸(大于等于视频播放区域)
|
||||
this.height = double.infinity,
|
||||
this.title = '', // 视频需要显示的标题
|
||||
}) : type = VideoPlayerType.file,
|
||||
url = file,
|
||||
super(key: key);
|
||||
|
||||
final url;
|
||||
final VideoPlayerType type;
|
||||
final double width;
|
||||
final double height;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
_VideoPlayerUIState createState() => _VideoPlayerUIState();
|
||||
}
|
||||
|
||||
class _VideoPlayerUIState extends State<VideoPlayerUI> {
|
||||
final GlobalKey<VideoPlayerControlState> _key =
|
||||
GlobalKey<VideoPlayerControlState>();
|
||||
|
||||
///指示video资源是否加载完成,加载完成后会获得总时长和视频长宽比等信息
|
||||
bool _videoInit = false;
|
||||
bool _videoError = false;
|
||||
|
||||
late VideoPlayerController _controller; // video控件管理器
|
||||
|
||||
/// 记录是否全屏
|
||||
bool get _isFullScreen =>
|
||||
MediaQuery.of(context).orientation == Orientation.landscape;
|
||||
|
||||
Size get _window => MediaQueryData.fromWindow(window).size;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_urlChange(); // 初始进行一次url加载
|
||||
// Screen.keepOn(true); // 设置屏幕常亮
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(VideoPlayerUI oldWidget) {
|
||||
if (oldWidget.url != widget.url) {
|
||||
_urlChange(); // url变化时重新执行一次url加载
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() async {
|
||||
super.dispose();
|
||||
if (_controller != null) {
|
||||
_controller.removeListener(_videoListener);
|
||||
_controller.dispose();
|
||||
}
|
||||
// Screen.keepOn(false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
top: !_isFullScreen,
|
||||
bottom: !_isFullScreen,
|
||||
left: !_isFullScreen,
|
||||
right: !_isFullScreen,
|
||||
child: Container(
|
||||
width: _isFullScreen ? _window.width : widget.width,
|
||||
height: _isFullScreen ? _window.height : widget.height,
|
||||
child: _isHadUrl(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 判断是否有url
|
||||
Widget _isHadUrl() {
|
||||
if (widget.url != null) {
|
||||
return ControllerWidget(
|
||||
controlKey: _key,
|
||||
controller: _controller,
|
||||
videoInit: _videoInit,
|
||||
title: widget.title,
|
||||
child: VideoPlayerPan(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: Colors.black,
|
||||
child: _isVideoInit(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(
|
||||
'暂无视频信息',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载url成功时,根据视频比例渲染播放器
|
||||
Widget _isVideoInit() {
|
||||
if (_videoInit) {
|
||||
return AspectRatio(
|
||||
aspectRatio: _controller.value.aspectRatio,
|
||||
child: VideoPlayer(_controller),
|
||||
);
|
||||
} else if (_controller != null && _videoError) {
|
||||
return Text(
|
||||
'加载出错',
|
||||
style: TextStyle(color: Colors.white),
|
||||
);
|
||||
} else {
|
||||
return SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _urlChange() async {
|
||||
if (widget.url == null || widget.url == '') return;
|
||||
if (_controller != null) {
|
||||
/// 如果控制器存在,清理掉重新创建
|
||||
_controller.removeListener(_videoListener);
|
||||
_controller.dispose();
|
||||
}
|
||||
setState(() {
|
||||
/// 重置组件参数
|
||||
_videoInit = false;
|
||||
_videoError = false;
|
||||
});
|
||||
if (widget.type == VideoPlayerType.file) {
|
||||
_controller = VideoPlayerController.file(widget.url);
|
||||
} else if (widget.type == VideoPlayerType.asset) {
|
||||
_controller = VideoPlayerController.asset(widget.url);
|
||||
} else {
|
||||
_controller = VideoPlayerController.network(widget.url);
|
||||
}
|
||||
|
||||
/// 加载资源完成时,监听播放进度,并且标记_videoInit=true加载完成
|
||||
_controller.addListener(_videoListener);
|
||||
await _controller.initialize();
|
||||
setState(() {
|
||||
_videoInit = true;
|
||||
_videoError = false;
|
||||
_controller.play();
|
||||
});
|
||||
}
|
||||
|
||||
void _videoListener() async {
|
||||
if (_controller.value.hasError) {
|
||||
setState(() {
|
||||
_videoError = true;
|
||||
});
|
||||
} else {
|
||||
Duration? res = await _controller.position;
|
||||
if (res! >= _controller.value.duration) {
|
||||
await _controller.seekTo(Duration(seconds: 0));
|
||||
await _controller.pause();
|
||||
}
|
||||
if (_controller.value.isPlaying && _key.currentState != null) {
|
||||
/// 减少build次数
|
||||
_key.currentState!.setPosition(
|
||||
position: res,
|
||||
totalDuration: _controller.value.duration,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,273 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:auto_orientation/auto_orientation.dart';
|
||||
import 'package:common_utils/common_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'controller_widget.dart';
|
||||
import 'video_player_slider.dart';
|
||||
|
||||
class VideoPlayerControl extends StatefulWidget {
|
||||
VideoPlayerControl({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
VideoPlayerControlState createState() => VideoPlayerControlState();
|
||||
}
|
||||
|
||||
class VideoPlayerControlState extends State<VideoPlayerControl> {
|
||||
VideoPlayerController get controller => ControllerWidget.of(context)!.controller;
|
||||
bool get videoInit => ControllerWidget.of(context)!.videoInit;
|
||||
String get title=>ControllerWidget.of(context)!.title;
|
||||
// 记录video播放进度
|
||||
Duration _position = Duration(seconds: 0);
|
||||
Duration _totalDuration = Duration(seconds: 0);
|
||||
late Timer _timer; // 计时器,用于延迟隐藏控件ui
|
||||
bool _hidePlayControl = true; // 控制是否隐藏控件ui
|
||||
double _playControlOpacity = 0; // 通过透明度动画显示/隐藏控件ui
|
||||
/// 记录是否全屏
|
||||
bool get _isFullScreen =>
|
||||
MediaQuery.of(context).orientation == Orientation.landscape;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (_timer != null) {
|
||||
_timer.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onDoubleTap: _playOrPause,
|
||||
onTap: _togglePlayControl,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: Colors.transparent,
|
||||
child: WillPopScope(
|
||||
child: Offstage(
|
||||
offstage: _hidePlayControl,
|
||||
child: AnimatedOpacity(
|
||||
// 加入透明度动画
|
||||
opacity: _playControlOpacity,
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: Column(
|
||||
children: <Widget>[_top(), _middle(), _bottom(context)],
|
||||
),
|
||||
),
|
||||
),
|
||||
onWillPop: _onWillPop,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 拦截返回键
|
||||
Future<bool> _onWillPop() async {
|
||||
if (_isFullScreen) {
|
||||
_toggleFullScreen();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 供父组件调用刷新页面,减少父组件的build
|
||||
void setPosition({position, totalDuration}) {
|
||||
setState(() {
|
||||
_position = position;
|
||||
_totalDuration = totalDuration;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _bottom(BuildContext context) {
|
||||
return Container(
|
||||
// 底部控件的容器
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
// 来点黑色到透明的渐变优雅一下
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: [Color.fromRGBO(0, 0, 0, .7), Color.fromRGBO(0, 0, 0, .1)],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
// 加载完成时才渲染,flex布局
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
// 播放按钮
|
||||
padding: EdgeInsets.zero,
|
||||
iconSize: 26,
|
||||
icon: Icon(
|
||||
// 根据控制器动态变化播放图标还是暂停
|
||||
controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: _playOrPause,
|
||||
),
|
||||
Expanded(
|
||||
// 相当于前端的flex: 1
|
||||
child: VideoPlayerSlider(
|
||||
startPlayControlTimer: _startPlayControlTimer,
|
||||
timer: _timer,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
// 播放时间
|
||||
margin: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
'${DateUtil.formatDateMs(
|
||||
_position!.inMilliseconds,
|
||||
format: 'mm:ss',
|
||||
)}/${DateUtil.formatDateMs(
|
||||
_totalDuration!.inMilliseconds,
|
||||
format: 'mm:ss',
|
||||
)}',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
// 全屏/横屏按钮
|
||||
padding: EdgeInsets.zero,
|
||||
iconSize: 26,
|
||||
icon: Icon(
|
||||
// 根据当前屏幕方向切换图标
|
||||
_isFullScreen ? Icons.fullscreen_exit : Icons.fullscreen,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
// 点击切换是否全屏
|
||||
_toggleFullScreen();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _middle() {
|
||||
return Expanded(
|
||||
child: Container(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _top() {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
// 来点黑色到透明的渐变优雅一下
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
colors: [Color.fromRGBO(0, 0, 0, .7), Color.fromRGBO(0, 0, 0, .1)],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
//在最上层或者不是横屏则隐藏按钮
|
||||
ModalRoute.of(context)!.isFirst && !_isFullScreen
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: backPress),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
//在最上层或者不是横屏则隐藏按钮
|
||||
ModalRoute.of(context)!.isFirst && !_isFullScreen
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void backPress() {
|
||||
print(_isFullScreen);
|
||||
// 如果是全屏,点击返回键则关闭全屏,如果不是,则系统返回键
|
||||
if (_isFullScreen) {
|
||||
_toggleFullScreen();
|
||||
} else if(ModalRoute.of(context)!.isFirst) {
|
||||
SystemNavigator.pop();
|
||||
}else{
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _playOrPause() {
|
||||
/// 同样的,点击动态播放或者暂停
|
||||
if (videoInit) {
|
||||
controller.value.isPlaying ? controller.pause() : controller.play();
|
||||
_startPlayControlTimer(); // 操作控件后,重置延迟隐藏控件的timer
|
||||
}
|
||||
}
|
||||
|
||||
void _togglePlayControl() {
|
||||
setState(() {
|
||||
if (_hidePlayControl) {
|
||||
/// 如果隐藏则显示
|
||||
_hidePlayControl = false;
|
||||
_playControlOpacity = 1;
|
||||
_startPlayControlTimer(); // 开始计时器,计时后隐藏
|
||||
} else {
|
||||
/// 如果显示就隐藏
|
||||
if (_timer != null) _timer.cancel(); // 有计时器先移除计时器
|
||||
_playControlOpacity = 0;
|
||||
Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
|
||||
_hidePlayControl = true; // 延迟500ms(透明度动画结束)后,隐藏
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _startPlayControlTimer() {
|
||||
/// 计时器,用法和前端js的大同小异
|
||||
if (_timer != null) _timer.cancel();
|
||||
_timer = Timer(Duration(seconds: 3), () {
|
||||
/// 延迟3s后隐藏
|
||||
setState(() {
|
||||
_playControlOpacity = 0;
|
||||
Future.delayed(Duration(milliseconds: 500)).whenComplete(() {
|
||||
_hidePlayControl = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleFullScreen() {
|
||||
setState(() {
|
||||
if (_isFullScreen) {
|
||||
/// 如果是全屏就切换竖屏
|
||||
AutoOrientation.portraitAutoMode();
|
||||
|
||||
///显示状态栏,与底部虚拟操作按钮
|
||||
SystemChrome.setEnabledSystemUIMode(
|
||||
SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]);
|
||||
} else {
|
||||
AutoOrientation.landscapeAutoMode();
|
||||
|
||||
///关闭状态栏,与底部虚拟操作按钮
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||
}
|
||||
_startPlayControlTimer(); // 操作完控件开始计时隐藏
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,246 @@
|
||||
import 'package:common_utils/common_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
//import 'package:screen/screen.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../after_layout.dart';
|
||||
import 'controller_widget.dart';
|
||||
import 'video_player_control.dart';
|
||||
|
||||
class VideoPlayerPan extends StatefulWidget {
|
||||
VideoPlayerPan({
|
||||
// this.controlKey,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
// final GlobalKey<VideoPlayerControlState> controlKey;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
_VideoPlayerPanState createState() => _VideoPlayerPanState();
|
||||
}
|
||||
|
||||
class _VideoPlayerPanState extends State<VideoPlayerPan>
|
||||
with AfterLayoutMixin<VideoPlayerPan> {
|
||||
late Offset startPosition; // 起始位置
|
||||
late double movePan; // 偏移量累计总和
|
||||
late double layoutWidth; // 组件宽度
|
||||
late double layoutHeight; // 组件高度
|
||||
String volumePercentage = ''; // 组件位移描述
|
||||
double playDialogOpacity = 0.0;
|
||||
bool allowHorizontal = false; // 是否允许快进
|
||||
Duration position = Duration(seconds: 0); // 当前时间
|
||||
double brightness = 0.0; //亮度
|
||||
bool brightnessOk = false; // 是否允许调节亮度
|
||||
|
||||
VideoPlayerController get controller => ControllerWidget.of(context)!.controller;
|
||||
bool get videoInit => ControllerWidget.of(context)!.videoInit;
|
||||
String get title=>ControllerWidget.of(context)!.title;
|
||||
|
||||
@override
|
||||
void afterFirstLayout(BuildContext context) {
|
||||
_reset(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
brightnessOk = false;
|
||||
allowHorizontal = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onVerticalDragStart: _onVerticalDragStart,
|
||||
onVerticalDragUpdate: _onVerticalDragUpdate,
|
||||
onVerticalDragEnd: _onVerticalDragEnd,
|
||||
onHorizontalDragStart: _onHorizontalDragStart,
|
||||
onHorizontalDragUpdate: _onHorizontalDragUpdate,
|
||||
onHorizontalDragEnd: _onHorizontalDragEnd,
|
||||
child: Container(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
widget.child,
|
||||
Center(
|
||||
child: AnimatedOpacity(
|
||||
opacity: playDialogOpacity,
|
||||
duration: Duration(milliseconds: 500),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 6.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black87,
|
||||
borderRadius: BorderRadius.all(Radius.circular(5.0))),
|
||||
child: Text(
|
||||
volumePercentage,
|
||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
VideoPlayerControl(
|
||||
key: ControllerWidget.of(context)!.controlKey,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onVerticalDragStart(details) async {
|
||||
_reset(context);
|
||||
startPosition = details.globalPosition;
|
||||
if (startPosition.dx < (layoutWidth / 2)) {
|
||||
/// 左边触摸
|
||||
// brightness = await Screen.brightness;
|
||||
brightnessOk = true;
|
||||
}
|
||||
}
|
||||
|
||||
void _onVerticalDragUpdate(details) {
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// 累计计算偏移量(下滑减少百分比,上滑增加百分比)
|
||||
movePan += (-details.delta.dy);
|
||||
if (startPosition.dx < (layoutWidth / 2)) {
|
||||
/// 左边触摸
|
||||
if (brightnessOk = true) {
|
||||
setState(() {
|
||||
volumePercentage = '亮度:${(_setBrightnessValue() * 100).toInt()}%';
|
||||
playDialogOpacity = 1.0;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
/// 右边触摸
|
||||
setState(() {
|
||||
volumePercentage = '音量:${(_setVerticalValue(num: 2) * 100).toInt()}%';
|
||||
playDialogOpacity = 1.0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onVerticalDragEnd(_) async {
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
if (startPosition.dx < (layoutWidth / 2)) {
|
||||
if (brightnessOk) {
|
||||
// await Screen.setBrightness(_setBrightnessValue());
|
||||
brightnessOk = false;
|
||||
// 左边触摸
|
||||
setState(() {
|
||||
playDialogOpacity = 0.0;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 右边触摸
|
||||
await controller.setVolume(_setVerticalValue());
|
||||
setState(() {
|
||||
playDialogOpacity = 0.0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
double _setBrightnessValue() {
|
||||
// 亮度百分控制
|
||||
double value =
|
||||
double.parse((movePan / layoutHeight + brightness).toStringAsFixed(2));
|
||||
if (value >= 1.00) {
|
||||
value = 1.00;
|
||||
} else if (value <= 0.00) {
|
||||
value = 0.00;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
double _setVerticalValue({int num = 1}) {
|
||||
// 声音亮度百分控制
|
||||
double value = double.parse(
|
||||
(movePan / layoutHeight + controller.value.volume)
|
||||
.toStringAsFixed(num));
|
||||
if (value >= 1.0) {
|
||||
value = 1.0;
|
||||
} else if (value <= 0.0) {
|
||||
value = 0.0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void _reset(BuildContext context) {
|
||||
startPosition = Offset(0, 0);
|
||||
movePan = 0;
|
||||
layoutHeight = context.size!.height;
|
||||
layoutWidth = context.size!.width;
|
||||
volumePercentage = '';
|
||||
}
|
||||
|
||||
void _onHorizontalDragStart(DragStartDetails details) async {
|
||||
_reset(context);
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
// 获取当前时间
|
||||
position = controller.value.position;
|
||||
// 暂停成功后才允许快进手势
|
||||
allowHorizontal = true;
|
||||
}
|
||||
|
||||
void _onHorizontalDragUpdate(DragUpdateDetails details) {
|
||||
if (!videoInit && !allowHorizontal) {
|
||||
return;
|
||||
}
|
||||
// 累计计算偏移量
|
||||
movePan += details.delta.dx;
|
||||
double value = _setHorizontalValue();
|
||||
// 用百分比计算出当前的秒数
|
||||
String currentSecond = DateUtil.formatDateMs(
|
||||
(value * controller.value.duration.inMilliseconds).toInt(),
|
||||
format: 'mm:ss',
|
||||
);
|
||||
if (value >= 0) {
|
||||
setState(() {
|
||||
volumePercentage = '快进至:$currentSecond';
|
||||
playDialogOpacity = 1.0;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
volumePercentage = '快退至:${(value * 100).toInt()}%';
|
||||
playDialogOpacity = 1.0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onHorizontalDragEnd(DragEndDetails details) async {
|
||||
if (!videoInit && !allowHorizontal) {
|
||||
return;
|
||||
}
|
||||
double value = _setHorizontalValue();
|
||||
int current =
|
||||
(value * controller.value.duration.inMilliseconds).toInt();
|
||||
await controller.seekTo(Duration(milliseconds: current));
|
||||
allowHorizontal = false;
|
||||
setState(() {
|
||||
playDialogOpacity = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
double _setHorizontalValue() {
|
||||
// 进度条百分控制
|
||||
double valueHorizontal =
|
||||
double.parse((movePan / layoutWidth).toStringAsFixed(2));
|
||||
// 当前进度条百分比
|
||||
double currentValue = position.inMilliseconds /
|
||||
controller.value.duration.inMilliseconds;
|
||||
double value =
|
||||
double.parse((currentValue + valueHorizontal).toStringAsFixed(2));
|
||||
if (value >= 1.00) {
|
||||
value = 1.00;
|
||||
} else if (value <= 0.00) {
|
||||
value = 0.00;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:common_utils/common_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import 'controller_widget.dart';
|
||||
|
||||
class VideoPlayerSlider extends StatefulWidget {
|
||||
final Function startPlayControlTimer;
|
||||
final Timer timer;
|
||||
|
||||
VideoPlayerSlider({required this.startPlayControlTimer, required this.timer});
|
||||
|
||||
@override
|
||||
_VideoPlayerSliderState createState() => _VideoPlayerSliderState();
|
||||
}
|
||||
|
||||
class _VideoPlayerSliderState extends State<VideoPlayerSlider> {
|
||||
VideoPlayerController get controller =>
|
||||
ControllerWidget.of(context)!.controller;
|
||||
|
||||
bool get videoInit => ControllerWidget.of(context)!.videoInit;
|
||||
late double progressValue; //进度
|
||||
late String labelProgress; //tip内容
|
||||
bool handle = false; //判断是否在滑动的标识
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
progressValue = 0.0;
|
||||
labelProgress = '00:00';
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(VideoPlayerSlider oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (!handle && videoInit) {
|
||||
int position = controller.value.position.inMilliseconds;
|
||||
int duration = controller.value.duration.inMilliseconds;
|
||||
if(position>=duration){
|
||||
position=duration;
|
||||
}
|
||||
setState(() {
|
||||
progressValue = position / duration * 100;
|
||||
labelProgress = DateUtil.formatDateMs(
|
||||
progressValue.toInt(),
|
||||
format: 'mm:ss',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliderTheme(
|
||||
//自定义风格
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
//进度条滑块左边颜色
|
||||
inactiveTrackColor: Colors.white,
|
||||
overlayShape: RoundSliderOverlayShape(
|
||||
//可继承SliderComponentShape自定义形状
|
||||
overlayRadius: 10, //滑块外圈大小
|
||||
),
|
||||
thumbShape: RoundSliderThumbShape(
|
||||
//可继承SliderComponentShape自定义形状
|
||||
disabledThumbRadius: 7, //禁用是滑块大小
|
||||
enabledThumbRadius: 7, //滑块大小
|
||||
),
|
||||
),
|
||||
child: Slider(
|
||||
value: progressValue,
|
||||
label: labelProgress,
|
||||
divisions: 100,
|
||||
onChangeStart: _onChangeStart,
|
||||
onChangeEnd: _onChangeEnd,
|
||||
onChanged: _onChanged,
|
||||
min: 0,
|
||||
max: 100,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onChangeEnd(_) {
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
widget.startPlayControlTimer();
|
||||
// 关闭手动操作标识
|
||||
handle = false;
|
||||
// 跳转到滑动时间
|
||||
int duration = controller.value.duration.inMilliseconds;
|
||||
controller.seekTo(
|
||||
Duration(milliseconds: (progressValue / 100 * duration).toInt()),
|
||||
);
|
||||
// if (!controller.value.isPlaying) {
|
||||
// controller.play();
|
||||
// }
|
||||
}
|
||||
|
||||
void _onChangeStart(_) {
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
if (widget.timer != null) {
|
||||
widget.timer.cancel();
|
||||
}
|
||||
// 开始手动操作标识
|
||||
handle = true;
|
||||
// if (controller.value.isPlaying) {
|
||||
// controller.pause();
|
||||
// }
|
||||
}
|
||||
|
||||
void _onChanged(double value) {
|
||||
if (!videoInit) {
|
||||
return;
|
||||
}
|
||||
if (widget.timer != null) {
|
||||
widget.timer.cancel();
|
||||
}
|
||||
int duration = controller.value.duration.inMilliseconds;
|
||||
setState(() {
|
||||
progressValue = value;
|
||||
labelProgress = DateUtil.formatDateMs(
|
||||
(value / 100 * duration).toInt(),
|
||||
format: 'mm:ss',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'editVideoLog_state.dart';
|
||||
|
||||
class EditVideoLogLogic extends BaseGetXController{
|
||||
EditVideoLogState state = EditVideoLogState();
|
||||
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import 'editVideoLog_logic.dart';
|
||||
|
||||
class EditVideoLogPage extends StatefulWidget {
|
||||
const EditVideoLogPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<EditVideoLogPage> createState() => _EditVideoLogPageState();
|
||||
}
|
||||
|
||||
class _EditVideoLogPageState extends State<EditVideoLogPage> {
|
||||
final logic = Get.put(EditVideoLogLogic());
|
||||
final state = Get.find<EditVideoLogLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: TitleAppBar(
|
||||
barTitle: "已选${state.seletVideoLog.value}项",
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor,
|
||||
actionsList: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
"全选",
|
||||
style: TextStyle(color: Colors.white, fontSize: 24.sp),
|
||||
),
|
||||
onPressed: () async {
|
||||
state.isSeletAll.value = !state.isSeletAll.value;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: 5,
|
||||
itemBuilder: (c, index) {
|
||||
return Column(children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("2023.10.23", style: TextStyle(fontSize: 20.sp)),
|
||||
]
|
||||
)),
|
||||
mainListView(index)
|
||||
],);
|
||||
}),
|
||||
),
|
||||
bottomBottomBtnWidget()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
var itemW = (1.sw - 15.w*4)/3;
|
||||
var itemH = (1.sw - 15.w*4)/3+40.h;
|
||||
Widget mainListView(int index){
|
||||
return Container(
|
||||
// margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 40.h),
|
||||
// color: Colors.blue,
|
||||
child: GridView.builder(
|
||||
padding: EdgeInsets.only(left: 15.w, right: 15.w),
|
||||
itemCount: index+1,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
//横轴元素个数
|
||||
crossAxisCount: 3,
|
||||
//纵轴间距
|
||||
mainAxisSpacing: 10.w,
|
||||
// 横轴间距
|
||||
crossAxisSpacing: 15.w,
|
||||
//子组件宽高长度比例
|
||||
childAspectRatio: itemW/itemH
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return Obx(() => videoItem());
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoItem(){
|
||||
return SizedBox(
|
||||
width: itemW,
|
||||
height: itemH,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10.w),
|
||||
child: Image(width: itemW, height: itemW, fit: BoxFit.fill, image: const AssetImage("images/main/icon_lockDetail_monitoringvoiceFrist.png")),
|
||||
),
|
||||
SizedBox(height:5.h),
|
||||
Text("2023.10.23 10:00", style: TextStyle(fontSize: 20.sp))
|
||||
],
|
||||
),
|
||||
Visibility(
|
||||
visible: true,
|
||||
child: Positioned(
|
||||
top: 0.w,
|
||||
right: 0.w,
|
||||
child: GestureDetector(
|
||||
onTap: (){
|
||||
|
||||
},
|
||||
child: Image(width: 40.w, height: 40.w, image: state.isSeletAll.value ? const AssetImage("images/icon_round_selet.png") : const AssetImage("images/icon_round_unSelet.png"))
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget bottomBottomBtnWidget(){
|
||||
return Container(
|
||||
width: 1.sw,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringDownloadVideo.png", "下载", Colors.white,(){
|
||||
|
||||
}),
|
||||
SizedBox(width:100.w),
|
||||
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringDeletVideo.png", "删除", AppColors.mainColor,(){
|
||||
|
||||
})
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget bottomBtnItemWidget(String iconUrl, String name, Color backgroundColor, Function() onClick) {
|
||||
var wh = 40.w;
|
||||
return GestureDetector(
|
||||
onTap: onClick,
|
||||
child: Container(
|
||||
height: 140.h,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 30.w),
|
||||
Image.asset(iconUrl, width: wh, height: wh, fit: BoxFit.fitWidth),
|
||||
SizedBox(height: 10.w),
|
||||
Expanded(child: Text(name, style: TextStyle(fontSize: 22.sp), textAlign: TextAlign.center))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class EditVideoLogState{
|
||||
|
||||
var seletVideoLog = 0.obs;
|
||||
var isSeletAll = false.obs;
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'videoLog_state.dart';
|
||||
|
||||
class VideoLogLogic extends BaseGetXController{
|
||||
VideoLogState state = VideoLogState();
|
||||
|
||||
}
|
||||
@ -0,0 +1,262 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/appRouters.dart';
|
||||
import 'package:star_lock/tools/noData.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import 'videoLog_logic.dart';
|
||||
|
||||
class VideoLogPage extends StatefulWidget {
|
||||
const VideoLogPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoLogPage> createState() => _VideoLogPageState();
|
||||
}
|
||||
|
||||
class _VideoLogPageState extends State<VideoLogPage> {
|
||||
final logic = Get.put(VideoLogLogic());
|
||||
final state = Get.find<VideoLogLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: TitleAppBar(
|
||||
haveTitleWidget: true,
|
||||
titleWidget: navBtn(),
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// 云存顶部
|
||||
Visibility(
|
||||
visible: !state.isNavLocal.value,
|
||||
child: vipTip()
|
||||
),
|
||||
// 本地顶部
|
||||
Visibility(
|
||||
visible: state.isNavLocal.value,
|
||||
child: localTip()
|
||||
),
|
||||
// title加编辑按钮
|
||||
editVideoTip(),
|
||||
Visibility(
|
||||
visible: !state.isNavLocal.value,
|
||||
child: Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: 5,
|
||||
itemBuilder: (c, index) {
|
||||
return Column(children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("2023.10.2$index", style: TextStyle(fontSize: 20.sp)),
|
||||
]
|
||||
)),
|
||||
mainListView(index)
|
||||
],);
|
||||
})
|
||||
)
|
||||
),
|
||||
// 本地顶部
|
||||
Visibility(
|
||||
visible: state.isNavLocal.value,
|
||||
child: Expanded(
|
||||
child: state.localList.isNotEmpty ? ListView.builder(
|
||||
itemCount: 5,
|
||||
itemBuilder: (c, index) {
|
||||
return Column(children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("2023.10.2$index", style: TextStyle(fontSize: 20.sp)),
|
||||
]
|
||||
)),
|
||||
mainListView(index)
|
||||
],);
|
||||
}): const NoData()
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// nav按钮
|
||||
Widget navBtn(){
|
||||
return SizedBox(
|
||||
width: 300.w,
|
||||
// color: Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: (){
|
||||
setState(() {
|
||||
state.isNavLocal.value = false;
|
||||
});
|
||||
},
|
||||
child: Obx(() => Text("云存", style: state.isNavLocal.value == true ? TextStyle(color: Colors.grey, fontSize: 26.sp) : TextStyle(color: Colors.white, fontSize: 28.sp)))
|
||||
),
|
||||
TextButton(
|
||||
onPressed: (){
|
||||
setState(() {
|
||||
state.isNavLocal.value = true;
|
||||
});
|
||||
},
|
||||
child: Obx(() => Text("本地", style: state.isNavLocal.value == true ? TextStyle(color: Colors.white, fontSize: 28.sp) : TextStyle(color: Colors.grey, fontSize: 26.sp)))
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 云存顶部视频
|
||||
Widget vipTip(){
|
||||
return GestureDetector(
|
||||
onTap: (){
|
||||
Get.toNamed(Routers.valueAddedServicesHighFunctionPage);
|
||||
},
|
||||
child: Container(
|
||||
height: 150.h,
|
||||
margin: EdgeInsets.all(15.w),
|
||||
padding: EdgeInsets.only(left:20.w, top:20.w, bottom:20.w, right: 10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF6F7F8),
|
||||
borderRadius: BorderRadius.circular(20.h)
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("3天滚动储存", style: TextStyle(fontSize: 26.sp)),
|
||||
SizedBox(height: 10.h),
|
||||
Text("星锁已为本设备免费提供3大滚动视频储存服务", style: TextStyle(fontSize: 22.sp, color: Colors.grey)),
|
||||
],
|
||||
)),
|
||||
SizedBox(width: 15.w),
|
||||
Text("去升级", style: TextStyle(fontSize: 24.sp)),
|
||||
Image(width: 40.w, height: 24.w, image: const AssetImage("images/icon_right_black.png"))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 本地顶部
|
||||
Widget localTip(){
|
||||
return GestureDetector(
|
||||
onTap: (){
|
||||
Get.toNamed(Routers.videoLogDownLoadPage);
|
||||
},
|
||||
child: Container(
|
||||
// height: 130.h,
|
||||
margin: EdgeInsets.all(15.w),
|
||||
padding: EdgeInsets.only(left:20.w, top:30.w, bottom:30.w, right: 10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF6F7F8),
|
||||
borderRadius: BorderRadius.circular(20.h)
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// SizedBox(height: 20.h),
|
||||
Text("下载列表", style: TextStyle(fontSize: 26.sp)),
|
||||
SizedBox(height: 15.h),
|
||||
Text("暂无下载内容", style: TextStyle(fontSize: 22.sp, color: Colors.grey)),
|
||||
],
|
||||
)),
|
||||
SizedBox(width: 15.w),
|
||||
// Text("去升级", style: TextStyle(fontSize: 24.sp)),
|
||||
Image(width: 40.w, height: 24.w, image: const AssetImage("images/icon_right_black.png"))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 标题加编辑按钮
|
||||
Widget editVideoTip(){
|
||||
return Container(
|
||||
margin: EdgeInsets.only(left:20.w),
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(state.isNavLocal.value == true ? "已下载":"全部视频", style: TextStyle(fontSize: 28.sp)),
|
||||
Expanded(child: SizedBox(width: 10.w)),
|
||||
IconButton(
|
||||
icon: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringEditVoice.png")),
|
||||
iconSize: 30,
|
||||
color: Colors.black54, onPressed: () {
|
||||
Get.toNamed(Routers.editVideoLogPage);
|
||||
},
|
||||
)
|
||||
// TextButton(
|
||||
// onPressed: (){
|
||||
//
|
||||
// },
|
||||
// child: Image(width: 40.w, height: 40.w, image: const AssetImage("images/main/icon_lockDetail_monitoringEditVoice.png"),)
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
var itemW = (1.sw - 15.w*4)/3;
|
||||
var itemH = (1.sw - 15.w*4)/3+40.h;
|
||||
|
||||
// 云存列表
|
||||
Widget mainListView(int index){
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.only(left: 15.w, right: 15.w),
|
||||
itemCount: index+1,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
//横轴元素个数
|
||||
crossAxisCount: 3,
|
||||
//纵轴间距
|
||||
mainAxisSpacing: 15.w,
|
||||
// 横轴间距
|
||||
crossAxisSpacing: 15.w,
|
||||
//子组件宽高长度比例
|
||||
childAspectRatio: itemW/itemH
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return videoItem((){
|
||||
Get.toNamed(Routers.videoLogDetailPage);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoItem(Function() action){
|
||||
return GestureDetector(
|
||||
onTap: action,
|
||||
child: SizedBox(
|
||||
width: itemW,
|
||||
height: itemH,
|
||||
child: Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10.w),
|
||||
child: Image(width: itemW, height: itemW, fit: BoxFit.fill, image: const AssetImage("images/main/icon_lockDetail_monitoringvoiceFrist.png")),
|
||||
),
|
||||
SizedBox(height:5.h),
|
||||
Text("2023.10.23 10:00", style: TextStyle(fontSize: 20.sp))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class VideoLogState{
|
||||
|
||||
var isNavLocal = false.obs;
|
||||
var localList = [];
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class ControlsOverlay extends StatelessWidget {
|
||||
const ControlsOverlay({required this.controller});
|
||||
|
||||
static const List<Duration> _exampleCaptionOffsets = <Duration>[
|
||||
Duration(seconds: -10),
|
||||
Duration(seconds: -3),
|
||||
Duration(seconds: -1, milliseconds: -500),
|
||||
Duration(milliseconds: -250),
|
||||
Duration.zero,
|
||||
Duration(milliseconds: 250),
|
||||
Duration(seconds: 1, milliseconds: 500),
|
||||
Duration(seconds: 3),
|
||||
Duration(seconds: 10),
|
||||
];
|
||||
static const List<double> _examplePlaybackRates = <double>[
|
||||
0.25,
|
||||
0.5,
|
||||
1.0,
|
||||
1.5,
|
||||
2.0,
|
||||
3.0,
|
||||
5.0,
|
||||
10.0,
|
||||
];
|
||||
|
||||
final VideoPlayerController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 50),
|
||||
reverseDuration: const Duration(milliseconds: 200),
|
||||
child: controller.value.isPlaying
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
color: Colors.black26,
|
||||
child: const Center(
|
||||
child:
|
||||
// CircularProgressIndicator()
|
||||
Icon(
|
||||
Icons.play_arrow,
|
||||
color: Colors.white,
|
||||
size: 60.0,
|
||||
semanticLabel: 'Play',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
controller.value.isPlaying ? controller.pause() : controller.play();
|
||||
},
|
||||
),
|
||||
|
||||
// Align(
|
||||
// alignment: Alignment.topLeft,
|
||||
// child: PopupMenuButton<Duration>(
|
||||
// initialValue: controller.value.captionOffset,
|
||||
// tooltip: 'Caption Offset',
|
||||
// onSelected: (Duration delay) {
|
||||
// controller.setCaptionOffset(delay);
|
||||
// },
|
||||
// itemBuilder: (BuildContext context) {
|
||||
// return <PopupMenuItem<Duration>>[
|
||||
// for (final Duration offsetDuration in _exampleCaptionOffsets)
|
||||
// PopupMenuItem<Duration>(
|
||||
// value: offsetDuration,
|
||||
// child: Text('${offsetDuration.inMilliseconds}ms'),
|
||||
// )
|
||||
// ];
|
||||
// },
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// // Using less vertical padding as the text is also longer
|
||||
// // horizontally, so it feels like it would need more spacing
|
||||
// // horizontally (matching the aspect ratio of the video).
|
||||
// vertical: 12,
|
||||
// horizontal: 16,
|
||||
// ),
|
||||
// child: Text('${controller.value.captionOffset.inMilliseconds}ms'),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Align(
|
||||
// alignment: Alignment.topRight,
|
||||
// child: PopupMenuButton<double>(
|
||||
// initialValue: controller.value.playbackSpeed,
|
||||
// tooltip: 'Playback speed',
|
||||
// onSelected: (double speed) {
|
||||
// controller.setPlaybackSpeed(speed);
|
||||
// },
|
||||
// itemBuilder: (BuildContext context) {
|
||||
// return <PopupMenuItem<double>>[
|
||||
// for (final double speed in _examplePlaybackRates)
|
||||
// PopupMenuItem<double>(
|
||||
// value: speed,
|
||||
// child: Text('${speed}x'),
|
||||
// )
|
||||
// ];
|
||||
// },
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// // Using less vertical padding as the text is also longer
|
||||
// // horizontally, so it feels like it would need more spacing
|
||||
// // horizontally (matching the aspect ratio of the video).
|
||||
// vertical: 12,
|
||||
// horizontal: 16,
|
||||
// ),
|
||||
// child: Text('${controller.value.playbackSpeed}x'),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
|
||||
Positioned(
|
||||
top: 0.h,
|
||||
left: 0.h,
|
||||
right: 0.h,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(left:20.w, right:20.w),
|
||||
// color: const Color(0xC83C3F41),
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("星锁 2023/10/23", style: TextStyle(color: Colors.white, fontSize: 20.sp)),
|
||||
Expanded(child: SizedBox(width: 10.w)),
|
||||
Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(10.w),
|
||||
child: Image(width: 50.w, height: 50.w, image: const AssetImage("images/main/icon_lockDetail_monitoringShareVideo_white.png")),
|
||||
),
|
||||
SizedBox(width: 20.w),
|
||||
Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(10.w),
|
||||
child: Image(width: 50.w, height: 50.w, image: const AssetImage("images/main/icon_lockDetail_monitoringDownloadVideo_white.png")),
|
||||
),
|
||||
SizedBox(width: 20.w),
|
||||
Container(
|
||||
width: 50.w,
|
||||
height: 50.w,
|
||||
padding: EdgeInsets.all(10.w),
|
||||
child: Image(width: 50.w, height: 50.w, image: const AssetImage("images/main/icon_lockDetail_monitoringDeletVideo_white.png")),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 10.w, left: 10.w),
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.5),
|
||||
height: 60.h,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
//暂停按钮
|
||||
InkWell(
|
||||
child: controller.value.isPlaying ? const Icon(Icons.pause, size: 30, color: Color(0xffefefef)) : const Icon(Icons.play_arrow, size: 30, color: Color(0xffefefef)),
|
||||
onTap: () {
|
||||
// if(controller.value.isBuffering == false){
|
||||
// return;
|
||||
// }
|
||||
controller.value.isPlaying ? controller.pause() : controller.play();
|
||||
},
|
||||
),
|
||||
//当前播放进度
|
||||
Text(formatString(controller.value.position), style: TextStyle(fontSize: 22.sp, color: const Color(0xffefefef)),),
|
||||
//进度条
|
||||
Expanded(
|
||||
|
||||
child: Slider(activeColor: const Color(0xFFFFFFFF), max: controller.value.duration.inMilliseconds.truncateToDouble(),
|
||||
value: controller.value.position.inMilliseconds.truncateToDouble(),
|
||||
onChanged: (newRating) {
|
||||
controller
|
||||
.seekTo(Duration(
|
||||
milliseconds:
|
||||
newRating
|
||||
.truncate()));
|
||||
},
|
||||
),
|
||||
),
|
||||
//总视频进度
|
||||
Text(formatString(controller.value.duration), style: TextStyle(fontSize: 22.sp, color: const Color(0xffefefef)),),
|
||||
//倍速下拉菜单
|
||||
// DropdownButtonHideUnderline(
|
||||
// child: DropdownButton2(
|
||||
// hint: Text(
|
||||
// _selectedValue,
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// color:
|
||||
// Color(0xffefefef),
|
||||
// ),
|
||||
// ),
|
||||
// items: items
|
||||
// .map((item) =>
|
||||
// DropdownMenuItem<
|
||||
// String>(
|
||||
// value: item,
|
||||
// child: Text(
|
||||
// '${item}x',
|
||||
// style:
|
||||
// const TextStyle(
|
||||
// fontSize:
|
||||
// 16,
|
||||
// ),
|
||||
// ),
|
||||
// ))
|
||||
// .toList(),
|
||||
// // value: _selectedValue,
|
||||
// // icon: Visibility(
|
||||
// // visible: false,
|
||||
// // child: Icon(Icons
|
||||
// // .arrow_downward)),
|
||||
// onChanged: (value) async {
|
||||
// double val =
|
||||
// double.parse(value
|
||||
// as String);
|
||||
// _videoPlayerController
|
||||
// .setPlaybackSpeed(val);
|
||||
// setState(() {
|
||||
// if (val == 1.0) {
|
||||
// _selectedValue =
|
||||
// '倍速';
|
||||
// } else {
|
||||
// _selectedValue =
|
||||
// '${value as String}x';
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// // dropdownWidth: 100,
|
||||
// // buttonWidth: 60,
|
||||
// // iconSize: 36,
|
||||
// // iconEnabledColor:Colors.white,
|
||||
// // buttonPadding:const EdgeInsets.only(left: 10),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String formatString(time) {
|
||||
var shortName = time.toString().substring(2, 7);
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
|
||||
import 'videoLogDetail_state.dart';
|
||||
|
||||
class VideoLogDetailLogic extends BaseGetXController{
|
||||
VideoLogDetailState state = VideoLogDetailState();
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,191 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import 'controlsOverlay_page.dart';
|
||||
import 'videoLogDetail_logic.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class VideoLogDetailPage extends StatefulWidget {
|
||||
const VideoLogDetailPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoLogDetailPage> createState() => _VideoLogDetailPageState();
|
||||
}
|
||||
|
||||
class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
|
||||
final logic = Get.put(VideoLogDetailLogic());
|
||||
final state = Get.find<VideoLogDetailLogic>().state;
|
||||
|
||||
late VideoPlayerController _controller;
|
||||
|
||||
// Future<ClosedCaptionFile> _loadCaptions() async {
|
||||
// final String fileContents = await DefaultAssetBundle.of(context)
|
||||
// .loadString('images/bumble_bee_captions.vtt');
|
||||
// return WebVTTCaptionFile(
|
||||
// fileContents); // For vtt files, use WebVTTCaptionFile
|
||||
// }
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = VideoPlayerController.networkUrl(
|
||||
Uri.parse('https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
|
||||
// closedCaptionFile: _loadCaptions(),
|
||||
// videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
|
||||
);
|
||||
|
||||
_controller.addListener(() {
|
||||
setState(() {
|
||||
print("controller.value.isBuffering:${_controller.value.isBuffering}");
|
||||
});
|
||||
});
|
||||
|
||||
_controller.setLooping(false);
|
||||
// _controller.setVolume(1);
|
||||
_controller.initialize();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: TitleAppBar(
|
||||
barTitle: "本地视频播放",
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 16/9,
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: <Widget>[
|
||||
VideoPlayer(_controller),
|
||||
// ClosedCaption(text: _controller.value.caption.text),
|
||||
ControlsOverlay(controller: _controller),
|
||||
(_controller.value.isPlaying || _controller.value.isBuffering) ? Container() : VideoProgressIndicator(_controller, colors:VideoProgressColors(playedColor:AppColors.mainColor), allowScrubbing: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text("2023.10.23", style: TextStyle(fontSize: 20.sp)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15.h),
|
||||
Row(
|
||||
children: [
|
||||
videoItem(true),
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: 4,
|
||||
itemBuilder: (c, index) {
|
||||
return Column(children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("2023.10.23", style: TextStyle(fontSize: 20.sp)),
|
||||
]
|
||||
)),
|
||||
mainListView()
|
||||
],);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
var itemW = (1.sw - 15.w*4)/3;
|
||||
var itemH = (1.sw - 15.w*4)/3+40.h;
|
||||
Widget mainListView(){
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.only(left: 15.w, right: 15.w),
|
||||
itemCount: 4,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
//横轴元素个数
|
||||
crossAxisCount: 3,
|
||||
//纵轴间距
|
||||
mainAxisSpacing: 10.w,
|
||||
// 横轴间距
|
||||
crossAxisSpacing: 15.w,
|
||||
//子组件宽高长度比例
|
||||
childAspectRatio: itemW/itemH
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return videoItem(false);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoItem(bool isPlay){
|
||||
return SizedBox(
|
||||
width: itemW,
|
||||
height: itemH,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10.w),
|
||||
child: Stack(
|
||||
children: [
|
||||
Image(width: itemW, height: itemW, fit: BoxFit.fill, image: const AssetImage("images/main/icon_lockDetail_monitoringvoiceFrist.png")),
|
||||
Positioned(
|
||||
left: 8.w,
|
||||
bottom: 5.h,
|
||||
child: Text("00:06", style: TextStyle(color: Colors.white, fontSize: 20.sp))
|
||||
),
|
||||
Visibility(
|
||||
visible: isPlay,
|
||||
child: Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
// padding: EdgeInsets.only(right: 10.w, left: 10.w),
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.3),
|
||||
child: Center(child: Text("播放中", style: TextStyle(color: Colors.white, fontSize: 22.sp))),
|
||||
)
|
||||
),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
SizedBox(height:5.h),
|
||||
Text("2023.10.23 10:00", style: TextStyle(fontSize: 20.sp))
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
class VideoLogDetailState{
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'videoLogDownLoad_state.dart';
|
||||
|
||||
class VideoLogDownLoadLogic extends BaseGetXController{
|
||||
VideoLogDownLoadState state = VideoLogDownLoadState();
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/noData.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import 'videoLogDownLoad_logic.dart';
|
||||
|
||||
class VideoLogDownLoadPage extends StatefulWidget {
|
||||
const VideoLogDownLoadPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VideoLogDownLoadPage> createState() => _VideoLogDownLoadPageState();
|
||||
}
|
||||
|
||||
class _VideoLogDownLoadPageState extends State<VideoLogDownLoadPage> {
|
||||
final logic = Get.put(VideoLogDownLoadLogic());
|
||||
final state = Get.find<VideoLogDownLoadLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: TitleAppBar(
|
||||
barTitle: "下载列表",
|
||||
haveBack: true,
|
||||
backgroundColor: AppColors.mainColor,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: state.localList.isNotEmpty ? ListView.builder(
|
||||
itemCount: 5,
|
||||
itemBuilder: (c, index) {
|
||||
return Column(children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(left:20.w, top: 15.w, bottom: 15.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text("2023.10.23", style: TextStyle(fontSize: 20.sp)),
|
||||
]
|
||||
)),
|
||||
mainListView(index)
|
||||
],);
|
||||
}): const NoData(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
var itemW = (1.sw - 15.w*4)/3;
|
||||
var itemH = (1.sw - 15.w*4)/3+40.h;
|
||||
Widget mainListView(int index){
|
||||
return Container(
|
||||
// margin: EdgeInsets.only(left: 10.w, right: 10.w, top: 40.h),
|
||||
// color: Colors.blue,
|
||||
child: GridView.builder(
|
||||
padding: EdgeInsets.only(left: 15.w, right: 15.w),
|
||||
itemCount: index+1,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
//横轴元素个数
|
||||
crossAxisCount: 3,
|
||||
//纵轴间距
|
||||
mainAxisSpacing: 10.w,
|
||||
// 横轴间距
|
||||
crossAxisSpacing: 15.w,
|
||||
//子组件宽高长度比例
|
||||
childAspectRatio: itemW/itemH
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return videoItem();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoItem(){
|
||||
return SizedBox(
|
||||
width: itemW,
|
||||
height: itemH,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10.w),
|
||||
child: Image(width: itemW, height: itemW, fit: BoxFit.fill, image: const AssetImage("images/main/icon_lockDetail_monitoringvoiceFrist.png")),
|
||||
),
|
||||
SizedBox(height:5.h),
|
||||
Text("2023.10.23 10:00", style: TextStyle(fontSize: 20.sp))
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
class VideoLogDownLoadState{
|
||||
var localList = [];
|
||||
}
|
||||
@ -323,6 +323,15 @@ class _DemoModeLockDetailPageState extends State<DemoModeLockDetailPage> {
|
||||
Get.toNamed(Routers.lockOperatingRecordPage,
|
||||
arguments: {"keyInfo": LockListInfoItemEntity()});
|
||||
}),
|
||||
// 视频日志
|
||||
bottomItem('images/main/icon_lockDetail_videoLog.png', "视频日志", () {
|
||||
//视频日志
|
||||
Get.toNamed(Routers.videoLogPage);
|
||||
}),
|
||||
// 消息提醒
|
||||
bottomItem('images/main/icon_lockDetail_messageReminding.png', "消息提醒", () {
|
||||
Get.toNamed(Routers.msgNotificationPage);
|
||||
}),
|
||||
// 设置
|
||||
bottomItem(
|
||||
'images/main/icon_main_set.png', TranslationLoader.lanKeys!.set!.tr,
|
||||
|
||||
@ -11,6 +11,7 @@ import '../../../../tools/submitBtn.dart';
|
||||
import '../../../../tools/titleAppBar.dart';
|
||||
import '../../../../tools/toast.dart';
|
||||
import '../../../../translations/trans_lib.dart';
|
||||
import '../../../lockDetail/lcokSet/lockSet/lockSetInfo_entity.dart';
|
||||
|
||||
class DemoModeLockSetPage extends StatefulWidget {
|
||||
const DemoModeLockSetPage({Key? key}) : super(key: key);
|
||||
@ -78,6 +79,24 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
// Get.toNamed(Routers.wirelessKeyboardPage);
|
||||
// Toast.show(msg: "功能暂未开放");
|
||||
}),
|
||||
// 照明
|
||||
CommonItem(
|
||||
leftTitel: '照明',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// 开门器
|
||||
CommonItem(
|
||||
leftTitel: '开门器',
|
||||
rightTitle: "",
|
||||
isHaveLine: false,
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
SizedBox(height: 10.h),
|
||||
// 自动闭锁
|
||||
CommonItem(
|
||||
@ -148,54 +167,30 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
}),
|
||||
SizedBox(height: 10.h),
|
||||
//---田总新增展示
|
||||
// Obx(() =>
|
||||
CommonItem(
|
||||
leftTitel: '面容开锁',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _otherUnHaveDoneSwitch())),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
CommonItem(
|
||||
leftTitel: '感应距离',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}
|
||||
),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
}),
|
||||
CommonItem(
|
||||
leftTitel: '自动亮屏',
|
||||
leftTitel: '消息提醒',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _otherUnHaveDoneSwitch())),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
CommonItem(
|
||||
leftTitel: '逗留警告',
|
||||
leftTitel: '猫眼设置',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _otherUnHaveDoneSwitch())),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
CommonItem(
|
||||
leftTitel: '异常警告',
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _otherUnHaveDoneSwitch())),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
CommonItem(
|
||||
leftTitel: '开门方向设置',
|
||||
rightTitle: "",
|
||||
@ -203,10 +198,7 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
isHaveLine: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}
|
||||
),
|
||||
// ),
|
||||
// Obx(() =>
|
||||
}),
|
||||
CommonItem(
|
||||
leftTitel: '电机功率设置',
|
||||
rightTitle: "",
|
||||
@ -214,11 +206,18 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}
|
||||
),
|
||||
}),
|
||||
// 支持蓝牙广播(关闭则不能使用蓝牙主动开锁)
|
||||
CommonItem(
|
||||
leftTitel: '蓝牙广播',
|
||||
rightTitle: "",
|
||||
isHaveLine: false,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget: _lockRemindSwitch()),
|
||||
// ),
|
||||
SizedBox(height: 10.h),
|
||||
//-----新增至此
|
||||
// 标记房态
|
||||
CommonItem(
|
||||
leftTitel:
|
||||
TranslationLoader.lanKeys!.markedHouseState!.tr,
|
||||
@ -228,22 +227,23 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// 考勤
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.checkingIn!.tr,
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _openCheckInSwitch())
|
||||
rightWidget: _openCheckInSwitch()
|
||||
),
|
||||
// 开锁提醒
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.unlockReminder!.tr,
|
||||
rightTitle: "",
|
||||
isHaveLine: false,
|
||||
isHaveRightWidget: true,
|
||||
rightWidget:
|
||||
SizedBox(width: 60.w, child: _lockRemindSwitch())),
|
||||
rightWidget: _lockRemindSwitch()),
|
||||
SizedBox(height: 10.h),
|
||||
// wifi配网
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader
|
||||
.lanKeys!.wifiDistributionNetwork!.tr,
|
||||
@ -253,6 +253,7 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// 锁时间
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.lockTime!.tr,
|
||||
rightTitle: "",
|
||||
@ -261,6 +262,7 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// 诊断
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.diagnose!.tr,
|
||||
rightTitle: "",
|
||||
@ -269,6 +271,7 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// 上传数据
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.uploadData!.tr,
|
||||
rightTitle: "",
|
||||
@ -277,15 +280,16 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
CommonItem(
|
||||
leftTitel:
|
||||
TranslationLoader.lanKeys!.importOtherLockData!.tr,
|
||||
rightTitle: "",
|
||||
isHaveLine: true,
|
||||
isHaveDirection: true,
|
||||
action: () {
|
||||
gotoAddLock();
|
||||
}),
|
||||
// CommonItem(
|
||||
// leftTitel:
|
||||
// TranslationLoader.lanKeys!.importOtherLockData!.tr,
|
||||
// rightTitle: "",
|
||||
// isHaveLine: true,
|
||||
// isHaveDirection: true,
|
||||
// action: () {
|
||||
// gotoAddLock();
|
||||
// }),
|
||||
// 锁升级
|
||||
CommonItem(
|
||||
leftTitel: TranslationLoader.lanKeys!.lockEscalation!.tr,
|
||||
rightTitle: "",
|
||||
@ -335,18 +339,6 @@ class _DemoModeLockSetPageState extends State<DemoModeLockSetPage> {
|
||||
);
|
||||
}
|
||||
|
||||
CupertinoSwitch _otherUnHaveDoneSwitch() {
|
||||
return CupertinoSwitch(
|
||||
activeColor: CupertinoColors.activeBlue,
|
||||
trackColor: CupertinoColors.systemGrey5,
|
||||
thumbColor: CupertinoColors.white,
|
||||
value: false,
|
||||
onChanged: (value) {
|
||||
gotoAddLock();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void gotoAddLock(){
|
||||
// Get.toNamed(Routers.seletLockTypePage);
|
||||
Toast.show(msg: "演示模式");
|
||||
|
||||
@ -110,6 +110,7 @@ class SaveLockLogic extends BaseGetXController {
|
||||
|
||||
// 厂商名称
|
||||
var vendor = reply.data.sublist(3, 23);
|
||||
print("vendor:$vendor reply.data:${reply.data}");
|
||||
var vendorStr = utf8String(vendor);
|
||||
state.lockInfo["vendor"] = vendorStr;
|
||||
// print("vendor:$vendor vendorStr:$vendorStr vendorStr.length${vendorStr.length}");
|
||||
|
||||
@ -128,7 +128,7 @@ class _StarLockMinePageState extends State<StarLockMinePage> with BaseWidget {
|
||||
// }),
|
||||
mineItem('images/mine/icon_mine_main_addLock.png',
|
||||
TranslationLoader.lanKeys!.addDevice!.tr, () {
|
||||
Navigator.pushNamed(context, Routers.seletLockTypePage);
|
||||
Get.toNamed(Routers.seletLockTypePage);
|
||||
}),
|
||||
// mineItem('images/mine/icon_mine_main_gateway.png',
|
||||
// TranslationLoader.lanKeys!.gateway!.tr, () {
|
||||
@ -136,7 +136,7 @@ class _StarLockMinePageState extends State<StarLockMinePage> with BaseWidget {
|
||||
// }),
|
||||
mineItem('images/mine/icon_mine_main_message.png',
|
||||
TranslationLoader.lanKeys!.message!.tr, () {
|
||||
Navigator.pushNamed(context, Routers.messageListPage);
|
||||
Get.toNamed(Routers.messageListPage);
|
||||
// Toast.show(msg: "功能暂未开放");
|
||||
}),
|
||||
//删除“客服”行
|
||||
@ -146,15 +146,15 @@ class _StarLockMinePageState extends State<StarLockMinePage> with BaseWidget {
|
||||
// }),
|
||||
mineItem('images/mine/icon_mine_main_set.png',
|
||||
TranslationLoader.lanKeys!.set!.tr, () {
|
||||
Navigator.pushNamed(context, Routers.mineSetPage);
|
||||
Get.toNamed(Routers.mineSetPage);
|
||||
}),
|
||||
mineItem('images/mine/icon_mine_main_vip.png',
|
||||
TranslationLoader.lanKeys!.valueAddedServices!.tr, () {
|
||||
Navigator.pushNamed(context, Routers.valueAddedServicesPage);
|
||||
Get.toNamed(Routers.valueAddedServicesPage);
|
||||
}),
|
||||
mineItem('images/mine/icon_mine_main_about.png',
|
||||
TranslationLoader.lanKeys!.about!.tr, () {
|
||||
Navigator.pushNamed(context, Routers.abountPage);
|
||||
Get.toNamed(Routers.abountPage);
|
||||
}),
|
||||
],
|
||||
),
|
||||
|
||||
@ -43,17 +43,14 @@ class _ValueAddedServicesPageListState
|
||||
// arguments: 2);
|
||||
// }),
|
||||
_valueAddedServicesItem(
|
||||
Image.asset(
|
||||
'images/mine/icon_mine_valueAddedServices_realName.png'),
|
||||
Image.asset('images/mine/icon_mine_valueAddedServices_realName.png'),
|
||||
TranslationLoader.lanKeys!.realNameAuthentication!.tr, () {
|
||||
Navigator.pushNamed(
|
||||
context, Routers.valueAddedServicesRealNamePage);
|
||||
Get.toNamed(Routers.valueAddedServicesRealNamePage);
|
||||
}),
|
||||
_valueAddedServicesItem(
|
||||
Image.asset('images/mine/icon_mine_valueAddedServices_vip.png'),
|
||||
TranslationLoader.lanKeys!.advancedFunction!.tr, () {
|
||||
Navigator.pushNamed(
|
||||
context, Routers.valueAddedServicesHighFunctionPage);
|
||||
Get.toNamed(Routers.valueAddedServicesHighFunctionPage);
|
||||
}),
|
||||
// _valueAddedServicesItem(
|
||||
// Image.asset('images/mine/icon_mine_valueAddedServices_push.png'),
|
||||
|
||||
@ -55,6 +55,8 @@ class BaseProvider extends GetConnect with Api {
|
||||
body: rs as T,
|
||||
statusText: res.statusText,
|
||||
);
|
||||
}else{
|
||||
|
||||
}
|
||||
// print('得到的数据======>bodyString:${res.bodyString} body:${res.body} bodyBytes:${res.bodyBytes} status:${res.status} statusText:${res.statusText} statusCode:${res.statusCode}');
|
||||
getDataResult(res.body);
|
||||
|
||||
@ -9,7 +9,8 @@ class NoData extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 1.sw,
|
||||
height: 1.sh - ScreenUtil().statusBarHeight,
|
||||
// height: 1.sh - ScreenUtil().statusBarHeight,
|
||||
height: 1.sw,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@ -112,7 +112,12 @@ dependencies:
|
||||
cached_network_image: ^3.2.0
|
||||
webview_flutter: ^4.2.3
|
||||
aliyun_push: ^0.1.6
|
||||
|
||||
|
||||
#视频播放器
|
||||
video_player: ^2.7.2
|
||||
#控制横竖屏控件
|
||||
auto_orientation: ^2.3.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||