1
This commit is contained in:
parent
8594b0151a
commit
121e423c8b
BIN
assets/images/custom_lock.png
Normal file
BIN
assets/images/custom_lock.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/images/custom_selected.png
Normal file
BIN
assets/images/custom_selected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@ -47,12 +47,12 @@ post_install do |installer|
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
|
||||
## dart: PermissionGroup.camera
|
||||
'PERMISSION_CAMERA=1',
|
||||
|
||||
## dart: PermissionGroup.photos
|
||||
'PERMISSION_PHOTOS=1',
|
||||
|
||||
## dart:PermissionGroup.photosAddOnly
|
||||
'PERMISSION_PHOTOS_ADD_ONLY=1',
|
||||
|
||||
## dart: PermissionGroup.appTrackingTransparency
|
||||
'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
|
||||
]
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This will enable you to take photos and recognize text within them for translation.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>We need access to your photo album so you can save wallpapers from the app to your album</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
|
||||
@ -24,7 +24,7 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: preferredSize.height,
|
||||
padding: EdgeInsets.fromLTRB(10, ScreenUtil().statusBarHeight, 20, 0).w,
|
||||
padding: EdgeInsets.fromLTRB(10, ScreenUtil().statusBarHeight, 10, 0).w,
|
||||
color: backgroundColor ?? seedColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -35,8 +35,6 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onBackTap ?? () => Get.back(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10).w,
|
||||
child: Image.asset(
|
||||
Assets.iconBack,
|
||||
width: 32.w,
|
||||
@ -46,7 +44,6 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
@ -60,7 +57,7 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(width: 13.w),
|
||||
Container(width: 32.w),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
66
lib/common/components/photo_picker_bottom_sheet.dart
Normal file
66
lib/common/components/photo_picker_bottom_sheet.dart
Normal file
@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class TPhotoPickerBottomSheet extends StatelessWidget {
|
||||
const TPhotoPickerBottomSheet({super.key, required this.funCamera, required this.funGallery});
|
||||
|
||||
final Function() funCamera;
|
||||
final Function() funGallery;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 120,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(26),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildItem('Open camera', funCamera),
|
||||
),
|
||||
const Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
color: Color(0xFFEDEDED),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildItem('Open gallery', funGallery),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(String text, Function() function) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
function();
|
||||
},
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF333333),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
253
lib/common/components/pin_code_verification_screen.dart
Normal file
253
lib/common/components/pin_code_verification_screen.dart
Normal file
@ -0,0 +1,253 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
import 'package:wallpaperx/common/utils/shared_util.dart';
|
||||
import 'package:wallpaperx/generated/assets.dart';
|
||||
|
||||
class PinCodeVerificationScreen extends StatefulWidget {
|
||||
final Function callback;
|
||||
final String checkPassword;
|
||||
|
||||
const PinCodeVerificationScreen({
|
||||
super.key,
|
||||
required this.callback,
|
||||
required this.checkPassword,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PinCodeVerificationScreen> createState() =>
|
||||
_PinCodeVerificationScreenState();
|
||||
}
|
||||
|
||||
class _PinCodeVerificationScreenState extends State<PinCodeVerificationScreen> {
|
||||
var onTapRecognizer;
|
||||
|
||||
TextEditingController textEditingController = TextEditingController();
|
||||
|
||||
late StreamController<ErrorAnimationType> errorController;
|
||||
|
||||
bool hasError = false;
|
||||
String currentText = "";
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
onTapRecognizer = TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
errorController = StreamController<ErrorAnimationType>();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
errorController.close();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
key: scaffoldKey,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 94.w),
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Please input a',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 28.sp,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'password',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 28.sp,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15.r),
|
||||
),
|
||||
child: Image.asset(
|
||||
Assets.imagesCustomLock,
|
||||
width: 100.w,
|
||||
height: 100.w,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 64.w),
|
||||
Form(
|
||||
key: formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 30,
|
||||
).w,
|
||||
child: PinCodeTextField(
|
||||
appContext: context,
|
||||
pastedTextStyle: TextStyle(
|
||||
color: Colors.green.shade600,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
dialogConfig: DialogConfig(platform: PinCodePlatform.iOS),
|
||||
length: 4,
|
||||
obscureText: true,
|
||||
obscuringCharacter: '*',
|
||||
animationType: AnimationType.fade,
|
||||
pinTheme: PinTheme(
|
||||
activeColor: const Color(0xff262626),
|
||||
selectedColor: const Color(0xff262626),
|
||||
selectedFillColor: Colors.grey,
|
||||
inactiveFillColor: const Color(0xff262626),
|
||||
errorBorderColor: const Color(0xff262626),
|
||||
inactiveColor: const Color(0xff262626),
|
||||
shape: PinCodeFieldShape.box,
|
||||
borderRadius: BorderRadius.circular(30.r),
|
||||
fieldHeight: 58.w,
|
||||
fieldWidth: 58.w,
|
||||
activeFillColor: hasError
|
||||
? const Color(0xff262626)
|
||||
: const Color(0xff262626),
|
||||
),
|
||||
showCursor: false,
|
||||
animationDuration: const Duration(milliseconds: 300),
|
||||
textStyle: TextStyle(
|
||||
fontSize: 32.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
height: 2.0,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
enableActiveFill: true,
|
||||
errorAnimationController: errorController,
|
||||
controller: textEditingController,
|
||||
keyboardType: TextInputType.number,
|
||||
boxShadows: const [
|
||||
BoxShadow(
|
||||
offset: Offset(0, 1),
|
||||
color: Colors.black12,
|
||||
blurRadius: 30,
|
||||
),
|
||||
],
|
||||
onCompleted: (v) {},
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
currentText = value;
|
||||
});
|
||||
},
|
||||
beforeTextPaste: (text) {
|
||||
setState(() {
|
||||
currentText = text!;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
)),
|
||||
),
|
||||
Visibility(
|
||||
visible: widget.checkPassword == "",
|
||||
child: Text(
|
||||
"First input as password",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
formKey.currentState?.validate();
|
||||
if (currentText.length == 4 && widget.checkPassword == "") {
|
||||
UPCache.getInstance()
|
||||
.setData("custom_password", currentText);
|
||||
widget.callback();
|
||||
} else if (currentText.length != 4 ||
|
||||
currentText != widget.checkPassword) {
|
||||
errorController.add(ErrorAnimationType
|
||||
.shake); // Triggering error shake animation
|
||||
setState(() {
|
||||
hasError = true;
|
||||
textEditingController.clear();
|
||||
});
|
||||
} else {
|
||||
widget.callback();
|
||||
setState(() {
|
||||
hasError = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 24,
|
||||
horizontal: 40,
|
||||
).w,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 17,
|
||||
).w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(30.r),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
colors: [
|
||||
Color(0xffBEEF32),
|
||||
Color(0xff2795E5),
|
||||
Color(0xff8041FD),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Confirm",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
child: Text(
|
||||
"Clear",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.sp,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
textEditingController.clear();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
41
lib/common/storage/custom_data.dart
Normal file
41
lib/common/storage/custom_data.dart
Normal file
@ -0,0 +1,41 @@
|
||||
import 'package:wallpaperx/common/storage/hive_storage.dart';
|
||||
import 'package:wallpaperx/entity/image_model.dart';
|
||||
|
||||
class CustomData {
|
||||
/// 私有构造函数
|
||||
CustomData._();
|
||||
|
||||
/// 静态常量用于保存类的唯一实例
|
||||
static final CustomData _instance = CustomData._();
|
||||
|
||||
/// 工厂构造函数返回类的唯一实例
|
||||
factory CustomData() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// 声明盒子
|
||||
/// 注意, main函数中这个盒子已经打开, 可以进行存储操作
|
||||
final _box = getCustomBox();
|
||||
|
||||
/// 获取壁纸
|
||||
List<ImageModel> getWallpaperData() {
|
||||
return _box.values.toList();
|
||||
}
|
||||
|
||||
/// 存储壁纸
|
||||
Future<int> setWallpaperData(ImageModel wallpaperData) async {
|
||||
return await _box.add(wallpaperData);
|
||||
}
|
||||
|
||||
/// 删除壁纸
|
||||
Future<void> delete(index) async {
|
||||
await _box.deleteAt(index);
|
||||
await _box.flush();
|
||||
}
|
||||
|
||||
/// 删除所有壁纸
|
||||
Future<void> clear() async {
|
||||
await _box.clear();
|
||||
await _box.flush();
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import 'package:wallpaperx/entity/tags_model.dart';
|
||||
|
||||
const favoriteBox = 'favoriteBox';
|
||||
const historyBox = 'historyBox';
|
||||
const customBox = 'customBox';
|
||||
|
||||
Future initHive() async {
|
||||
// 初始化
|
||||
@ -16,6 +17,7 @@ Future initHive() async {
|
||||
// 打开盒子
|
||||
await Hive.openBox<ImageModel>(favoriteBox);
|
||||
await Hive.openBox<ImageModel>(historyBox);
|
||||
await Hive.openBox<ImageModel>(customBox);
|
||||
}
|
||||
|
||||
/// 获取盒子
|
||||
@ -27,3 +29,8 @@ Box<ImageModel> getFavoriteBox() {
|
||||
Box<ImageModel> getHistoryBox() {
|
||||
return Hive.box<ImageModel>(historyBox);
|
||||
}
|
||||
|
||||
/// 获取盒子
|
||||
Box<ImageModel> getCustomBox() {
|
||||
return Hive.box<ImageModel>(customBox);
|
||||
}
|
||||
@ -46,6 +46,8 @@ class Assets {
|
||||
static const String iconUnFavorite = 'assets/icon/un_favorite.png';
|
||||
static const String iconUp = 'assets/icon/up.png';
|
||||
static const String imagesCollectionSelected = 'assets/images/collection_selected.png';
|
||||
static const String imagesCustomLock = 'assets/images/custom_lock.png';
|
||||
static const String imagesCustomSelected = 'assets/images/custom_selected.png';
|
||||
static const String imagesRecommendBottomBackground = 'assets/images/recommend_bottom_background.png';
|
||||
static const String imagesRecommendSelected = 'assets/images/recommend_selected.png';
|
||||
static const String imagesRecommendTopBackground = 'assets/images/recommend_top_background.png';
|
||||
|
||||
230
lib/page/custom/custom_controller.dart
Normal file
230
lib/page/custom/custom_controller.dart
Normal file
@ -0,0 +1,230 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crop_your_image/crop_your_image.dart';
|
||||
import 'package:flip_card/flip_card_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:wallpaperx/common/components/dialog/remind_dialog.dart';
|
||||
import 'package:wallpaperx/common/components/easy_loading.dart';
|
||||
import 'package:wallpaperx/common/components/navigation_bar/custom_appbar.dart';
|
||||
import 'package:wallpaperx/common/components/photo_picker_bottom_sheet.dart';
|
||||
import 'package:wallpaperx/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaperx/common/storage/custom_data.dart';
|
||||
import 'package:wallpaperx/common/utils/device_info_util.dart';
|
||||
import 'package:wallpaperx/common/utils/log_print.dart';
|
||||
import 'package:wallpaperx/common/utils/permission_util.dart';
|
||||
import 'package:wallpaperx/common/utils/shared_util.dart';
|
||||
import 'package:wallpaperx/entity/image_model.dart';
|
||||
import 'package:wallpaperx/routes/app_pages.dart';
|
||||
|
||||
class CustomController extends GetxController {
|
||||
static CustomController get to => Get.find<CustomController>();
|
||||
late ScrollController scrollController;
|
||||
late ViewState viewState;
|
||||
RxList customList = [].obs;
|
||||
|
||||
String password = "";
|
||||
|
||||
TextEditingController textEditingController = TextEditingController();
|
||||
|
||||
late StreamController<ErrorAnimationType> errorController;
|
||||
|
||||
late FlipCardController flipCardController;
|
||||
|
||||
bool hasError = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
errorController = StreamController<ErrorAnimationType>();
|
||||
scrollController = ScrollController();
|
||||
flipCardController = FlipCardController();
|
||||
password = UPCache.getInstance().get("custom_password")??"";
|
||||
getCustomList();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
scrollController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void getCustomList() {
|
||||
customList.clear();
|
||||
customList.addAll(CustomData().getWallpaperData().reversed.toList());
|
||||
LogPrint.d(customList.length);
|
||||
refreshCustomList();
|
||||
}
|
||||
|
||||
void refreshCustomList() {
|
||||
viewState = customList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/// 点击壁纸
|
||||
void toImageDetail(int position) {
|
||||
Get.toNamed(AppPages.wallpaperDetail, arguments: {
|
||||
'position': position,
|
||||
'isSetHistory': false,
|
||||
'wallpaperList': customList,
|
||||
});
|
||||
}
|
||||
|
||||
/// 长按壁纸
|
||||
void onLongPressImage(int position) {
|
||||
Get.dialog(
|
||||
barrierDismissible: false,
|
||||
RemindDialog(
|
||||
content: 'Are you sure you want to delete the record?',
|
||||
confirmOnTap: () {
|
||||
// 计算原始列表中的对应索引
|
||||
int indexToRemoveFromDb =
|
||||
CustomData().getWallpaperData().length - 1 - position;
|
||||
CustomData().delete(indexToRemoveFromDb);
|
||||
customList.removeAt(position);
|
||||
refreshCustomList();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> toPhotos() async {
|
||||
Get.bottomSheet(
|
||||
isScrollControlled: true,
|
||||
TPhotoPickerBottomSheet(
|
||||
funCamera: () async {
|
||||
bool result =
|
||||
await PermissionUtil.checkPermission([Permission.camera]);
|
||||
if (!result) return;
|
||||
_openCameraGallery(ImageSource.camera);
|
||||
},
|
||||
funGallery: () async {
|
||||
Permission permission = Permission.photos;
|
||||
if (Platform.isAndroid) {
|
||||
int sdkInt = await DeviceInfoUtil.getAndroidSystemVersion();
|
||||
if (sdkInt <= 32) {
|
||||
permission = Permission.storage;
|
||||
} else {
|
||||
permission = Permission.photos;
|
||||
}
|
||||
}
|
||||
bool result = await PermissionUtil.checkPermission([permission]);
|
||||
if (!result) return;
|
||||
_openCameraGallery(ImageSource.gallery);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openCameraGallery(ImageSource source) async {
|
||||
final ImagePicker picker = ImagePicker();
|
||||
final XFile? photo = await picker.pickImage(source: source);
|
||||
if (photo != null) {
|
||||
final controller = CropController();
|
||||
Uint8List image = await photo.readAsBytes();
|
||||
Get.to(
|
||||
() => Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: const CustomAppbar(''),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Crop(
|
||||
baseColor: Colors.black,
|
||||
image: image,
|
||||
controller: controller,
|
||||
onCropped: (image) {
|
||||
setFile(image);
|
||||
LogPrint.d('裁剪后的图片:${image.lengthInBytes}');
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Container()),
|
||||
GestureDetector(
|
||||
onTap: controller.crop,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 46,
|
||||
).w,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 35,
|
||||
vertical: 8,
|
||||
).w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(30.r),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
colors: [
|
||||
Color(0xffBEEF32),
|
||||
Color(0xff2795E5),
|
||||
Color(0xff8041FD),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Apply",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future setFile(Uint8List image) async {
|
||||
try {
|
||||
String uuid = const Uuid().v4();
|
||||
Directory directory = await getApplicationDocumentsDirectory();
|
||||
File file = File('${directory.path}/$uuid.txt');
|
||||
// 保存到文件系统
|
||||
await file.writeAsBytes(image);
|
||||
// 询问用户是否保存它
|
||||
ImageModel model = ImageModel(imageUrl: file.path);
|
||||
CustomData().setWallpaperData(model);
|
||||
Get.back();
|
||||
getCustomList();
|
||||
} catch (e) {
|
||||
toast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List? readImage(String filePath) {
|
||||
try {
|
||||
final file = File(filePath);
|
||||
Uint8List u8 = file.readAsBytesSync();
|
||||
return u8;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 翻转
|
||||
void flipCard() {
|
||||
flipCardController.toggleCard();
|
||||
}
|
||||
}
|
||||
129
lib/page/custom/custom_view.dart
Normal file
129
lib/page/custom/custom_view.dart
Normal file
@ -0,0 +1,129 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flip_card/flip_card.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaperx/common/components/navigation_bar/custom_appbar.dart';
|
||||
import 'package:wallpaperx/common/components/pin_code_verification_screen.dart';
|
||||
import 'package:wallpaperx/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaperx/entity/image_model.dart';
|
||||
import 'package:wallpaperx/generated/assets.dart';
|
||||
|
||||
import 'custom_controller.dart';
|
||||
|
||||
class CustomView extends GetView<CustomController> {
|
||||
const CustomView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(CustomController());
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: FlipCard(
|
||||
flipOnTouch: false,
|
||||
controller: controller.flipCardController,
|
||||
fill: Fill.fillBack,
|
||||
side: CardSide.FRONT,
|
||||
direction: FlipDirection.HORIZONTAL,
|
||||
front: PinCodeVerificationScreen(
|
||||
callback: controller.flipCard,
|
||||
checkPassword: controller.password,
|
||||
),
|
||||
back: _buildCustomWidget(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCustomWidget(context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(Assets.imagesSettingBackground),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomAppbar(
|
||||
"Custom",
|
||||
backgroundColor: Colors.transparent,
|
||||
backWidget: Container(width: 32.w),
|
||||
titleStyle: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Expanded(child: GetBuilder<CustomController>(
|
||||
builder: (logic) {
|
||||
return ViewStateWidget(
|
||||
viewState: controller.viewState,
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeTop: true,
|
||||
child: Scrollbar(
|
||||
controller: controller.scrollController,
|
||||
child: MasonryGridView.count(
|
||||
controller: controller.scrollController,
|
||||
itemCount: controller.customList.length,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 15.w,
|
||||
crossAxisSpacing: 15.w,
|
||||
padding: const EdgeInsets.fromLTRB(15, 20, 15, 0).w,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
ImageModel item = controller.customList[index];
|
||||
Uint8List? image =
|
||||
controller.readImage(item.imageUrl ?? "");
|
||||
if (image == null) return Container();
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(
|
||||
() => Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: const CustomAppbar(''),
|
||||
body: Container(
|
||||
alignment: Alignment.center,
|
||||
child: Image.memory(image),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(13.r),
|
||||
),
|
||||
child: Image.memory(image),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 10.w,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80.w,
|
||||
child: FloatingActionButton(
|
||||
backgroundColor: Colors.grey,
|
||||
onPressed: controller.toPhotos,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import 'package:wallpaperx/common/utils/shared_util.dart';
|
||||
import 'package:wallpaperx/config/app_tracking_transparency_manager.dart';
|
||||
import 'package:wallpaperx/entity/userinfo_model.dart';
|
||||
import 'package:wallpaperx/generated/assets.dart';
|
||||
import 'package:wallpaperx/page/custom/custom_view.dart';
|
||||
import 'package:wallpaperx/page/library/library_view.dart';
|
||||
import 'package:wallpaperx/page/recommend/recommend_view.dart';
|
||||
import 'package:wallpaperx/page/settings/settings_view.dart';
|
||||
@ -15,6 +16,7 @@ class HomeController extends GetxController with WidgetsBindingObserver {
|
||||
final pages = [
|
||||
PageItem(Assets.imagesRecommendSelected, const RecommendView()),
|
||||
PageItem(Assets.imagesCollectionSelected, const LibraryView()),
|
||||
PageItem(Assets.imagesCustomSelected, const CustomView()),
|
||||
PageItem(Assets.imagesSettingSelected, const SettingsView()),
|
||||
];
|
||||
late PageController pageController;
|
||||
|
||||
@ -85,9 +85,7 @@ class WallpaperDetailController extends GetxController {
|
||||
filePath = savePath;
|
||||
bool canSave = true;
|
||||
if (Platform.isIOS) {
|
||||
canSave = await PermissionUtil.checkPermission(
|
||||
[Permission.photosAddOnly],
|
||||
);
|
||||
canSave = await PermissionUtil.checkPermission([Permission.photos]);
|
||||
}
|
||||
if (canSave) {
|
||||
final result =
|
||||
|
||||
@ -101,6 +101,11 @@ dependencies:
|
||||
app_tracking_transparency: ^2.0.5
|
||||
gradient_borders: ^1.0.1
|
||||
stacked_animated_list: ^1.0.1
|
||||
pin_code_fields: ^8.0.1
|
||||
image_picker: ^1.1.2
|
||||
uuid: ^4.4.2
|
||||
flutter_file_dialog: ^3.0.2
|
||||
crop_your_image: ^1.1.0
|
||||
|
||||
# Firebase
|
||||
firebase_core: ^2.32.0
|
||||
|
||||
Loading…
Reference in New Issue
Block a user