|
|
@@ -0,0 +1,557 @@
|
|
|
+import 'dart:io';
|
|
|
+
|
|
|
+import 'package:clean/model/asset_info.dart';
|
|
|
+import 'package:clean/module/privacy/privacy_controller.dart';
|
|
|
+import 'package:clean/utils/expand.dart';
|
|
|
+import 'package:clean/utils/file_utils.dart';
|
|
|
+import 'package:clean/utils/image_util.dart';
|
|
|
+import 'package:flutter/Material.dart';
|
|
|
+import 'package:flutter/cupertino.dart';
|
|
|
+import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
+import 'package:get/get.dart';
|
|
|
+import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
|
|
+import '../../base/base_view.dart';
|
|
|
+import '../../resource/assets.gen.dart';
|
|
|
+import '../more/more_controller.dart';
|
|
|
+import 'dart:typed_data';
|
|
|
+
|
|
|
+class PrivacyPage extends BaseView<PrivacyController> {
|
|
|
+ const PrivacyPage({super.key});
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget buildBody(BuildContext context) {
|
|
|
+ return Obx(() {
|
|
|
+ return Stack(
|
|
|
+ children: [
|
|
|
+ !controller.isUnlock.value
|
|
|
+ ? _buildPasswordPage()
|
|
|
+ : _buildPrivacySpace(context),
|
|
|
+ IgnorePointer(
|
|
|
+ child: Assets.images.bgHome.image(
|
|
|
+ width: 360.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 输入密码界面
|
|
|
+ Widget _buildPasswordPage() {
|
|
|
+ return SafeArea(
|
|
|
+ child: Container(
|
|
|
+ padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
|
|
|
+ child: Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ Get.back();
|
|
|
+ },
|
|
|
+ child:
|
|
|
+ Assets.images.iconCommonBack.image(width: 28.w, height: 28.w),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 28.h,
|
|
|
+ ),
|
|
|
+ Align(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Assets.images.iconPrivacyLock
|
|
|
+ .image(width: 70.w, height: 70.w),
|
|
|
+ Text(
|
|
|
+ "Input password",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 16.sp,
|
|
|
+ fontWeight: FontWeight.w700,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 28.h,
|
|
|
+ ),
|
|
|
+ Obx(() {
|
|
|
+ return Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ width: 32.h,
|
|
|
+ height: 32.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ width: 2.w,
|
|
|
+ ),
|
|
|
+ borderRadius:
|
|
|
+ BorderRadius.all(Radius.circular(16.h)),
|
|
|
+ color: controller.passwordStr.value.isNotEmpty
|
|
|
+ ? "#0279FB".color
|
|
|
+ : Colors.transparent),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ margin: EdgeInsets.only(left: 16.w),
|
|
|
+ width: 32.h,
|
|
|
+ height: 32.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ width: 2.w,
|
|
|
+ ),
|
|
|
+ borderRadius:
|
|
|
+ BorderRadius.all(Radius.circular(16.h)),
|
|
|
+ color: controller.passwordStr.value.length >= 2
|
|
|
+ ? "#0279FB".color
|
|
|
+ : Colors.transparent),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ margin: EdgeInsets.only(left: 16.w),
|
|
|
+ width: 32.h,
|
|
|
+ height: 32.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ width: 2.w,
|
|
|
+ ),
|
|
|
+ borderRadius:
|
|
|
+ BorderRadius.all(Radius.circular(16.h)),
|
|
|
+ color: controller.passwordStr.value.length >= 3
|
|
|
+ ? "#0279FB".color
|
|
|
+ : Colors.transparent),
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ margin: EdgeInsets.only(left: 16.w),
|
|
|
+ width: 32.h,
|
|
|
+ height: 32.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ border: Border.all(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ width: 2.w,
|
|
|
+ ),
|
|
|
+ borderRadius:
|
|
|
+ BorderRadius.all(Radius.circular(16.h)),
|
|
|
+ color: controller.passwordStr.value.length >= 4
|
|
|
+ ? "#0279FB".color
|
|
|
+ : Colors.transparent),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ SizedBox(
|
|
|
+ height: 67.h,
|
|
|
+ ),
|
|
|
+ _buildPasswordInput(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPasswordInput() {
|
|
|
+ return Column(
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
+ children: [
|
|
|
+ _buildNumberBtn("1"),
|
|
|
+ _buildNumberBtn("2"),
|
|
|
+ _buildNumberBtn("3"),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ SizedBox(height: 20.h),
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
+ children: [
|
|
|
+ _buildNumberBtn("4"),
|
|
|
+ _buildNumberBtn("5"),
|
|
|
+ _buildNumberBtn("6"),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ SizedBox(height: 20.h),
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
+ children: [
|
|
|
+ _buildNumberBtn("7"),
|
|
|
+ _buildNumberBtn("8"),
|
|
|
+ _buildNumberBtn("9"),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ SizedBox(height: 20.h),
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
+ children: [
|
|
|
+ _buildNumberBtn(""),
|
|
|
+ _buildNumberBtn("0"),
|
|
|
+ _buildDeleteBtn(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildNumberBtn(String num) {
|
|
|
+ return Opacity(
|
|
|
+ opacity: num.isEmpty ? 0 : 1,
|
|
|
+ child: GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ controller.inputPassword(num);
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ width: 76.w,
|
|
|
+ height: 76.w,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: "#FFFFFF".color.withOpacity(0.12),
|
|
|
+ borderRadius: BorderRadius.all(Radius.circular(38.w)),
|
|
|
+ ),
|
|
|
+ child: Align(
|
|
|
+ child: Text(
|
|
|
+ num,
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 20.sp,
|
|
|
+ fontWeight: FontWeight.w700,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildDeleteBtn() {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ if (controller.passwordStr.isNotEmpty) {
|
|
|
+ controller.passwordStr.value = controller.passwordStr.value
|
|
|
+ .substring(0, controller.passwordStr.value.length - 1);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ width: 76.w,
|
|
|
+ height: 76.w,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: "#FFFFFF".color.withOpacity(0.12),
|
|
|
+ borderRadius: BorderRadius.all(Radius.circular(38.w)),
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child:
|
|
|
+ Assets.images.iconPrivacyDelete.image(width: 34.w, height: 24.h),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPrivacySpace(BuildContext context) {
|
|
|
+ return SafeArea(
|
|
|
+ child: Container(
|
|
|
+ padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
|
|
|
+ child: Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ Get.back();
|
|
|
+ },
|
|
|
+ child: Assets.images.iconCommonBack
|
|
|
+ .image(width: 28.w, height: 28.w),
|
|
|
+ ),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ Get.back();
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ width: 71.w,
|
|
|
+ height: 30.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: "#1F2D3F".color,
|
|
|
+ borderRadius: BorderRadius.all(
|
|
|
+ Radius.circular(15.h),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ "Select",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 14.sp,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 12.h,
|
|
|
+ ),
|
|
|
+ Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ "Privacy Space",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontWeight: FontWeight.w700,
|
|
|
+ fontSize: 24.sp,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ GestureDetector(
|
|
|
+ child: Assets.images.iconPrivacyUnlock
|
|
|
+ .image(width: 34.w, height: 34.w),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ // _buildEmptyPhotoView(context),
|
|
|
+ _buildPhotoView(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ _buildEmptyPhotoView(BuildContext context) {
|
|
|
+ return Center(
|
|
|
+ child: Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ SizedBox(
|
|
|
+ height: 130.h,
|
|
|
+ ),
|
|
|
+ // Image.memory(),
|
|
|
+ // Image.file(controller.file!),
|
|
|
+ Assets.images.iconPrivacyEmptyImage.image(width: 70.w, height: 70.w),
|
|
|
+ SizedBox(
|
|
|
+ height: 22.h,
|
|
|
+ ),
|
|
|
+ Text(
|
|
|
+ "Upload Files to Privacy Space",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white.withOpacity(0.9),
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ fontSize: 18.sp,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 116.h,
|
|
|
+ ),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ controller.uploadBtnClick();
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ width: 180.w,
|
|
|
+ height: 48.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ borderRadius: BorderRadius.all(
|
|
|
+ Radius.circular(10.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ "Upload",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontSize: 16.sp,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildPhotoView() {
|
|
|
+ return Expanded(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ SizedBox(
|
|
|
+ height: 20.h,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: ListView.builder(
|
|
|
+ shrinkWrap: true,
|
|
|
+ itemCount: controller.monthCount,
|
|
|
+ itemBuilder: (context, index) {
|
|
|
+ final monthAssets = ImageUtil.getMonthAssets(controller.assetsByMonth, index);
|
|
|
+ return Column(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ ImageUtil.getMonthText(controller.assetsByMonth, index),
|
|
|
+ style: TextStyle(
|
|
|
+ fontSize: 16.sp,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ color: Colors.white),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 11.h,
|
|
|
+ ),
|
|
|
+ GridView.builder(
|
|
|
+ shrinkWrap: true,
|
|
|
+ physics: NeverScrollableScrollPhysics(),
|
|
|
+ itemCount: monthAssets.length,
|
|
|
+ itemBuilder: (context, index) {
|
|
|
+ var asset = monthAssets[index];
|
|
|
+ return _buildAssetItem(asset);
|
|
|
+ },
|
|
|
+ gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
|
+ crossAxisCount: 3, // 每行有 4 列
|
|
|
+ mainAxisSpacing: 8.w, // 主轴(垂直)上的间距
|
|
|
+ crossAxisSpacing: 8.w, // 交叉轴(水平)上的间距
|
|
|
+ childAspectRatio: 1.0, // 子项宽高比
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ SizedBox(
|
|
|
+ height: 24.h,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ controller.uploadBtnClick();
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ width: 328.w,
|
|
|
+ height: 48.h,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: "#0279FB".color,
|
|
|
+ borderRadius: BorderRadius.all(
|
|
|
+ Radius.circular(10.r),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Center(
|
|
|
+ child: Text(
|
|
|
+ "Upload",
|
|
|
+ style: TextStyle(
|
|
|
+ color: Colors.white,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ fontSize: 16.sp,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建图片项
|
|
|
+ Widget _buildAssetItem(AssetInfo asset) {
|
|
|
+ return GestureDetector(
|
|
|
+ onTap: () => _showImageDetail(asset),
|
|
|
+ child: Stack(
|
|
|
+ children: [
|
|
|
+ ClipRRect(
|
|
|
+ borderRadius: BorderRadius.circular(8.r),
|
|
|
+ child: FutureBuilder<Uint8List?>(
|
|
|
+ future: ImageUtil.getImageThumbnail(asset),
|
|
|
+ builder: (context, snapshot) {
|
|
|
+ if (snapshot.connectionState == ConnectionState.waiting) {
|
|
|
+ return Container(
|
|
|
+ color: Colors.grey[200],
|
|
|
+ child: Center(
|
|
|
+ child: SizedBox(
|
|
|
+ width: 20.w,
|
|
|
+ height: 20.w,
|
|
|
+ child: CircularProgressIndicator(
|
|
|
+ strokeWidth: 2.w,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (snapshot.hasData && snapshot.data != null) {
|
|
|
+ return Image.memory(
|
|
|
+ snapshot.data!,
|
|
|
+ width: double.infinity,
|
|
|
+ height: double.infinity,
|
|
|
+ fit: BoxFit.cover,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return Container(
|
|
|
+ color: Colors.grey[200],
|
|
|
+ child: Icon(
|
|
|
+ Icons.error_outline,
|
|
|
+ color: Colors.grey[400],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ // // 删除按钮
|
|
|
+ // Positioned(
|
|
|
+ // right: 4.w,
|
|
|
+ // top: 4.w,
|
|
|
+ // child: GestureDetector(
|
|
|
+ // // onTap: () => _deleteAsset(asset),
|
|
|
+ // child: Container(
|
|
|
+ // padding: EdgeInsets.all(4.w),
|
|
|
+ // decoration: BoxDecoration(
|
|
|
+ // color: Colors.black.withOpacity(0.5),
|
|
|
+ // shape: BoxShape.circle,
|
|
|
+ // ),
|
|
|
+ // child: Icon(
|
|
|
+ // Icons.close,
|
|
|
+ // size: 16.w,
|
|
|
+ // color: Colors.white,
|
|
|
+ // ),
|
|
|
+ // ),
|
|
|
+ // ),
|
|
|
+ // ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示图片详情
|
|
|
+ void _showImageDetail(AssetInfo asset) {
|
|
|
+ Get.dialog(
|
|
|
+ Dialog(
|
|
|
+ backgroundColor: Colors.transparent,
|
|
|
+ child: FutureBuilder<File?>(
|
|
|
+ future: ImageUtil.getImageFile(asset),
|
|
|
+ builder: (context, snapshot) {
|
|
|
+ if (snapshot.connectionState == ConnectionState.waiting) {
|
|
|
+ return const Center(child: CircularProgressIndicator());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (snapshot.hasData && snapshot.data != null) {
|
|
|
+ return InteractiveViewer(
|
|
|
+ child: Image.file(
|
|
|
+ snapshot.data!,
|
|
|
+ fit: BoxFit.contain,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ return const Center(
|
|
|
+ child: Text(
|
|
|
+ '加载失败',
|
|
|
+ style: TextStyle(color: Colors.white),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|