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'] ||= [
|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||||
'$(inherited)',
|
'$(inherited)',
|
||||||
|
|
||||||
|
## dart: PermissionGroup.camera
|
||||||
|
'PERMISSION_CAMERA=1',
|
||||||
|
|
||||||
## dart: PermissionGroup.photos
|
## dart: PermissionGroup.photos
|
||||||
'PERMISSION_PHOTOS=1',
|
'PERMISSION_PHOTOS=1',
|
||||||
|
|
||||||
## dart:PermissionGroup.photosAddOnly
|
|
||||||
'PERMISSION_PHOTOS_ADD_ONLY=1',
|
|
||||||
|
|
||||||
## dart: PermissionGroup.appTrackingTransparency
|
## dart: PermissionGroup.appTrackingTransparency
|
||||||
'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
|
'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
|
||||||
]
|
]
|
||||||
|
|||||||
@ -31,6 +31,8 @@
|
|||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>This will enable you to take photos and recognize text within them for translation.</string>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<string>We need access to your photo album so you can save wallpapers from the app to your album</string>
|
<string>We need access to your photo album so you can save wallpapers from the app to your album</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: preferredSize.height,
|
height: preferredSize.height,
|
||||||
padding: EdgeInsets.fromLTRB(10, ScreenUtil().statusBarHeight, 20, 0).w,
|
padding: EdgeInsets.fromLTRB(10, ScreenUtil().statusBarHeight, 10, 0).w,
|
||||||
color: backgroundColor ?? seedColor,
|
color: backgroundColor ?? seedColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -35,14 +35,11 @@ class CustomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onBackTap ?? () => Get.back(),
|
onTap: onBackTap ?? () => Get.back(),
|
||||||
child: Padding(
|
child: Image.asset(
|
||||||
padding: const EdgeInsets.all(10).w,
|
Assets.iconBack,
|
||||||
child: Image.asset(
|
width: 32.w,
|
||||||
Assets.iconBack,
|
height: 32.w,
|
||||||
width: 32.w,
|
color: Colors.white,
|
||||||
height: 32.w,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -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 favoriteBox = 'favoriteBox';
|
||||||
const historyBox = 'historyBox';
|
const historyBox = 'historyBox';
|
||||||
|
const customBox = 'customBox';
|
||||||
|
|
||||||
Future initHive() async {
|
Future initHive() async {
|
||||||
// 初始化
|
// 初始化
|
||||||
@ -16,6 +17,7 @@ Future initHive() async {
|
|||||||
// 打开盒子
|
// 打开盒子
|
||||||
await Hive.openBox<ImageModel>(favoriteBox);
|
await Hive.openBox<ImageModel>(favoriteBox);
|
||||||
await Hive.openBox<ImageModel>(historyBox);
|
await Hive.openBox<ImageModel>(historyBox);
|
||||||
|
await Hive.openBox<ImageModel>(customBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取盒子
|
/// 获取盒子
|
||||||
@ -26,4 +28,9 @@ Box<ImageModel> getFavoriteBox() {
|
|||||||
/// 获取盒子
|
/// 获取盒子
|
||||||
Box<ImageModel> getHistoryBox() {
|
Box<ImageModel> getHistoryBox() {
|
||||||
return Hive.box<ImageModel>(historyBox);
|
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 iconUnFavorite = 'assets/icon/un_favorite.png';
|
||||||
static const String iconUp = 'assets/icon/up.png';
|
static const String iconUp = 'assets/icon/up.png';
|
||||||
static const String imagesCollectionSelected = 'assets/images/collection_selected.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 imagesRecommendBottomBackground = 'assets/images/recommend_bottom_background.png';
|
||||||
static const String imagesRecommendSelected = 'assets/images/recommend_selected.png';
|
static const String imagesRecommendSelected = 'assets/images/recommend_selected.png';
|
||||||
static const String imagesRecommendTopBackground = 'assets/images/recommend_top_background.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/config/app_tracking_transparency_manager.dart';
|
||||||
import 'package:wallpaperx/entity/userinfo_model.dart';
|
import 'package:wallpaperx/entity/userinfo_model.dart';
|
||||||
import 'package:wallpaperx/generated/assets.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/library/library_view.dart';
|
||||||
import 'package:wallpaperx/page/recommend/recommend_view.dart';
|
import 'package:wallpaperx/page/recommend/recommend_view.dart';
|
||||||
import 'package:wallpaperx/page/settings/settings_view.dart';
|
import 'package:wallpaperx/page/settings/settings_view.dart';
|
||||||
@ -15,6 +16,7 @@ class HomeController extends GetxController with WidgetsBindingObserver {
|
|||||||
final pages = [
|
final pages = [
|
||||||
PageItem(Assets.imagesRecommendSelected, const RecommendView()),
|
PageItem(Assets.imagesRecommendSelected, const RecommendView()),
|
||||||
PageItem(Assets.imagesCollectionSelected, const LibraryView()),
|
PageItem(Assets.imagesCollectionSelected, const LibraryView()),
|
||||||
|
PageItem(Assets.imagesCustomSelected, const CustomView()),
|
||||||
PageItem(Assets.imagesSettingSelected, const SettingsView()),
|
PageItem(Assets.imagesSettingSelected, const SettingsView()),
|
||||||
];
|
];
|
||||||
late PageController pageController;
|
late PageController pageController;
|
||||||
|
|||||||
@ -85,9 +85,7 @@ class WallpaperDetailController extends GetxController {
|
|||||||
filePath = savePath;
|
filePath = savePath;
|
||||||
bool canSave = true;
|
bool canSave = true;
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
canSave = await PermissionUtil.checkPermission(
|
canSave = await PermissionUtil.checkPermission([Permission.photos]);
|
||||||
[Permission.photosAddOnly],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (canSave) {
|
if (canSave) {
|
||||||
final result =
|
final result =
|
||||||
|
|||||||
@ -101,6 +101,11 @@ dependencies:
|
|||||||
app_tracking_transparency: ^2.0.5
|
app_tracking_transparency: ^2.0.5
|
||||||
gradient_borders: ^1.0.1
|
gradient_borders: ^1.0.1
|
||||||
stacked_animated_list: ^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
|
||||||
firebase_core: ^2.32.0
|
firebase_core: ^2.32.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user