1.按照UI图修改
2.完善其他功能
@ -27,7 +27,7 @@ def keystoreProperties = new Properties()
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
android {
|
||||
namespace "com.lux.wallpaper"
|
||||
namespace "com.now.wallpaper"
|
||||
compileSdk flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
@ -46,10 +46,11 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.lux.wallpaper"
|
||||
applicationId "com.now.wallpaper"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
// minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 24
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
@ -5,14 +5,18 @@
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32"
|
||||
tools:ignore="ScopedStorage" />
|
||||
tools:ignore="ScopedStorage"
|
||||
/>
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
android:maxSdkVersion="32"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
|
||||
<application
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
<activity
|
||||
@ -40,6 +44,18 @@
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
|
||||
<!-- Provider -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility?hl=en and
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
package com.lux.wallpaper
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
@ -0,0 +1,20 @@
|
||||
package com.now.wallpaper
|
||||
|
||||
import io.flutter.Log
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
try {
|
||||
flutterEngine.plugins.add(SetWallpaperPlugin())
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
"GeneratedPluginRegistrant",
|
||||
"Error registering plugin set_wallpaper, com.now.wallpaper.SetWallpaperPlugin",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.now.wallpaper
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SetWallpaperPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
|
||||
private lateinit var context: Context
|
||||
private lateinit var channel: MethodChannel
|
||||
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "set_wallpaper")
|
||||
context = flutterPluginBinding.applicationContext
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
||||
if (call.method == "setWallpaper") {
|
||||
val path = call.argument<String>("path")
|
||||
val wallpaperType = call.argument<Int>("wallpaperType") // 0 for home screen, 1 for lock screen, 2 for both
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val bitmap = BitmapFactory.decodeFile(path)
|
||||
withContext(Dispatchers.Main) {
|
||||
try {
|
||||
val wallpaperManager = WallpaperManager.getInstance(context)
|
||||
when (wallpaperType) {
|
||||
0 -> wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_SYSTEM)
|
||||
1 -> wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_LOCK)
|
||||
2 -> {
|
||||
wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_SYSTEM)
|
||||
wallpaperManager.setBitmap(bitmap, null, true, WallpaperManager.FLAG_LOCK)
|
||||
}
|
||||
}
|
||||
result.success("Wallpaper applied successfully")
|
||||
} catch (e: Exception) {
|
||||
result.error("ERROR", "Failed to set wallpaper: ${e.message}", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--<item android:drawable="?android:colorBackground" />-->
|
||||
<item android:drawable="@android:color/white" />
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
android:src="@mipmap/launcher_icon" />
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
@ -6,6 +6,6 @@
|
||||
<item>¬
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
android:src="@mipmap/launcher_icon" />
|
||||
</item>
|
||||
</layer-list>
|
||||
|
||||
BIN
android/app/src/main/res/mipmap-hdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
@ -5,6 +5,10 @@
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wallpaper</string>
|
||||
<string name="app_name">Now wallpaper</string>
|
||||
</resources>
|
||||
@ -5,6 +5,10 @@
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
|
||||
8
android/app/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<files-path name="files" path="." />
|
||||
<cache-path name="cache" path="." />
|
||||
<external-files-path name="external-files" path="." />
|
||||
<external-cache-path name="external-cache" path="." />
|
||||
<external-media-path name="external-media" path="." />
|
||||
</paths>
|
||||
@ -20,7 +20,7 @@ pluginManagement {
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "7.3.0" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
|
||||
BIN
assets/icon/icon_app.jpeg
Normal file
|
After Width: | Height: | Size: 913 KiB |
BIN
assets/images/catalog_selected.png
Executable file
|
After Width: | Height: | Size: 730 B |
BIN
assets/images/catalog_unchecked.png
Executable file
|
After Width: | Height: | Size: 572 B |
BIN
assets/images/discover_selected.png
Executable file
|
After Width: | Height: | Size: 494 B |
BIN
assets/images/discover_unchecked.png
Executable file
|
After Width: | Height: | Size: 506 B |
BIN
assets/images/favorite_selected.png
Executable file
|
After Width: | Height: | Size: 479 B |
BIN
assets/images/favorite_unchecked.png
Executable file
|
After Width: | Height: | Size: 487 B |
BIN
assets/images/ic_back.png
Executable file
|
After Width: | Height: | Size: 373 B |
BIN
assets/images/ic_download.png
Executable file
|
After Width: | Height: | Size: 772 B |
BIN
assets/images/ic_favorite.png
Executable file
|
After Width: | Height: | Size: 738 B |
BIN
assets/images/ic_filtered.png
Executable file
|
After Width: | Height: | Size: 663 B |
BIN
assets/images/ic_preview.png
Executable file
|
After Width: | Height: | Size: 400 B |
BIN
assets/images/ic_setting_arrow.png
Normal file
|
After Width: | Height: | Size: 286 B |
BIN
assets/images/ic_settings.png
Executable file
|
After Width: | Height: | Size: 654 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@ -496,7 +496,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lux.wallpaper;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.now.wallpaper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -558,7 +558,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@ -616,7 +616,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@ -681,7 +681,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lux.wallpaper;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.now.wallpaper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@ -704,7 +704,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lux.wallpaper;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.now.wallpaper;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 911 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 1.8 MiB |
@ -2,10 +2,12 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Wallpaper</string>
|
||||
<string>Now wallpaper</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@ -24,6 +26,12 @@
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>To save wallpapers to gallery, we need this permission</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>To save wallpapers to gallery, we need this permission</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
@ -41,11 +49,5 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Please allow the APP to save photos to the album</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/7
|
||||
// Description: BaseAppBar
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class BaseAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const BaseAppBar(
|
||||
this.titleStr, {
|
||||
super.key,
|
||||
this.height,
|
||||
this.backgroundColor,
|
||||
this.titleStyle,
|
||||
this.title,
|
||||
this.iconColor,
|
||||
this.showLeading = true,
|
||||
this.leading,
|
||||
this.leadingOnPressed,
|
||||
this.actions,
|
||||
this.systemOverlayStyle,
|
||||
this.bottom,
|
||||
});
|
||||
|
||||
final double? height;
|
||||
final Color? backgroundColor;
|
||||
final String titleStr;
|
||||
final TextStyle? titleStyle;
|
||||
final Widget? title;
|
||||
final Color? iconColor;
|
||||
final bool showLeading;
|
||||
final Widget? leading;
|
||||
final Function()? leadingOnPressed;
|
||||
final List<Widget>? actions;
|
||||
final SystemUiOverlayStyle? systemOverlayStyle;
|
||||
final PreferredSizeWidget? bottom;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget titleWidget = title ??
|
||||
Text(
|
||||
titleStr,
|
||||
style: titleStyle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
Widget? leadingWidget = showLeading
|
||||
? (leading ??
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios_rounded,
|
||||
color: iconColor,
|
||||
),
|
||||
onPressed: leadingOnPressed ?? Get.back,
|
||||
))
|
||||
: null;
|
||||
return AppBar(
|
||||
title: titleWidget,
|
||||
backgroundColor: backgroundColor,
|
||||
systemOverlayStyle: systemOverlayStyle,
|
||||
leading: leadingWidget,
|
||||
actions: actions,
|
||||
bottom: bottom,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(height ?? kToolbarHeight);
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/11
|
||||
// Description: 瀑布流
|
||||
|
||||
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:wallpaper/common/components/image_widget.dart';
|
||||
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
|
||||
class BaseMasonryGridView extends StatelessWidget {
|
||||
const BaseMasonryGridView({
|
||||
super.key,
|
||||
this.viewState = ViewState.normal,
|
||||
required this.wallpaperList,
|
||||
required this.itemOnTap,
|
||||
this.onLongPress,
|
||||
});
|
||||
|
||||
final List<WallpaperData> wallpaperList;
|
||||
final Function(int index) itemOnTap;
|
||||
final Function(int index)? onLongPress;
|
||||
final ViewState? viewState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewStateWidget(
|
||||
state: viewState!,
|
||||
child: MasonryGridView.count(
|
||||
itemCount: wallpaperList.length,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 8.w,
|
||||
crossAxisSpacing: 8.w,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8).w,
|
||||
itemBuilder: (context, index) {
|
||||
return _wallpaperItem(wallpaperList[index], index);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _wallpaperItem(WallpaperData item, index) {
|
||||
return InkWell(
|
||||
onTap: () => itemOnTap(index),
|
||||
borderRadius: BorderRadius.circular(8).r,
|
||||
onLongPress: onLongPress != null ? () => onLongPress!(index) : null,
|
||||
child: ImageWidget(
|
||||
url: item.previewThumb,
|
||||
fit: BoxFit.contain,
|
||||
radius: 8.r,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
60
lib/common/components/button/base_textbutton.dart
Normal file
@ -0,0 +1,60 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/7
|
||||
// Description: BaseTextButton
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:now_wallpaper/res/themes/app_styles.dart';
|
||||
|
||||
class BaseTextButton extends StatelessWidget {
|
||||
final double? width;
|
||||
final double? height;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final Color? bgColor;
|
||||
final double? radius;
|
||||
final String? label;
|
||||
final TextStyle? textStyle;
|
||||
final Widget? child;
|
||||
final Function()? onTap;
|
||||
|
||||
const BaseTextButton({
|
||||
super.key,
|
||||
this.label,
|
||||
this.width,
|
||||
this.height,
|
||||
this.buttonStyle,
|
||||
this.bgColor,
|
||||
this.radius,
|
||||
this.textStyle,
|
||||
this.child,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: TextButton(
|
||||
onPressed: onTap,
|
||||
style: buttonStyle ??
|
||||
baseTextButtonStyle(
|
||||
bgColor: bgColor,
|
||||
radius: radius,
|
||||
),
|
||||
child: child ??
|
||||
Text(
|
||||
label ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textStyle ??
|
||||
TextStyle(
|
||||
color: const Color(0xFF333333),
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
53
lib/common/components/dialog/bottomsheet_dialog.dart
Normal file
@ -0,0 +1,53 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/16
|
||||
// Description: BottomSheetDialog
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:now_wallpaper/common/components/button/base_textbutton.dart';
|
||||
import 'package:now_wallpaper/common/components/divider_widget.dart';
|
||||
|
||||
class BottomSheetDialog {
|
||||
static void show(void Function(int location) function) {
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
color: Colors.white,
|
||||
height: 150.h,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BaseTextButton(
|
||||
label: 'Home Screen',
|
||||
onTap: () {
|
||||
Get.back();
|
||||
function(0);
|
||||
},
|
||||
),
|
||||
),
|
||||
const DividerWidget(),
|
||||
Expanded(
|
||||
child: BaseTextButton(
|
||||
label: 'Lock Screen',
|
||||
onTap: () {
|
||||
Get.back();
|
||||
function(1);
|
||||
},
|
||||
),
|
||||
),
|
||||
const DividerWidget(),
|
||||
Expanded(
|
||||
child: BaseTextButton(
|
||||
label: 'Both Screen',
|
||||
onTap: () {
|
||||
Get.back();
|
||||
function(2);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaper/common/utils/obj_util.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:now_wallpaper/common/utils/obj_util.dart';
|
||||
|
||||
class ProcessDialog extends StatelessWidget {
|
||||
final double process;
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/divider_widget.dart';
|
||||
import 'package:now_wallpaper/common/components/divider_widget.dart';
|
||||
|
||||
class HintDialog extends StatelessWidget {
|
||||
const HintDialog({
|
||||
class RemindDialog extends StatelessWidget {
|
||||
const RemindDialog({
|
||||
super.key,
|
||||
this.title,
|
||||
this.content,
|
||||
@ -55,7 +55,7 @@ class HintDialog extends StatelessWidget {
|
||||
content ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.black54,
|
||||
color: const Color(0xFF333333),
|
||||
fontSize: 15.sp,
|
||||
),
|
||||
),
|
||||
@ -103,7 +103,7 @@ class HintDialog extends StatelessWidget {
|
||||
label,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: isConfirm ? Colors.black : Colors.black45,
|
||||
color: isConfirm ? Colors.black : const Color(0xFF666666),
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:wallpaper/common/utils/obj_util.dart';
|
||||
|
||||
/// EasyLoading配置
|
||||
void configLoading() {
|
||||
@ -34,7 +33,7 @@ void configLoading() {
|
||||
// 指示器的宽度, 默认4.0, 仅对[EasyLoadingIndicatorType.ring, EasyLoadingIndicatorType.dualRing]有效.
|
||||
..lineWidth = 2.0
|
||||
// [showSuccess] [showError] [showInfo]的展示时间, 默认2000ms.
|
||||
..displayDuration = const Duration(milliseconds: 2000)
|
||||
..displayDuration = const Duration(milliseconds: 1000)
|
||||
// 动画时间, 默认200ms.
|
||||
..animationDuration = const Duration(milliseconds: 200)
|
||||
// 文本的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
||||
@ -54,8 +53,8 @@ void configLoading() {
|
||||
}
|
||||
|
||||
void toast(String? value, {bool show = true}) {
|
||||
if (show && ObjUtil.isNotEmptyStr(value)) {
|
||||
EasyLoading.showToast('$value');
|
||||
if (show && value != null) {
|
||||
EasyLoading.showToast(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
lib/common/components/image_network_widget.dart
Normal file
@ -0,0 +1,57 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/9
|
||||
// Description: 加载网络图像
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:now_wallpaper/generated/assets.dart';
|
||||
|
||||
class ImageNetworkWidget extends StatelessWidget {
|
||||
const ImageNetworkWidget({
|
||||
super.key,
|
||||
this.width,
|
||||
this.height,
|
||||
this.radius = 0.0,
|
||||
required this.url,
|
||||
this.fit = BoxFit.cover,
|
||||
this.placeholder,
|
||||
this.errorWidget,
|
||||
});
|
||||
|
||||
final double? width;
|
||||
final double? height;
|
||||
final double radius;
|
||||
final String? url;
|
||||
final BoxFit fit;
|
||||
final Widget? placeholder;
|
||||
final Widget? errorWidget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
child: CachedNetworkImage(
|
||||
width: width,
|
||||
height: height,
|
||||
imageUrl: '$url',
|
||||
fit: fit,
|
||||
placeholder: (context, url) {
|
||||
return placeholder ?? _placeholderWidget(Assets.imagesImgPlaceholder);
|
||||
},
|
||||
errorWidget: (context, url, error) {
|
||||
return errorWidget ?? _placeholderWidget(Assets.imagesImgError);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _placeholderWidget(String imgName) {
|
||||
return Container(
|
||||
color: Colors.white10,
|
||||
child: Image.asset(
|
||||
imgName,
|
||||
color: Colors.white12,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/9
|
||||
// Description: 加载网络图像
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wallpaper/generated/assets.dart';
|
||||
|
||||
class ImageWidget extends StatelessWidget {
|
||||
const ImageWidget({
|
||||
super.key,
|
||||
this.width,
|
||||
this.height,
|
||||
this.radius = 0.0,
|
||||
this.url,
|
||||
this.filePath,
|
||||
this.isLocal = false,
|
||||
this.fit = BoxFit.cover,
|
||||
this.placeholder,
|
||||
this.errorWidget,
|
||||
});
|
||||
|
||||
final double? width, height;
|
||||
final double radius;
|
||||
final String? url;
|
||||
final String? filePath;
|
||||
final bool isLocal;
|
||||
final BoxFit fit;
|
||||
final Widget? placeholder;
|
||||
final Widget? errorWidget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
child: Visibility(
|
||||
visible: !isLocal,
|
||||
replacement: Image.file(
|
||||
File('$filePath'),
|
||||
fit: fit,
|
||||
width: width,
|
||||
height: height,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
width: width,
|
||||
height: height,
|
||||
imageUrl: '$url',
|
||||
fit: fit,
|
||||
placeholder: (context, url) {
|
||||
return placeholder ?? _placeErrWidget(Assets.imagesPlaceholder);
|
||||
},
|
||||
errorWidget: (context, url, error) {
|
||||
return errorWidget ?? _placeErrWidget(Assets.imagesError);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _placeErrWidget(String imgName) {
|
||||
return Container(
|
||||
color: Colors.white10,
|
||||
child: Image.asset(
|
||||
imgName,
|
||||
color: Colors.white10,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
70
lib/common/components/navigation_bar/base_appbar.dart
Normal file
@ -0,0 +1,70 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/7
|
||||
// Description: BaseAppBar
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:now_wallpaper/generated/assets.dart';
|
||||
import 'package:now_wallpaper/res/themes/app_colors.dart';
|
||||
|
||||
class BaseAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const BaseAppBar(
|
||||
this.title, {
|
||||
super.key,
|
||||
this.backgroundColor,
|
||||
this.backWidget,
|
||||
this.titleStyle,
|
||||
});
|
||||
|
||||
final Color? backgroundColor;
|
||||
final Widget? backWidget;
|
||||
final String title;
|
||||
final TextStyle? titleStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: preferredSize.height,
|
||||
padding: EdgeInsets.fromLTRB(10, ScreenUtil().statusBarHeight, 20, 0).w,
|
||||
color: backgroundColor ?? seedColor,
|
||||
child: Row(
|
||||
children: [
|
||||
backWidget ??
|
||||
ClipOval(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: Get.back,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10).w,
|
||||
child: Image.asset(
|
||||
Assets.imagesIcBack,
|
||||
width: 13.w,
|
||||
height: 13.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: titleStyle ??
|
||||
TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(ScreenUtil().statusBarHeight + kToolbarHeight);
|
||||
}
|
||||
62
lib/common/components/navigation_bar/title_bar_widget.dart
Normal file
@ -0,0 +1,62 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/11
|
||||
// Description: 标题栏
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:now_wallpaper/generated/assets.dart';
|
||||
import 'package:now_wallpaper/res/themes/app_sizes.dart';
|
||||
|
||||
class TitleBarWidget extends StatelessWidget {
|
||||
const TitleBarWidget(
|
||||
this.title, {
|
||||
super.key,
|
||||
this.showSettingsBtn = true,
|
||||
this.settingsOnTap,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final bool? showSettingsBtn;
|
||||
final Function()? settingsOnTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: titleBarHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 20.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showSettingsBtn!) ...[
|
||||
ClipOval(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: settingsOnTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10).w,
|
||||
child: Image.asset(
|
||||
Assets.imagesIcSettings,
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10.w),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
58
lib/common/components/refresh/base_easyrefresh.dart
Normal file
@ -0,0 +1,58 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/7
|
||||
// Description: 下拉刷新、上拉加载
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
|
||||
class BaseEasyRefresh extends StatelessWidget {
|
||||
const BaseEasyRefresh({
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.header,
|
||||
this.footer,
|
||||
this.onRefresh,
|
||||
this.onLoad,
|
||||
this.refreshOnStart = false,
|
||||
required this.viewState,
|
||||
required this.child,
|
||||
this.width,
|
||||
this.height,
|
||||
});
|
||||
|
||||
final EasyRefreshController controller;
|
||||
final Header? header;
|
||||
final Footer? footer;
|
||||
final FutureOr Function()? onRefresh;
|
||||
final FutureOr Function()? onLoad;
|
||||
final bool refreshOnStart;
|
||||
final ViewState viewState;
|
||||
final Widget child;
|
||||
final double? width;
|
||||
final double? height;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EasyRefresh(
|
||||
controller: controller,
|
||||
header: header ?? const ClassicHeader(),
|
||||
footer: footer ?? const ClassicFooter(),
|
||||
onRefresh: onRefresh,
|
||||
onLoad: onLoad,
|
||||
refreshOnStart: refreshOnStart,
|
||||
child: viewState == ViewState.normal ? child : SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
width: width ?? 1.sw,
|
||||
height: height ?? 300.h,
|
||||
child: ViewStateWidget(
|
||||
viewState: viewState,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/11
|
||||
// Description: 标题栏
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:wallpaper/res/themes/app_sizes.dart';
|
||||
|
||||
class TitleBarWidget extends StatelessWidget {
|
||||
const TitleBarWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.showMenuBtn = false,
|
||||
this.deleteOnTap,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final bool? showMenuBtn;
|
||||
final Function()? deleteOnTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: kTextTabBarHeight,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 22.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showMenuBtn!) ...[
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_showPopMenu(context);
|
||||
},
|
||||
icon: const Icon(Icons.menu, color: Colors.white),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPopMenu(context) {
|
||||
showMenu(
|
||||
context: context,
|
||||
surfaceTintColor: Colors.white,
|
||||
position: RelativeRect.fromLTRB(double.infinity, statusToolBarHeight, 0.0, 0.0),
|
||||
items: [
|
||||
PopupMenuItem<String>(
|
||||
value: 'delete',
|
||||
onTap: deleteOnTap,
|
||||
child: const ListTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: Text('Delete All'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9,22 +9,26 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
enum ViewState { normal, error, loading, empty }
|
||||
|
||||
class ViewStateWidget extends StatelessWidget {
|
||||
const ViewStateWidget({super.key, required this.state, required this.child});
|
||||
const ViewStateWidget({
|
||||
super.key,
|
||||
required this.viewState,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final ViewState state;
|
||||
final ViewState viewState;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (state) {
|
||||
switch (viewState) {
|
||||
case ViewState.normal:
|
||||
return child;
|
||||
case ViewState.loading:
|
||||
return loadingView();
|
||||
case ViewState.error:
|
||||
return errorView;
|
||||
case ViewState.empty:
|
||||
return emptyView;
|
||||
case ViewState.error:
|
||||
return errorView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +57,7 @@ Widget get emptyView {
|
||||
'No data available',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: const Color(0xFF666666),
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
),
|
||||
@ -67,7 +71,7 @@ Widget get errorView {
|
||||
'An error occurred, please try again later',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
color: const Color(0xFF666666),
|
||||
fontSize: 14.sp,
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/11
|
||||
// Description: 设置锁屏/首页壁纸
|
||||
|
||||
import 'package:async_wallpaper/async_wallpaper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/divider_widget.dart';
|
||||
import 'package:wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:wallpaper/res/themes/app_colors.dart';
|
||||
|
||||
class WallpaperPicker {
|
||||
static void picker(String imgPath) {
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
_bottomSheetItem('Lock Screen', () {
|
||||
Get.back();
|
||||
setWallpaper(imgPath, AsyncWallpaper.LOCK_SCREEN);
|
||||
}),
|
||||
const DividerWidget(),
|
||||
_bottomSheetItem('Home Screen', () {
|
||||
Get.back();
|
||||
setWallpaper(imgPath, AsyncWallpaper.HOME_SCREEN);
|
||||
}),
|
||||
const DividerWidget(),
|
||||
_bottomSheetItem('Both Screen', () {
|
||||
Get.back();
|
||||
setWallpaper(imgPath, AsyncWallpaper.BOTH_SCREENS);
|
||||
}),
|
||||
const DividerWidget(),
|
||||
_bottomSheetItem('Cancel', Get.back),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _bottomSheetItem(String label, Function() onTap) {
|
||||
return SizedBox(
|
||||
width: 1.sw,
|
||||
child: TextButton(
|
||||
onPressed: onTap,
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: seedColor,
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Future setWallpaper(String imgPath, int wallpaperLocation) async {
|
||||
loading();
|
||||
String result;
|
||||
try {
|
||||
result = await AsyncWallpaper.setWallpaperFromFile(
|
||||
filePath: imgPath,
|
||||
wallpaperLocation: AsyncWallpaper.LOCK_SCREEN,
|
||||
// toastDetails: ToastDetails.success(),
|
||||
// errorToastDetails: ToastDetails.error(),
|
||||
)
|
||||
? 'Wallpaper applied successfully'
|
||||
: 'Failed to get wallpaper';
|
||||
} on PlatformException {
|
||||
result = 'Failed to get wallpaper';
|
||||
}
|
||||
dismiss();
|
||||
toast(result);
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,12 @@
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
import 'package:wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||
import 'package:wallpaper/common/network/base_error.dart';
|
||||
import 'package:wallpaper/common/network/dio_interceptor.dart';
|
||||
import 'package:wallpaper/common/utils/log_print.dart';
|
||||
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:now_wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:now_wallpaper/common/network/base_error.dart';
|
||||
import 'package:now_wallpaper/common/network/dio_interceptor.dart';
|
||||
import 'package:now_wallpaper/common/utils/log_print.dart';
|
||||
import 'package:now_wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:now_wallpaper/models/base_resp_model.dart';
|
||||
|
||||
class DioClient {
|
||||
static final DioClient _instance = DioClient._internal();
|
||||
@ -71,10 +71,11 @@ class DioClient {
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
BaseError error = getError(e);
|
||||
toast(error.message, show: showToast);
|
||||
if (fail != null) fail(error);
|
||||
toast(error.message, show: showToast);
|
||||
} catch (e) {
|
||||
LogPrint.e(e.toString());
|
||||
toast(e.toString(), show: showToast);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||
import 'package:now_wallpaper/models/base_resp_model.dart';
|
||||
|
||||
class DioInterceptor extends Interceptor {
|
||||
@override
|
||||
|
||||
23
lib/common/plugin/set_wallpaper.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/17
|
||||
// Description: 设置壁纸插件
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class SetWallpaper {
|
||||
static const MethodChannel _channel = MethodChannel('set_wallpaper');
|
||||
|
||||
static Future<String> setWallpaper(String path, int wallpaperType) async {
|
||||
try {
|
||||
final String result = await _channel.invokeMethod('setWallpaper', {
|
||||
"path": path,
|
||||
"wallpaperType": wallpaperType // 0 for home screen, 1 for lock screen, 2 for both
|
||||
});
|
||||
print(result);
|
||||
return result;
|
||||
} on PlatformException catch (e) {
|
||||
print('Failed to set wallpaper: ${e.message}.');
|
||||
return 'Failed to set wallpaper: ${e.message}.';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,8 @@
|
||||
// Date: 2024/5/8
|
||||
// Description: 收藏数据
|
||||
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
|
||||
import 'hive_storage.dart';
|
||||
import 'package:now_wallpaper/common/storage/hive_storage.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
|
||||
class FavoriteData {
|
||||
/// 私有构造函数
|
||||
@ -28,17 +27,19 @@ class FavoriteData {
|
||||
}
|
||||
|
||||
/// 存储壁纸
|
||||
void setWallpaperData(WallpaperData wallpaperData) {
|
||||
_box.add(wallpaperData);
|
||||
Future<int> setWallpaperData(WallpaperData wallpaperData) async {
|
||||
return await _box.add(wallpaperData);
|
||||
}
|
||||
|
||||
/// 删除壁纸
|
||||
void delete(index) {
|
||||
_box.deleteAt(index);
|
||||
Future<void> delete(index) async {
|
||||
await _box.deleteAt(index);
|
||||
await _box.flush();
|
||||
}
|
||||
|
||||
/// 删除所有壁纸
|
||||
void clear() {
|
||||
_box.clear();
|
||||
Future<void> clear() async {
|
||||
await _box.clear();
|
||||
await _box.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// Description: 持久化储存
|
||||
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
|
||||
const favoriteBox = 'favoriteBox';
|
||||
|
||||
|
||||
@ -5,11 +5,17 @@
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
|
||||
class DeviceInfoUtil {
|
||||
/// 获取当前设备的sdk版本
|
||||
static Future<int> getSDKVersion() async {
|
||||
/// 获取当前Android设备的系统版本
|
||||
static Future<int> getAndroidSystemVersion() async {
|
||||
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
|
||||
int sdkVersion = androidInfo.version.sdkInt;
|
||||
return sdkVersion;
|
||||
AndroidDeviceInfo androidDeviceInfo = await deviceInfoPlugin.androidInfo;
|
||||
return androidDeviceInfo.version.sdkInt;
|
||||
}
|
||||
|
||||
/// 获取当前iOS设备的系统版本
|
||||
static Future<String> getIOSSystemVersion() async {
|
||||
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||
IosDeviceInfo iosDeviceInfo = await deviceInfoPlugin.iosInfo;
|
||||
return iosDeviceInfo.systemVersion;
|
||||
}
|
||||
}
|
||||
@ -3,31 +3,30 @@
|
||||
// Description: 下载文件
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaper/common/network/dio_client.dart';
|
||||
import 'package:wallpaper/common/utils/filesize_util.dart';
|
||||
import 'package:wallpaper/common/utils/local_path_util.dart';
|
||||
import 'package:wallpaper/common/utils/log_print.dart';
|
||||
import 'package:wallpaper/common/utils/num_util.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:now_wallpaper/common/network/dio_client.dart';
|
||||
import 'package:now_wallpaper/common/utils/filesize_util.dart';
|
||||
import 'package:now_wallpaper/common/utils/local_path_util.dart';
|
||||
import 'package:now_wallpaper/common/utils/log_print.dart';
|
||||
import 'package:now_wallpaper/common/utils/num_util.dart';
|
||||
|
||||
class DownloadUtil {
|
||||
/// 下载壁纸
|
||||
static Future downloadWallpaper(String imageUrl, Function(String savePath) callBack) async {
|
||||
/// 下载文件
|
||||
static Future downloadFile(String imageUrl, Function(String savePath) callBack) async {
|
||||
var process = 0.0.obs;
|
||||
var processStr = ''.obs;
|
||||
var cancelToken = CancelToken();
|
||||
Get.showSnackbar(GetSnackBar(
|
||||
borderRadius: 8.r,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16).w,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 16).w,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.white,
|
||||
titleText: const Text(
|
||||
'Loading...',
|
||||
'downloading...',
|
||||
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
|
||||
),
|
||||
messageText: Obx(() {
|
||||
@ -53,15 +52,13 @@ class DownloadUtil {
|
||||
);
|
||||
}),
|
||||
));
|
||||
Directory dir = Directory(await LocalPathUtil.getTemporaryPath());
|
||||
Directory dir = await LocalPathUtil.getTemporaryPath();
|
||||
bool exist = await dir.exists();
|
||||
if (!exist) {
|
||||
// 若目录不存在,先创建
|
||||
await dir.create();
|
||||
}
|
||||
// 获取图片文件的名称,使用uri来解析url
|
||||
Uri uri = Uri.parse(imageUrl);
|
||||
String savePath = '${dir.path}/${uri.pathSegments.last}';
|
||||
String savePath = '${dir.path}/${DateTime.now().millisecondsSinceEpoch}.jpg';
|
||||
LogPrint.d('壁纸保存位置:$savePath');
|
||||
await DioClient().download(imageUrl, savePath, cancelToken: cancelToken, onReceiveProgress: (int count, int total) {
|
||||
if (total != -1) {
|
||||
|
||||
@ -2,27 +2,28 @@
|
||||
// Date: 2024/5/10
|
||||
// Description: 本地路径
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class LocalPathUtil {
|
||||
/// 获取临时文件路径(IOS和Android通用)
|
||||
/// Android: /data/user/0/包名/cache
|
||||
static Future<String> getTemporaryPath() async {
|
||||
final dir = await getTemporaryDirectory();
|
||||
return dir.path;
|
||||
/// 获取临时目录
|
||||
/// `NSCachesDirectory` on iOS and macOS
|
||||
/// `Context.getCacheDir` on Android.
|
||||
static Future<Directory> getTemporaryPath() async {
|
||||
return await getTemporaryDirectory();
|
||||
}
|
||||
|
||||
/// 获取应用支持目录(IOS和Android通用)
|
||||
/// Android: /data/user/0/包名/files
|
||||
static Future<String> getSupportPath() async {
|
||||
final dir = await getApplicationSupportDirectory();
|
||||
return dir.path;
|
||||
/// 获取应用支持目录
|
||||
/// `NSApplicationSupportDirectory` on iOS and macOS.
|
||||
/// The Flutter engine's `PathUtils.getFilesDir` API on Android.
|
||||
static Future<Directory> getSupportPath() async {
|
||||
return await getApplicationSupportDirectory();
|
||||
}
|
||||
|
||||
/// 获取应用文件目录(IOS和Android通用)
|
||||
/// Android: /data/user/0/包名/app_flutter
|
||||
static Future<String> getDocumentsPath() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
return dir.path;
|
||||
/// 获取应用文档目录
|
||||
/// `NSDocumentDirectory` on iOS and macOS.
|
||||
/// The Flutter engine's `PathUtils.getDataDirectory` API on Android.
|
||||
static Future<Directory> getDocumentsPath() async {
|
||||
return await getApplicationDocumentsDirectory();
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
// Description: 日志打印
|
||||
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:wallpaper/res/values/strings.dart';
|
||||
|
||||
final _logger = Logger(
|
||||
printer: PrettyPrinter(
|
||||
@ -23,7 +22,7 @@ final _logger = Logger(
|
||||
);
|
||||
|
||||
class LogPrint {
|
||||
static const String _tag = '$appName Log';
|
||||
static const String _tag = 'LogPrint';
|
||||
|
||||
LogPrint.t(dynamic msg, {String tag = _tag}) {
|
||||
_logger.t('[$tag]: $msg');
|
||||
|
||||
@ -4,15 +4,19 @@
|
||||
|
||||
class ObjUtil {
|
||||
static bool isNotEmptyStr(String? str) {
|
||||
return str == null || str.trim().isNotEmpty;
|
||||
return str != null && str.trim().isNotEmpty;
|
||||
}
|
||||
|
||||
static String getStr(String? str) {
|
||||
return isNotEmptyStr(str) ? str! : '';
|
||||
}
|
||||
|
||||
static bool isNotEmptyList(Iterable? list) {
|
||||
return list == null || list.isNotEmpty;
|
||||
return list != null && list.isNotEmpty;
|
||||
}
|
||||
|
||||
static bool isNotEmptyMap(Map? map) {
|
||||
return map == null || map.isNotEmpty;
|
||||
return map != null && map.isNotEmpty;
|
||||
}
|
||||
|
||||
static bool isEmpty(Object? object) {
|
||||
@ -44,18 +48,4 @@ class ObjUtil {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// get length.
|
||||
static int getLength(Object? value) {
|
||||
if (value == null) return 0;
|
||||
if (value is String) {
|
||||
return value.length;
|
||||
} else if (value is Iterable) {
|
||||
return value.length;
|
||||
} else if (value is Map) {
|
||||
return value.length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:wallpaper/common/components/dialog/hint_dialog.dart';
|
||||
import 'package:now_wallpaper/common/components/dialog/remind_dialog.dart';
|
||||
|
||||
class PermissionUtil {
|
||||
/// 检测是否有权限
|
||||
@ -108,7 +108,7 @@ class PermissionUtil {
|
||||
static _showFailedDialog(List<Permission> permissionList, {bool isPermanentlyDenied = false}) async {
|
||||
Get.dialog(
|
||||
barrierDismissible: false,
|
||||
HintDialog(
|
||||
RemindDialog(
|
||||
content: await _getInstructions(permissionList),
|
||||
confirmText: isPermanentlyDenied ? 'Go Settings' : 'Confirm',
|
||||
confirmOnTap: () {
|
||||
@ -138,7 +138,7 @@ class PermissionUtil {
|
||||
}
|
||||
String explain = '';
|
||||
if (failedPermission == Permission.storage) {
|
||||
explain = 'Please allow the APP to save photos to the album';
|
||||
explain = 'To save wallpapers to gallery, we need this permission';
|
||||
}
|
||||
return explain;
|
||||
}
|
||||
|
||||
15
lib/common/utils/wallpaper_manage.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// Author: fengshengxiong
|
||||
// Date: 2024/5/11
|
||||
// Description: 设置锁屏/首页壁纸
|
||||
|
||||
import 'package:now_wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:now_wallpaper/common/plugin/set_wallpaper.dart';
|
||||
|
||||
class WallpaperManage {
|
||||
static Future setWallpaper(String imgPath, int location) async {
|
||||
loading();
|
||||
String result = await SetWallpaper.setWallpaper(imgPath, location);
|
||||
dismiss();
|
||||
toast(result);
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,22 @@
|
||||
class Assets {
|
||||
Assets._();
|
||||
|
||||
static const String imagesError = 'assets/images/error.png';
|
||||
static const String imagesPlaceholder = 'assets/images/placeholder.png';
|
||||
static const String iconIconApp = 'assets/icon/icon_app.jpeg';
|
||||
static const String imagesCatalogSelected = 'assets/images/catalog_selected.png';
|
||||
static const String imagesCatalogUnchecked = 'assets/images/catalog_unchecked.png';
|
||||
static const String imagesDiscoverSelected = 'assets/images/discover_selected.png';
|
||||
static const String imagesDiscoverUnchecked = 'assets/images/discover_unchecked.png';
|
||||
static const String imagesFavoriteSelected = 'assets/images/favorite_selected.png';
|
||||
static const String imagesFavoriteUnchecked = 'assets/images/favorite_unchecked.png';
|
||||
static const String imagesIcBack = 'assets/images/ic_back.png';
|
||||
static const String imagesIcDownload = 'assets/images/ic_download.png';
|
||||
static const String imagesIcFavorite = 'assets/images/ic_favorite.png';
|
||||
static const String imagesIcFiltered = 'assets/images/ic_filtered.png';
|
||||
static const String imagesIcPreview = 'assets/images/ic_preview.png';
|
||||
static const String imagesIcSettingArrow = 'assets/images/ic_setting_arrow.png';
|
||||
static const String imagesIcSettings = 'assets/images/ic_settings.png';
|
||||
static const String imagesImgError = 'assets/images/img_error.png';
|
||||
static const String imagesImgPlaceholder = 'assets/images/img_placeholder.png';
|
||||
static const String jsonWallpaper = 'assets/json/wallpaper.json';
|
||||
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
// This file is automatically generated. DO NOT EDIT, all your changes would be lost.
|
||||
import 'package:flutter/material.dart' show debugPrint;
|
||||
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/models/base_resp_model.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
|
||||
JsonConvert jsonConvert = JsonConvert();
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||
import 'package:now_wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:now_wallpaper/models/base_resp_model.dart';
|
||||
|
||||
BaseRespModel $BaseRespModelFromJson(Map<String, dynamic> json) {
|
||||
final BaseRespModel baseRespModel = BaseRespModel();
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'package:now_wallpaper/generated/json/base/json_convert_content.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
|
||||
WallpaperModel $WallpaperModelFromJson(Map<String, dynamic> json) {
|
||||
final WallpaperModel wallpaperModel = WallpaperModel();
|
||||
final List<WallpaperData>? data = (json['data'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => jsonConvert.convert<WallpaperData>(e) as WallpaperData)
|
||||
?.map((e) => jsonConvert.convert<WallpaperData>(e) as WallpaperData)
|
||||
.toList();
|
||||
if (data != null) {
|
||||
wallpaperModel.data = data;
|
||||
@ -47,8 +44,8 @@ WallpaperData $WallpaperDataFromJson(Map<String, dynamic> json) {
|
||||
if (original != null) {
|
||||
wallpaperData.original = original;
|
||||
}
|
||||
final String? previewThumb = jsonConvert.convert<String>(
|
||||
json['previewThumb']);
|
||||
final String? previewThumb =
|
||||
jsonConvert.convert<String>(json['previewThumb']);
|
||||
if (previewThumb != null) {
|
||||
wallpaperData.previewThumb = previewThumb;
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:wallpaper/common/storage/hive_storage.dart';
|
||||
import 'package:wallpaper/res/themes/app_themes.dart';
|
||||
import 'package:wallpaper/res/values/strings.dart';
|
||||
import 'package:wallpaper/routes/app_pages.dart';
|
||||
import 'package:now_wallpaper/common/components/easy_loading.dart';
|
||||
import 'package:now_wallpaper/common/storage/hive_storage.dart';
|
||||
import 'package:now_wallpaper/res/themes/app_themes.dart';
|
||||
import 'package:now_wallpaper/res/values/strings.dart';
|
||||
import 'package:now_wallpaper/routes/app_pages.dart';
|
||||
|
||||
void main() async {
|
||||
// 初始化Hive
|
||||
@ -41,6 +41,7 @@ class MyApp extends StatelessWidget {
|
||||
builder: (context, child) {
|
||||
return GetMaterialApp(
|
||||
title: appName,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: darkTheme,
|
||||
darkTheme: darkTheme,
|
||||
themeMode: ThemeMode.dark,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:wallpaper/generated/json/base/json_field.dart';
|
||||
import 'package:wallpaper/generated/json/base_resp_model.g.dart';
|
||||
import 'package:now_wallpaper/generated/json/base/json_field.dart';
|
||||
import 'package:now_wallpaper/generated/json/base_resp_model.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class BaseRespModel {
|
||||
@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:wallpaper/generated/json/base/json_field.dart';
|
||||
import 'package:wallpaper/generated/json/wallpaper_model.g.dart';
|
||||
import 'package:now_wallpaper/generated/json/base/json_field.dart';
|
||||
import 'package:now_wallpaper/generated/json/wallpaper_model.g.dart';
|
||||
|
||||
part 'wallpaper_model.g.dart';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:wallpaper/res/values/strings.dart';
|
||||
import 'package:now_wallpaper/res/values/strings.dart';
|
||||
|
||||
class AboutController extends GetxController {
|
||||
var versionName = ''.obs;
|
||||
@ -14,6 +14,6 @@ class AboutController extends GetxController {
|
||||
/// 获取版本号
|
||||
void _getVersion() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
versionName.value = '$appName\nversion:${packageInfo.version}';
|
||||
versionName.value = 'App Version:${packageInfo.version}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/base_appbar.dart';
|
||||
|
||||
import 'about_controller.dart';
|
||||
import 'package:now_wallpaper/common/components/navigation_bar/base_appbar.dart';
|
||||
import 'package:now_wallpaper/generated/assets.dart';
|
||||
import 'package:now_wallpaper/modules/about/about_controller.dart';
|
||||
import 'package:now_wallpaper/res/values/strings.dart';
|
||||
|
||||
class AboutView extends GetView<AboutController> {
|
||||
const AboutView({super.key});
|
||||
@ -12,24 +13,45 @@ class AboutView extends GetView<AboutController> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const BaseAppBar('About'),
|
||||
body: Column(
|
||||
body: Container(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 100.h),
|
||||
SizedBox(height: 60.h),
|
||||
Image.asset(
|
||||
Assets.iconIconApp,
|
||||
width: 110.w,
|
||||
height: 110.w,
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
Obx(() {
|
||||
return Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
controller.versionName.value,
|
||||
return RichText(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: appName,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14.sp,
|
||||
fontSize: 22.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '\n${controller.versionName.value}',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
43
lib/modules/catalog/catalog_controller.dart
Normal file
@ -0,0 +1,43 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/modules/root/root_controller.dart';
|
||||
import 'package:now_wallpaper/routes/app_pages.dart';
|
||||
|
||||
class CatalogController extends GetxController {
|
||||
static CatalogController get to => Get.find<CatalogController>();
|
||||
late ViewState viewState;
|
||||
late List<WallpaperModel> wallpaperModelList;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getData();
|
||||
}
|
||||
|
||||
void getData() {
|
||||
wallpaperModelList = RootController.to.wallpaperModelList;
|
||||
viewState = wallpaperModelList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/// 从分类中随机取5张
|
||||
List<WallpaperData> getRandomFiveList(WallpaperModel item) {
|
||||
if (item.data != null && item.data!.length >= 5) {
|
||||
var flatList = item.data!.map((e) => e).toList();
|
||||
return (flatList..shuffle()).take(5).toList();
|
||||
} else {
|
||||
return item.data == null ? <WallpaperData>[] : item.data!;
|
||||
}
|
||||
}
|
||||
|
||||
void onTapSingleCls(WallpaperModel wallpaperModel) {
|
||||
// 进入单个分类页面
|
||||
Get.toNamed(AppPages.singleCls, arguments: wallpaperModel);
|
||||
}
|
||||
|
||||
/// 点击壁纸
|
||||
void onTapItem(List<WallpaperData> wallpaperList, int index) {
|
||||
Get.toNamed(AppPages.wallpaperDet, arguments: {'position': index, 'wallpaperList': wallpaperList});
|
||||
}
|
||||
}
|
||||
109
lib/modules/catalog/catalog_view.dart
Normal file
@ -0,0 +1,109 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:now_wallpaper/common/components/image_network_widget.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/modules/catalog/catalog_controller.dart';
|
||||
|
||||
class CatalogView extends GetView<CatalogController> {
|
||||
const CatalogView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.lazyPut(() => CatalogController());
|
||||
return GetBuilder<CatalogController>(
|
||||
builder: (logic) {
|
||||
return ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 7.h),
|
||||
itemCount: controller.wallpaperModelList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildCatalogItem(controller.wallpaperModelList[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(height: 24.h);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCatalogItem(WallpaperModel item) {
|
||||
final wallpaperList = controller.getRandomFiveList(item);
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${item.name}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (item.data != null && item.data!.isNotEmpty) ...[
|
||||
SizedBox(width: 10.w),
|
||||
GestureDetector(
|
||||
onTap: () => controller.onTapSingleCls(item),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${item.data!.length} More',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Icon(Icons.keyboard_arrow_right, size: 18.w),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
SizedBox(
|
||||
height: 233.h,
|
||||
child: ShaderMask(
|
||||
shaderCallback: (Rect bounds) {
|
||||
return const LinearGradient(
|
||||
colors: [Colors.transparent, Color(0xFF0C0C15), Color(0xFF0C0C15), Colors.transparent],
|
||||
stops: [0.0, 0.1, 0.9, 1],
|
||||
).createShader(bounds);
|
||||
},
|
||||
blendMode: BlendMode.dstIn,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: wallpaperList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildWallpaperItem(wallpaperList, wallpaperList[index], index);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(width: 8.w);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWallpaperItem(List<WallpaperData> wallpaperList, WallpaperData item, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () => controller.onTapItem(wallpaperList, index),
|
||||
child: ImageNetworkWidget(
|
||||
url: item.previewThumb,
|
||||
width: 136.w,
|
||||
height: double.infinity,
|
||||
radius: 8.r,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
75
lib/modules/discover/discover_controller.dart
Normal file
@ -0,0 +1,75 @@
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:now_wallpaper/common/utils/obj_util.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/modules/root/root_controller.dart';
|
||||
import 'package:now_wallpaper/routes/app_pages.dart';
|
||||
|
||||
class DiscoverController extends GetxController {
|
||||
static DiscoverController get to => Get.find<DiscoverController>();
|
||||
late final EasyRefreshController refreshController;
|
||||
late ViewState viewState;
|
||||
var allWallpaper = <WallpaperData>[];
|
||||
var wallpaperDataList = <WallpaperData>[];
|
||||
var todayNewestList = <WallpaperData>[];
|
||||
WallpaperModel? todayNewestData;
|
||||
/// 分类名称
|
||||
var clsName = '';
|
||||
/// 页码、每页数量
|
||||
int pageNum = 1;
|
||||
int pageSize = 10;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
refreshController = EasyRefreshController(controlFinishLoad: true);
|
||||
getData();
|
||||
}
|
||||
|
||||
void getData() {
|
||||
todayNewestData = RootController.to.getRandomCls();
|
||||
if (todayNewestData != null && todayNewestData!.data != null && todayNewestData!.data!.length >= 3) {
|
||||
clsName = ObjUtil.getStr(todayNewestData!.name);
|
||||
var flatList = todayNewestData!.data!.map((e) => e).toList();
|
||||
// 随机打乱此列表中的元素,再取前3个元素
|
||||
todayNewestList = (flatList..shuffle()).take(3).toList();
|
||||
}
|
||||
viewState = RootController.to.viewState;
|
||||
allWallpaper = RootController.to.wallpaperModelList.expand((element) => element.data ?? <WallpaperData>[]).toList();
|
||||
_getDataToShow();
|
||||
}
|
||||
|
||||
void _getDataToShow() {
|
||||
if (allWallpaper.isNotEmpty) {
|
||||
// 开始的索引
|
||||
int startIndex = (pageNum - 1) * pageSize;
|
||||
// 结束的索引
|
||||
int endIndex = startIndex + pageSize;
|
||||
wallpaperDataList.addAll(allWallpaper.sublist(startIndex, endIndex));
|
||||
refreshController.finishLoad();
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
/// 上拉加载
|
||||
void onLoad() {
|
||||
if (pageNum == (allWallpaper.length / pageSize).ceil()) {
|
||||
refreshController.finishLoad(IndicatorResult.noMore);
|
||||
} else {
|
||||
pageNum++;
|
||||
_getDataToShow();
|
||||
}
|
||||
}
|
||||
|
||||
/// 点击今日最新
|
||||
void onTapTodayNewest() {
|
||||
// 进入单个分类页面
|
||||
Get.toNamed(AppPages.singleCls, arguments: todayNewestData);
|
||||
}
|
||||
|
||||
/// 点击壁纸
|
||||
void onTapItem(int index) {
|
||||
Get.toNamed(AppPages.wallpaperDet, arguments: {'position': index, 'wallpaperList': wallpaperDataList});
|
||||
}
|
||||
}
|
||||
124
lib/modules/discover/discover_view.dart
Normal file
@ -0,0 +1,124 @@
|
||||
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:now_wallpaper/common/components/image_network_widget.dart';
|
||||
import 'package:now_wallpaper/common/components/refresh/base_easyrefresh.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/modules/discover/discover_controller.dart';
|
||||
import 'package:now_wallpaper/res/themes/app_sizes.dart';
|
||||
|
||||
class DiscoverView extends GetView<DiscoverController> {
|
||||
const DiscoverView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.lazyPut(() => DiscoverController());
|
||||
return GetBuilder<DiscoverController>(
|
||||
builder: (controller) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 7.h),
|
||||
if (controller.todayNewestList.isNotEmpty) ...[
|
||||
_buildTodayNewest(),
|
||||
SizedBox(height: 7.h),
|
||||
],
|
||||
Expanded(
|
||||
child: BaseEasyRefresh(
|
||||
controller: controller.refreshController,
|
||||
onLoad: controller.onLoad,
|
||||
viewState: controller.viewState,
|
||||
height: discoverListHeight,
|
||||
child: MasonryGridView.count(
|
||||
itemCount: controller.wallpaperDataList.length,
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10.w,
|
||||
crossAxisSpacing: 10.w,
|
||||
padding: EdgeInsets.fromLTRB(20.w, 0, 20.w, 7.h),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildDiscoverItem(controller.wallpaperDataList[index], index);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDiscoverItem(WallpaperData item, index) {
|
||||
return GestureDetector(
|
||||
onTap: () => controller.onTapItem(index),
|
||||
child: ImageNetworkWidget(
|
||||
url: item.previewThumb,
|
||||
radius: 8.r,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 今日最新
|
||||
Widget _buildTodayNewest() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20).w,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12).r,
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: controller.onTapTodayNewest,
|
||||
child: Container(
|
||||
height: todayNewestHeight,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16).w,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: RichText(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
text: TextSpan(
|
||||
text: 'Today',
|
||||
style: TextStyle(
|
||||
color: const Color(0xFF666666),
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '\n${controller.clsName}',
|
||||
style: TextStyle(
|
||||
color: const Color(0xFF333333),
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...List.generate(controller.todayNewestList.length, (index) {
|
||||
final item = controller.todayNewestList[index];
|
||||
return Row(
|
||||
children: [
|
||||
ImageNetworkWidget(
|
||||
url: item.previewThumb,
|
||||
width: 42.w,
|
||||
height: 42.w,
|
||||
radius: 4.r,
|
||||
),
|
||||
if (index != controller.todayNewestList.length - 1) ...[
|
||||
SizedBox(width: 8.w),
|
||||
],
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,38 +1,50 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:wallpaper/common/components/dialog/hint_dialog.dart';
|
||||
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||
import 'package:wallpaper/common/storage/favorite_data.dart';
|
||||
import 'package:wallpaper/routes/app_pages.dart';
|
||||
import 'package:now_wallpaper/common/components/dialog/remind_dialog.dart';
|
||||
import 'package:now_wallpaper/common/components/view_state_widget.dart';
|
||||
import 'package:now_wallpaper/common/storage/favorite_data.dart';
|
||||
import 'package:now_wallpaper/models/wallpaper_model.dart';
|
||||
import 'package:now_wallpaper/modules/root/root_controller.dart';
|
||||
import 'package:now_wallpaper/routes/app_pages.dart';
|
||||
|
||||
class FavoriteController extends GetxController {
|
||||
static FavoriteController get to => Get.find<FavoriteController>();
|
||||
var viewState = ViewState.loading;
|
||||
late List<WallpaperData> wallpaperList;
|
||||
late ViewState viewState;
|
||||
late List<WallpaperData> favoriteList;
|
||||
var todayHottestList = <WallpaperData>[];
|
||||
WallpaperModel? todayHottestData;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_getData();
|
||||
getData();
|
||||
getTodayHottestList();
|
||||
}
|
||||
|
||||
/// 获取数据
|
||||
void _getData() {
|
||||
wallpaperList = FavoriteData().getWallpaperData().reversed.toList();
|
||||
_changeViewState();
|
||||
void getData() {
|
||||
favoriteList = FavoriteData().getWallpaperData().reversed.toList();
|
||||
_refreshList();
|
||||
}
|
||||
|
||||
/// 改变页面状态
|
||||
void _changeViewState() {
|
||||
viewState = wallpaperList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||
void _refreshList() {
|
||||
viewState = favoriteList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||
refresh();
|
||||
}
|
||||
|
||||
/// 获取今日最热门壁纸
|
||||
void getTodayHottestList() {
|
||||
// 从分类中随机取3张壁纸
|
||||
todayHottestData = RootController.to.getRandomCls();
|
||||
if (todayHottestData != null && todayHottestData!.data != null && todayHottestData!.data!.length >= 3) {
|
||||
var flatList = todayHottestData!.data!.map((e) => e).toList();
|
||||
todayHottestList = (flatList..shuffle()).take(3).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// 点击壁纸
|
||||
void itemOnTap(int index) async {
|
||||
void onTapItem(int index, bool isFavorite) {
|
||||
Get.toNamed(AppPages.wallpaperDet, arguments: {
|
||||
'isFavorite': true,
|
||||
'position': index,
|
||||
'wallpaperList': wallpaperList
|
||||
'wallpaperList': isFavorite ? favoriteList : todayHottestList,
|
||||
});
|
||||
}
|
||||
|
||||
@ -40,11 +52,14 @@ class FavoriteController extends GetxController {
|
||||
void onLongPress(int index) {
|
||||
Get.dialog(
|
||||
barrierDismissible: false,
|
||||
HintDialog(
|
||||
RemindDialog(
|
||||
content: 'Are you sure you want to delete the wallpaper?',
|
||||
confirmOnTap: () {
|
||||
FavoriteData().delete(index);
|
||||
refreshList();
|
||||
// 计算原始列表中的对应索引
|
||||
int indexToRemoveFromDb = FavoriteData().getWallpaperData().length - 1 - index;
|
||||
FavoriteData().delete(indexToRemoveFromDb);
|
||||
favoriteList.removeAt(index);
|
||||
_refreshList();
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -54,21 +69,19 @@ class FavoriteController extends GetxController {
|
||||
void deleteAll() {
|
||||
Get.dialog(
|
||||
barrierDismissible: false,
|
||||
HintDialog(
|
||||
RemindDialog(
|
||||
content: 'Are you sure to delete all wallpapers?',
|
||||
confirmOnTap: () {
|
||||
confirmOnTap: () async {
|
||||
FavoriteData().clear();
|
||||
wallpaperList.clear();
|
||||
_changeViewState();
|
||||
refresh();
|
||||
favoriteList.clear();
|
||||
_refreshList();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 刷新
|
||||
void refreshList() {
|
||||
_getData();
|
||||
refresh();
|
||||
void onTapViewMore() {
|
||||
// 进入单个分类页面
|
||||
Get.toNamed(AppPages.singleCls, arguments: todayHottestData);
|
||||
}
|
||||
}
|
||||
|
||||