第一版完成
@ -70,6 +70,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
release {
|
release {
|
||||||
|
ndk{
|
||||||
|
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
|
||||||
|
}
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
|
|||||||
@ -1,15 +1,27 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="32"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="@string/app_name"
|
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:requestLegacyExternalStorage="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
@ -17,11 +29,10 @@
|
|||||||
to determine the Window background behind the Flutter UI. -->
|
to determine the Window background behind the Flutter UI. -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme"
|
android:resource="@style/NormalTheme" />
|
||||||
/>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
@ -37,8 +48,8 @@
|
|||||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||||
<data android:mimeType="text/plain"/>
|
<data android:mimeType="text/plain" />
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:drawable="?android:colorBackground" />
|
<!--<item android:drawable="?android:colorBackground" />-->
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
<!-- You can insert your own image assets here -->
|
||||||
<!-- <item>
|
<item>
|
||||||
<bitmap
|
<bitmap
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:src="@mipmap/launch_image" />
|
android:src="@mipmap/ic_launcher" />
|
||||||
</item> -->
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!-- Modify this file to customize your launch splash screen -->
|
||||||
<!-- Modify this file to customize your launch splash screen -->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:drawable="@android:color/white" />
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
<!-- You can insert your own image assets here -->
|
<!-- You can insert your own image assets here -->
|
||||||
<!-- <item>
|
<item>¬
|
||||||
<bitmap
|
<bitmap
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:src="@mipmap/launch_image" />
|
android:src="@mipmap/ic_launcher" />
|
||||||
</item> -->
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
BIN
assets/images/error.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/images/placeholder.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
12208
assets/json/wallpaper.json
Normal file
@ -8,12 +8,14 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
|
2DCFEA80A305BFF9A959A7DC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFDFEB7EDEB2089913ADD3B0 /* Pods_Runner.framework */; };
|
||||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
D52A56DB3AAE02F2724B4CBE /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F7EC70670285B234B265439 /* Pods_RunnerTests.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -40,14 +42,19 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0F7EC70670285B234B265439 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
1561731E247DBD5F8A0E0B0F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
418D1D078E33D929E8A6B10D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
47B01B907AB8EEA90704F2FE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
7B71BC8DD4AFE8E93734BDDC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -55,19 +62,44 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
A037E86DEDE09232C797AA86 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
CFDFEB7EDEB2089913ADD3B0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D1C685EC8726F4DCDC6C4284 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
5AA6084520452439D2649B6D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
D52A56DB3AAE02F2724B4CBE /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
2DCFEA80A305BFF9A959A7DC /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
0797B7C7F9044CE8DB306198 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D1C685EC8726F4DCDC6C4284 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
47B01B907AB8EEA90704F2FE /* Pods-Runner.release.xcconfig */,
|
||||||
|
A037E86DEDE09232C797AA86 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
418D1D078E33D929E8A6B10D /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
1561731E247DBD5F8A0E0B0F /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
7B71BC8DD4AFE8E93734BDDC /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -76,6 +108,15 @@
|
|||||||
path = RunnerTests;
|
path = RunnerTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
58D07E0F3F53151088D726CF /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CFDFEB7EDEB2089913ADD3B0 /* Pods_Runner.framework */,
|
||||||
|
0F7EC70670285B234B265439 /* Pods_RunnerTests.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -94,6 +135,8 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
0797B7C7F9044CE8DB306198 /* Pods */,
|
||||||
|
58D07E0F3F53151088D726CF /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@ -128,8 +171,10 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
9F3C7A1490C44408DFBA2D44 /* [CP] Check Pods Manifest.lock */,
|
||||||
331C807D294A63A400263BE5 /* Sources */,
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
331C807F294A63A400263BE5 /* Resources */,
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
5AA6084520452439D2649B6D /* Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -145,12 +190,15 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
34C811B39ECD78862B33AD2D /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
39CCEDF39CF579238AE308F0 /* [CP] Embed Pods Frameworks */,
|
||||||
|
405E88F0300BCE9602B4DA07 /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -222,6 +270,45 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
34C811B39ECD78862B33AD2D /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
39CCEDF39CF579238AE308F0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@ -238,6 +325,23 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
405E88F0300BCE9602B4DA07 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@ -253,6 +357,28 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
|
9F3C7A1490C44408DFBA2D44 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@ -327,6 +453,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@ -379,6 +506,7 @@
|
|||||||
};
|
};
|
||||||
331C8088294A63A400263BE5 /* Debug */ = {
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 418D1D078E33D929E8A6B10D /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@ -396,6 +524,7 @@
|
|||||||
};
|
};
|
||||||
331C8089294A63A400263BE5 /* Release */ = {
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 1561731E247DBD5F8A0E0B0F /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@ -411,6 +540,7 @@
|
|||||||
};
|
};
|
||||||
331C808A294A63A400263BE5 /* Profile */ = {
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 7B71BC8DD4AFE8E93734BDDC /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@ -448,6 +578,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@ -505,6 +636,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
|||||||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@ -1,122 +1,14 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"filename" : "Icon-App-1024x1024@1x.png",
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
"scale" : "1x"
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 295 B |
|
Before Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 704 B |
|
Before Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 586 B |
|
Before Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
6
ios/Runner/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,23 +1,21 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "Icon-App-1024x1024@1x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "LaunchImage.png",
|
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "LaunchImage@2x.png",
|
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"filename" : "LaunchImage@3x.png",
|
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
ios/Runner/Assets.xcassets/LaunchImage.imageset/Icon-App-1024x1024@1x.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
@ -1,5 +0,0 @@
|
|||||||
# Launch Screen Assets
|
|
||||||
|
|
||||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
|
||||||
|
|
||||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
|
||||||
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--View Controller-->
|
||||||
@ -14,9 +16,15 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFit" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||||
|
<rect key="frame" x="156.66666666666666" y="386" width="80" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="jA8-Ug-YSl"/>
|
||||||
|
<constraint firstAttribute="height" constant="80" id="uJb-kM-IXm"/>
|
||||||
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -28,10 +36,10 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
<point key="canvasLocation" x="80.916030534351137" y="264.08450704225356"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="LaunchImage" width="168" height="185"/>
|
<image name="LaunchImage" width="1024" height="1024"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||||
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Flutter View Controller-->
|
<!--Flutter View Controller-->
|
||||||
@ -14,13 +16,14 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-16" y="-41"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -45,5 +45,7 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
|
<string>Please allow the APP to save photos to the album</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
70
lib/common/components/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/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);
|
||||||
|
}
|
||||||
56
lib/common/components/base_masonry_gridview.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
117
lib/common/components/dialog/hint_dialog.dart
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/11
|
||||||
|
// Description: 提示框
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
class HintDialog extends StatelessWidget {
|
||||||
|
const HintDialog({
|
||||||
|
super.key,
|
||||||
|
this.title,
|
||||||
|
this.content,
|
||||||
|
this.showCancelBtn = true,
|
||||||
|
this.cancelText,
|
||||||
|
this.confirmText,
|
||||||
|
this.confirmOnTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? title;
|
||||||
|
final String? content;
|
||||||
|
final bool? showCancelBtn;
|
||||||
|
final String? cancelText;
|
||||||
|
final String? confirmText;
|
||||||
|
final Function()? confirmOnTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 0.8.sw,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
Text(
|
||||||
|
title ?? 'Reminder',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 18.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
Text(
|
||||||
|
content ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black54,
|
||||||
|
fontSize: 15.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
const DividerWidget(),
|
||||||
|
SizedBox(
|
||||||
|
height: 48.h,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if (showCancelBtn!) ...[
|
||||||
|
_optionButton(cancelText ?? 'Cancel', false),
|
||||||
|
Container(
|
||||||
|
width: 1.w,
|
||||||
|
height: double.infinity,
|
||||||
|
color: const Color(0xFFE5E5E5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
_optionButton(confirmText ?? 'Confirm', true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _optionButton(String label, bool isConfirm) {
|
||||||
|
return Expanded(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
if (isConfirm && confirmOnTap != null) {
|
||||||
|
confirmOnTap!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: double.infinity,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: isConfirm ? Colors.black : Colors.black45,
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
lib/common/components/dialog/process_dialog.dart
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: 进度框
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
class ProcessDialog extends StatelessWidget {
|
||||||
|
final double process;
|
||||||
|
final String processStr;
|
||||||
|
final Function() cancelOnTap;
|
||||||
|
final bool showCanCancel;
|
||||||
|
|
||||||
|
const ProcessDialog(
|
||||||
|
this.process,
|
||||||
|
this.processStr, {
|
||||||
|
super.key,
|
||||||
|
this.showCanCancel = true,
|
||||||
|
required this.cancelOnTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: Center(
|
||||||
|
child: IntrinsicWidth(
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: 80.w,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
),
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
loadingView(
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(Colors.black),
|
||||||
|
value: process,
|
||||||
|
),
|
||||||
|
if (ObjUtil.isNotEmptyStr(processStr)) ...[
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
processStr,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (!showCanCancel) ...[
|
||||||
|
SizedBox(height: 10.h),
|
||||||
|
],
|
||||||
|
if (showCanCancel) ...[
|
||||||
|
SizedBox(
|
||||||
|
height: 40.h,
|
||||||
|
child: TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
onPressed: cancelOnTap,
|
||||||
|
child: Text(
|
||||||
|
'Cancel',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/common/components/divider_widget.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: 分割线
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
class DividerWidget extends StatelessWidget {
|
||||||
|
const DividerWidget({super.key, this.height, this.color});
|
||||||
|
|
||||||
|
final double? height;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Divider(
|
||||||
|
height: height ?? 1.w,
|
||||||
|
thickness: height ?? 1.w,
|
||||||
|
color: color ?? const Color(0xFFE5E5E5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import '../theme/colors.dart';
|
import 'package:wallpaper/common/utils/obj_util.dart';
|
||||||
|
|
||||||
/// EasyLoading配置
|
/// EasyLoading配置
|
||||||
void configLoading() {
|
void configLoading() {
|
||||||
@ -26,7 +26,7 @@ void configLoading() {
|
|||||||
// 指示器的大小, 默认40.0.
|
// 指示器的大小, 默认40.0.
|
||||||
..indicatorSize = 26.0
|
..indicatorSize = 26.0
|
||||||
// loading的圆角大小, 默认5.0.
|
// loading的圆角大小, 默认5.0.
|
||||||
..radius = 5.0
|
..radius = 8.0
|
||||||
// 文本大小, 默认15.0.
|
// 文本大小, 默认15.0.
|
||||||
..fontSize = 14.0
|
..fontSize = 14.0
|
||||||
// 进度条指示器的宽度, 默认2.0.
|
// 进度条指示器的宽度, 默认2.0.
|
||||||
@ -34,39 +34,39 @@ void configLoading() {
|
|||||||
// 指示器的宽度, 默认4.0, 仅对[EasyLoadingIndicatorType.ring, EasyLoadingIndicatorType.dualRing]有效.
|
// 指示器的宽度, 默认4.0, 仅对[EasyLoadingIndicatorType.ring, EasyLoadingIndicatorType.dualRing]有效.
|
||||||
..lineWidth = 2.0
|
..lineWidth = 2.0
|
||||||
// [showSuccess] [showError] [showInfo]的展示时间, 默认2000ms.
|
// [showSuccess] [showError] [showInfo]的展示时间, 默认2000ms.
|
||||||
..displayDuration = const Duration(milliseconds: 1000)
|
..displayDuration = const Duration(milliseconds: 2000)
|
||||||
// 动画时间, 默认200ms.
|
// 动画时间, 默认200ms.
|
||||||
..animationDuration = const Duration(milliseconds: 200)
|
..animationDuration = const Duration(milliseconds: 200)
|
||||||
// 文本的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
// 文本的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
||||||
..textColor = white
|
..textColor = Colors.black
|
||||||
// 指示器的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
// 指示器的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
||||||
..indicatorColor = white
|
..indicatorColor = Colors.black
|
||||||
// 进度条指示器的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
// 进度条指示器的颜色, 仅对[EasyLoadingStyle.custom]有效.
|
||||||
..progressColor = white
|
..progressColor = Colors.black
|
||||||
// loading的背景色, 仅对[EasyLoadingStyle.custom]有效.
|
// loading的背景色, 仅对[EasyLoadingStyle.custom]有效.
|
||||||
..backgroundColor = loadingBg
|
..backgroundColor = Colors.white
|
||||||
// 遮罩的背景色, 仅对[EasyLoadingMaskType.custom]有效.
|
// 遮罩的背景色, 仅对[EasyLoadingMaskType.custom]有效.
|
||||||
..maskColor = black.withOpacity(0.3)
|
..maskColor = Colors.black.withOpacity(0.3)
|
||||||
// 当loading展示的时候,是否允许用户操作.
|
// 当loading展示的时候,是否允许用户操作.
|
||||||
..userInteractions = false
|
..userInteractions = false
|
||||||
// 点击背景是否关闭.
|
// 点击背景是否关闭.
|
||||||
..dismissOnTap = false;
|
..dismissOnTap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void toast(String? value, {bool isShow = true}) {
|
void toast(String? value, {bool show = true}) {
|
||||||
if (isShow) {
|
if (show && ObjUtil.isNotEmptyStr(value)) {
|
||||||
EasyLoading.showToast(value ?? '');
|
EasyLoading.showToast('$value');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loading({String? value, bool isShow = true}) {
|
void loading({String? value, bool show = true}) {
|
||||||
if (isShow) {
|
if (show) {
|
||||||
EasyLoading.show(status: value);
|
EasyLoading.show(status: value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dismiss({bool isDismiss = true}) {
|
void dismiss({bool dismiss = true}) {
|
||||||
if (isDismiss) {
|
if (dismiss) {
|
||||||
EasyLoading.dismiss();
|
EasyLoading.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
lib/common/components/get_bind_widget.dart
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: 该组件可以回收GetXController,用于处理无法某些组件的GetXController无法回收的情况
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
/// GetBindWidget can bind GetxController, and when the page is disposed,
|
||||||
|
/// it can automatically destroy the bound related GetXController
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Sample:
|
||||||
|
///
|
||||||
|
/// class SampleController extends GetxController {
|
||||||
|
/// final String title = 'My Awesome View';
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// class SamplePage extends StatelessWidget {
|
||||||
|
/// final controller = SampleController();
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Widget build(BuildContext context) {
|
||||||
|
/// return GetBindWidget(
|
||||||
|
/// bind: controller,
|
||||||
|
/// child: Container(),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
class GetBindWidget extends StatefulWidget {
|
||||||
|
const GetBindWidget({
|
||||||
|
super.key,
|
||||||
|
this.bind,
|
||||||
|
this.tag,
|
||||||
|
this.binds,
|
||||||
|
this.tags,
|
||||||
|
required this.child,
|
||||||
|
}) : assert(
|
||||||
|
binds == null || tags == null || binds.length == tags.length,
|
||||||
|
'The binds and tags arrays length should be equal\n'
|
||||||
|
'and the elements in the two arrays correspond one-to-one',
|
||||||
|
);
|
||||||
|
|
||||||
|
final GetxController? bind;
|
||||||
|
final String? tag;
|
||||||
|
|
||||||
|
final List<GetxController>? binds;
|
||||||
|
final List<String>? tags;
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
GetBindWidgetState createState() => GetBindWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetBindWidgetState extends State<GetBindWidget> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return widget.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_closeGetXController();
|
||||||
|
_closeGetXControllers();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
///Close GetxController bound to the current page
|
||||||
|
void _closeGetXController() {
|
||||||
|
if (widget.bind == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = widget.bind.runtimeType.toString() + (widget.tag ?? '');
|
||||||
|
GetInstance().delete(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Batch close GetxController bound to the current page
|
||||||
|
void _closeGetXControllers() {
|
||||||
|
if (widget.binds == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < widget.binds!.length; i++) {
|
||||||
|
var type = widget.binds![i].runtimeType.toString();
|
||||||
|
|
||||||
|
if (widget.tags == null) {
|
||||||
|
GetInstance().delete(key: type);
|
||||||
|
} else {
|
||||||
|
var key = type + (widget.tags?[i] ?? '');
|
||||||
|
GetInstance().delete(key: key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
lib/common/components/image_widget.dart
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// 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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
lib/common/components/keep_alive_wrapper.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: 保持组件状态
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class KeepAliveWrapper extends StatefulWidget {
|
||||||
|
const KeepAliveWrapper({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.keepAlive = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final bool keepAlive;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _KeepAliveWrapperState extends State<KeepAliveWrapper> with AutomaticKeepAliveClientMixin {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
return widget.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => widget.keepAlive;
|
||||||
|
}
|
||||||
69
lib/common/components/title_bar_widget.dart
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
75
lib/common/components/view_state_widget.dart
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: 状态视图
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
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});
|
||||||
|
|
||||||
|
final ViewState state;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
switch (state) {
|
||||||
|
case ViewState.normal:
|
||||||
|
return child;
|
||||||
|
case ViewState.loading:
|
||||||
|
return loadingView();
|
||||||
|
case ViewState.error:
|
||||||
|
return errorView;
|
||||||
|
case ViewState.empty:
|
||||||
|
return emptyView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 加载中视图
|
||||||
|
Widget loadingView({
|
||||||
|
Color? color,
|
||||||
|
Animation<Color?>? valueColor,
|
||||||
|
Color? backgroundColor,
|
||||||
|
double? value,
|
||||||
|
}) {
|
||||||
|
return Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: color,
|
||||||
|
valueColor: valueColor,
|
||||||
|
backgroundColor: backgroundColor ?? Colors.grey,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 空视图
|
||||||
|
Widget get emptyView {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'No data available',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 错误视图
|
||||||
|
Widget get errorView {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'An error occurred, please try again later',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
80
lib/common/components/wallpaper_picker.dart
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
// Author: fengshengxiong
|
|
||||||
// Date: 2024/5/7
|
|
||||||
// Description: 接口Api
|
|
||||||
|
|
||||||
class Api {
|
|
||||||
/// baseUrl
|
|
||||||
static const String baseUrl = 'http://47.108.227.40:6376/';
|
|
||||||
}
|
|
||||||
22
lib/common/models/base_resp_model.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:wallpaper/generated/json/base/json_field.dart';
|
||||||
|
import 'package:wallpaper/generated/json/base_resp_model.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class BaseRespModel {
|
||||||
|
int? code;
|
||||||
|
String? message;
|
||||||
|
dynamic data;
|
||||||
|
|
||||||
|
BaseRespModel();
|
||||||
|
|
||||||
|
factory BaseRespModel.fromJson(Map<String, dynamic> json) => $BaseRespModelFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => $BaseRespModelToJson(this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return jsonEncode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
lib/common/models/wallpaper_model.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
part 'wallpaper_model.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class WallpaperModel {
|
||||||
|
List<WallpaperData>? data;
|
||||||
|
String? name;
|
||||||
|
|
||||||
|
WallpaperModel();
|
||||||
|
|
||||||
|
factory WallpaperModel.fromJson(Map<String, dynamic> json) => $WallpaperModelFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => $WallpaperModelToJson(this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return jsonEncode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HiveType(typeId: 1)
|
||||||
|
@JsonSerializable()
|
||||||
|
class WallpaperData extends HiveObject{
|
||||||
|
@HiveField(0)
|
||||||
|
String? banner;
|
||||||
|
|
||||||
|
@HiveField(1)
|
||||||
|
String? original;
|
||||||
|
|
||||||
|
@HiveField(2)
|
||||||
|
String? previewThumb;
|
||||||
|
|
||||||
|
@HiveField(3)
|
||||||
|
String? source;
|
||||||
|
|
||||||
|
WallpaperData();
|
||||||
|
|
||||||
|
factory WallpaperData.fromJson(Map<String, dynamic> json) => $WallpaperDataFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => $WallpaperDataToJson(this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return jsonEncode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lib/common/models/wallpaper_model.g.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'wallpaper_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// TypeAdapterGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class WallpaperDataAdapter extends TypeAdapter<WallpaperData> {
|
||||||
|
@override
|
||||||
|
final int typeId = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
WallpaperData read(BinaryReader reader) {
|
||||||
|
final numOfFields = reader.readByte();
|
||||||
|
final fields = <int, dynamic>{
|
||||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
|
};
|
||||||
|
return WallpaperData()
|
||||||
|
..banner = fields[0] as String?
|
||||||
|
..original = fields[1] as String?
|
||||||
|
..previewThumb = fields[2] as String?
|
||||||
|
..source = fields[3] as String?;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(BinaryWriter writer, WallpaperData obj) {
|
||||||
|
writer
|
||||||
|
..writeByte(4)
|
||||||
|
..writeByte(0)
|
||||||
|
..write(obj.banner)
|
||||||
|
..writeByte(1)
|
||||||
|
..write(obj.original)
|
||||||
|
..writeByte(2)
|
||||||
|
..write(obj.previewThumb)
|
||||||
|
..writeByte(3)
|
||||||
|
..write(obj.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is WallpaperDataAdapter &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
typeId == other.typeId;
|
||||||
|
}
|
||||||
39
lib/common/network/base_error.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: 服务端基本错误类型封装类
|
||||||
|
|
||||||
|
abstract class BaseError {
|
||||||
|
final int? code;
|
||||||
|
final String? message;
|
||||||
|
|
||||||
|
BaseError({this.code, this.message});
|
||||||
|
}
|
||||||
|
|
||||||
|
class NeedLogin implements BaseError {
|
||||||
|
@override
|
||||||
|
int get code => 401;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get message => "Unauthenticated";
|
||||||
|
}
|
||||||
|
|
||||||
|
class NeedAuth implements BaseError {
|
||||||
|
@override
|
||||||
|
int get code => 403;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get message => "Unauthorized access";
|
||||||
|
}
|
||||||
|
|
||||||
|
class OtherError implements BaseError {
|
||||||
|
final int? statusCode;
|
||||||
|
final String? statusMessage;
|
||||||
|
|
||||||
|
OtherError({this.statusCode, this.statusMessage});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int? get code => statusCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get message => statusMessage;
|
||||||
|
}
|
||||||
169
lib/common/network/dio_client.dart
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: dio封装
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
class DioClient {
|
||||||
|
static final DioClient _instance = DioClient._internal();
|
||||||
|
|
||||||
|
factory DioClient() => _instance;
|
||||||
|
late Dio _dio;
|
||||||
|
|
||||||
|
DioClient._internal() {
|
||||||
|
_dio = Dio();
|
||||||
|
final baseOptions = BaseOptions(
|
||||||
|
baseUrl: '',
|
||||||
|
connectTimeout: const Duration(seconds: 15),
|
||||||
|
receiveTimeout: const Duration(seconds: 10),
|
||||||
|
);
|
||||||
|
_dio.options = baseOptions;
|
||||||
|
_dio.interceptors.add(DioInterceptor());
|
||||||
|
_dio.interceptors.add(PrettyDioLogger(
|
||||||
|
requestHeader: true,
|
||||||
|
requestBody: true,
|
||||||
|
compact: false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 请求
|
||||||
|
Future request<T>(
|
||||||
|
String path, {
|
||||||
|
required RequestMethod requestMethod,
|
||||||
|
dynamic data,
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
Options? options,
|
||||||
|
bool isFormData = false,
|
||||||
|
bool showLoading = false,
|
||||||
|
bool showToast = true,
|
||||||
|
required Function(T? result) success,
|
||||||
|
Function(BaseError baseError)? fail,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
loading(show: showLoading);
|
||||||
|
Response response = await _dio.request(
|
||||||
|
path,
|
||||||
|
data: isFormData ? FormData.fromMap(data) : data,
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
options: _getOptions(requestMethod, options),
|
||||||
|
);
|
||||||
|
dismiss(dismiss: showLoading);
|
||||||
|
BaseRespModel baseRespModel = response.data;
|
||||||
|
if (baseRespModel.code == 200) {
|
||||||
|
success(JsonConvert.fromJsonAsT<T>(baseRespModel.data));
|
||||||
|
} else {
|
||||||
|
toast(baseRespModel.message, show: showToast);
|
||||||
|
if (fail != null) {
|
||||||
|
fail(OtherError(
|
||||||
|
statusCode: baseRespModel.code,
|
||||||
|
statusMessage: baseRespModel.message,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
BaseError error = getError(e);
|
||||||
|
toast(error.message, show: showToast);
|
||||||
|
if (fail != null) fail(error);
|
||||||
|
} catch (e) {
|
||||||
|
LogPrint.e(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Options? _getOptions(RequestMethod requestMethod, Options? options) {
|
||||||
|
options ??= Options();
|
||||||
|
options.method = requestMethod.method;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 下载
|
||||||
|
Future<void> download(
|
||||||
|
String urlPath,
|
||||||
|
dynamic savePath, {
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
CancelToken? cancelToken,
|
||||||
|
data,
|
||||||
|
Options? options,
|
||||||
|
required Function() success,
|
||||||
|
Function(String? err)? fail,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await _dio.download(
|
||||||
|
urlPath,
|
||||||
|
savePath,
|
||||||
|
onReceiveProgress: onReceiveProgress,
|
||||||
|
queryParameters: queryParameters,
|
||||||
|
cancelToken: cancelToken,
|
||||||
|
data: data,
|
||||||
|
options: Options(receiveTimeout: const Duration(seconds: 0)),
|
||||||
|
);
|
||||||
|
success();
|
||||||
|
} on DioException catch (e) {
|
||||||
|
BaseError error = getError(e);
|
||||||
|
toast(error.message);
|
||||||
|
if (fail != null) fail(error.message);
|
||||||
|
} catch (e) {
|
||||||
|
LogPrint.e(e.toString());
|
||||||
|
toast(e.toString());
|
||||||
|
if (fail != null) fail(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseError getError(DioException e) {
|
||||||
|
if (e.runtimeType == DioException) {
|
||||||
|
switch (e.type) {
|
||||||
|
case DioExceptionType.connectionTimeout:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'connection timed out');
|
||||||
|
case DioExceptionType.sendTimeout:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'send timeout');
|
||||||
|
case DioExceptionType.receiveTimeout:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'receive timeout');
|
||||||
|
case DioExceptionType.badCertificate:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'certificate error');
|
||||||
|
case DioExceptionType.cancel:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'request canceled');
|
||||||
|
case DioExceptionType.connectionError:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'connection error');
|
||||||
|
case DioExceptionType.unknown:
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'unknown error');
|
||||||
|
case DioExceptionType.badResponse:
|
||||||
|
final response = e.response;
|
||||||
|
if (response!.statusCode == 401) {
|
||||||
|
return NeedLogin();
|
||||||
|
} else if (response.statusCode == 403) {
|
||||||
|
return NeedAuth();
|
||||||
|
} else {
|
||||||
|
return OtherError(
|
||||||
|
statusCode: response.statusCode,
|
||||||
|
statusMessage: response.statusMessage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OtherError(statusCode: -1, statusMessage: 'unknown error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RequestMethod {
|
||||||
|
get('GET'),
|
||||||
|
post('POST'),
|
||||||
|
put('PUT'),
|
||||||
|
head('HEAD'),
|
||||||
|
delete('DELETE'),
|
||||||
|
patch('PATCH');
|
||||||
|
|
||||||
|
const RequestMethod(
|
||||||
|
this.method,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String method;
|
||||||
|
}
|
||||||
31
lib/common/network/dio_interceptor.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/9
|
||||||
|
// Description: dio拦截器
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||||
|
|
||||||
|
class DioInterceptor extends Interceptor {
|
||||||
|
@override
|
||||||
|
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||||
|
if (response.data is! ResponseBody) {
|
||||||
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
|
BaseRespModel baseRespModel = BaseRespModel();
|
||||||
|
if (response.data != null && response.data is Map) {
|
||||||
|
baseRespModel = BaseRespModel.fromJson(response.data);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
baseRespModel = BaseRespModel.fromJson(jsonDecode(response.data));
|
||||||
|
} catch (e) {
|
||||||
|
baseRespModel.data = response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.data = baseRespModel;
|
||||||
|
return handler.resolve(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onResponse(response, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lib/common/storage/favorite_data.dart
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/8
|
||||||
|
// Description: 收藏数据
|
||||||
|
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
|
||||||
|
import 'hive_storage.dart';
|
||||||
|
|
||||||
|
class FavoriteData {
|
||||||
|
/// 私有构造函数
|
||||||
|
FavoriteData._();
|
||||||
|
|
||||||
|
/// 静态常量用于保存类的唯一实例
|
||||||
|
static final FavoriteData _instance = FavoriteData._();
|
||||||
|
|
||||||
|
/// 工厂构造函数返回类的唯一实例
|
||||||
|
factory FavoriteData() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 声明盒子
|
||||||
|
/// 注意, main函数中这个盒子已经打开, 可以进行存储操作
|
||||||
|
final _box = getFavoriteBox();
|
||||||
|
|
||||||
|
/// 获取壁纸
|
||||||
|
List<WallpaperData> getWallpaperData() {
|
||||||
|
return _box.values.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 存储壁纸
|
||||||
|
void setWallpaperData(WallpaperData wallpaperData) {
|
||||||
|
_box.add(wallpaperData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除壁纸
|
||||||
|
void delete(index) {
|
||||||
|
_box.deleteAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除所有壁纸
|
||||||
|
void clear() {
|
||||||
|
_box.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/common/storage/hive_storage.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/8
|
||||||
|
// Description: 持久化储存
|
||||||
|
|
||||||
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
|
||||||
|
const favoriteBox = 'favoriteBox';
|
||||||
|
|
||||||
|
Future initHive() async {
|
||||||
|
// 初始化
|
||||||
|
await Hive.initFlutter();
|
||||||
|
// 注册类型适配器
|
||||||
|
Hive.registerAdapter(WallpaperDataAdapter());
|
||||||
|
// 打开盒子
|
||||||
|
await Hive.openBox<WallpaperData>(favoriteBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取盒子
|
||||||
|
Box<WallpaperData> getFavoriteBox() {
|
||||||
|
return Hive.box<WallpaperData>(favoriteBox);
|
||||||
|
}
|
||||||
@ -1,16 +0,0 @@
|
|||||||
// Author: fengshengxiong
|
|
||||||
// Date: 2024/5/7
|
|
||||||
// Description: 颜色
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// 主要颜色
|
|
||||||
const primary = Colors.deepPurple;
|
|
||||||
|
|
||||||
const white = Colors.white;
|
|
||||||
|
|
||||||
const black = Colors.black;
|
|
||||||
|
|
||||||
const grey = Colors.grey;
|
|
||||||
|
|
||||||
const loadingBg = Color(0xFF616161);
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
// Author: fengshengxiong
|
|
||||||
// Date: 2024/5/8
|
|
||||||
// Description: 大小
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
// Author: fengshengxiong
|
|
||||||
// Date: 2024/5/7
|
|
||||||
// Description: 主题
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'colors.dart';
|
|
||||||
|
|
||||||
/// 浅色主题
|
|
||||||
ThemeData lightTheme = ThemeData.light(useMaterial3: true).copyWith(
|
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: primary),
|
|
||||||
primaryColor: primary,
|
|
||||||
scaffoldBackgroundColor: white,
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
|
||||||
backgroundColor: primary,
|
|
||||||
iconTheme: const IconThemeData(color: white),
|
|
||||||
elevation: 0.0,
|
|
||||||
centerTitle: true,
|
|
||||||
titleTextStyle: TextStyle(color: white, fontSize: 25.sp, fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
textSelectionTheme: const TextSelectionThemeData(
|
|
||||||
cursorColor: primary,
|
|
||||||
selectionHandleColor: primary,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 深色主题
|
|
||||||
ThemeData darkTheme = ThemeData.dark(useMaterial3: true).copyWith(
|
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: primary),
|
|
||||||
primaryColor: primary,
|
|
||||||
);
|
|
||||||
15
lib/common/utils/device_info_util.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/11
|
||||||
|
// Description: 设备信息
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
|
||||||
|
class DeviceInfoUtil {
|
||||||
|
/// 获取当前设备的sdk版本
|
||||||
|
static Future<int> getSDKVersion() async {
|
||||||
|
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||||
|
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
|
||||||
|
int sdkVersion = androidInfo.version.sdkInt;
|
||||||
|
return sdkVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
lib/common/utils/download_util.dart
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/10
|
||||||
|
// 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';
|
||||||
|
|
||||||
|
class DownloadUtil {
|
||||||
|
/// 下载壁纸
|
||||||
|
static Future downloadWallpaper(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,
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
titleText: const Text(
|
||||||
|
'Loading...',
|
||||||
|
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
messageText: Obx(() {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
loadingView(
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(Colors.black),
|
||||||
|
value: process.value,
|
||||||
|
),
|
||||||
|
SizedBox(width: 10.w),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
processStr.value,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
Directory dir = Directory(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}';
|
||||||
|
LogPrint.d('壁纸保存位置:$savePath');
|
||||||
|
await DioClient().download(imageUrl, savePath, cancelToken: cancelToken, onReceiveProgress: (int count, int total) {
|
||||||
|
if (total != -1) {
|
||||||
|
process.value = NumUtil.strToDouble(NumUtil.formatNum(count / total));
|
||||||
|
processStr.value = '${FileSizeUtil.fileSize(count)}/${FileSizeUtil.fileSize(total)}';
|
||||||
|
}
|
||||||
|
}, success: () {
|
||||||
|
Get.back();
|
||||||
|
callBack(savePath);
|
||||||
|
}, fail: (err) {
|
||||||
|
Get.back();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
70
lib/common/utils/filesize_util.dart
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: 文件大小单位转换
|
||||||
|
|
||||||
|
class FileSizeUtil {
|
||||||
|
/// A method returns a human readable string representing a file fileSize
|
||||||
|
static String fileSize(dynamic size, [int round = 2]) {
|
||||||
|
/**
|
||||||
|
* [size] can be passed as number or as string
|
||||||
|
*
|
||||||
|
* the optional parameter [round] specifies the number
|
||||||
|
* of digits after comma/point (default is 2)
|
||||||
|
*/
|
||||||
|
var divider = 1024;
|
||||||
|
int fileSize;
|
||||||
|
try {
|
||||||
|
fileSize = int.parse(size.toString());
|
||||||
|
} catch (e) {
|
||||||
|
throw ArgumentError('Can not parse the size parameter: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider) {
|
||||||
|
return '$fileSize B';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider && fileSize % divider == 0) {
|
||||||
|
return '${(fileSize / divider).toStringAsFixed(0)} KB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider) {
|
||||||
|
return '${(fileSize / divider).toStringAsFixed(round)} KB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider && fileSize % divider == 0) {
|
||||||
|
return '${(fileSize / (divider * divider)).toStringAsFixed(0)} MB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider) {
|
||||||
|
return '${(fileSize / divider / divider).toStringAsFixed(round)} MB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider * divider && fileSize % divider == 0) {
|
||||||
|
return '${(fileSize / (divider * divider * divider)).toStringAsFixed(0)} GB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider * divider) {
|
||||||
|
return '${(fileSize / divider / divider / divider).toStringAsFixed(round)} GB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider * divider * divider &&
|
||||||
|
fileSize % divider == 0) {
|
||||||
|
num r = fileSize / divider / divider / divider / divider;
|
||||||
|
return '${r.toStringAsFixed(0)} TB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider * divider * divider) {
|
||||||
|
num r = fileSize / divider / divider / divider / divider;
|
||||||
|
return '${r.toStringAsFixed(round)} TB';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize < divider * divider * divider * divider * divider * divider &&
|
||||||
|
fileSize % divider == 0) {
|
||||||
|
num r = fileSize / divider / divider / divider / divider / divider;
|
||||||
|
return '${r.toStringAsFixed(0)} PB';
|
||||||
|
} else {
|
||||||
|
num r = fileSize / divider / divider / divider / divider / divider;
|
||||||
|
return '${r.toStringAsFixed(round)} PB';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
lib/common/utils/local_path_util.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/10
|
||||||
|
// Description: 本地路径
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用支持目录(IOS和Android通用)
|
||||||
|
/// Android: /data/user/0/包名/files
|
||||||
|
static Future<String> getSupportPath() async {
|
||||||
|
final dir = await getApplicationSupportDirectory();
|
||||||
|
return dir.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用文件目录(IOS和Android通用)
|
||||||
|
/// Android: /data/user/0/包名/app_flutter
|
||||||
|
static Future<String> getDocumentsPath() async {
|
||||||
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
|
return dir.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
// Description: 日志打印
|
// Description: 日志打印
|
||||||
|
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
import '../common/config/strings.dart';
|
import 'package:wallpaper/res/values/strings.dart';
|
||||||
|
|
||||||
final _logger = Logger(
|
final _logger = Logger(
|
||||||
printer: PrettyPrinter(
|
printer: PrettyPrinter(
|
||||||
28
lib/common/utils/num_util.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: NumUtil
|
||||||
|
|
||||||
|
class NumUtil {
|
||||||
|
static int getInt(num? value) {
|
||||||
|
if (value == null) return 0;
|
||||||
|
return value.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static double getDouble(num? value) {
|
||||||
|
if (value == null) return 0.0;
|
||||||
|
return value.toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String formatNum(double? num, {int index = 2}){
|
||||||
|
if (num == null) return '0';
|
||||||
|
if((num.toString().length - num.toString().lastIndexOf('.') - 1) < index){
|
||||||
|
return num.toStringAsFixed(index).substring(0, num.toString().lastIndexOf('.') + index + 1).toString();
|
||||||
|
}else{
|
||||||
|
return num.toString().substring(0, num.toString().lastIndexOf('.') + index + 1).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strToDouble(String valueStr, {double defValue = 0.0}) {
|
||||||
|
return double.tryParse(valueStr) ?? defValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
// Description: 对象工具类
|
// Description: 对象工具类
|
||||||
|
|
||||||
class ObjUtil {
|
class ObjUtil {
|
||||||
static bool isNotEmptyString(String? str) {
|
static bool isNotEmptyStr(String? str) {
|
||||||
return str == null || str.trim().isNotEmpty;
|
return str == null || str.trim().isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
145
lib/common/utils/permission_util.dart
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/10
|
||||||
|
// Description: 权限处理
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:wallpaper/common/components/dialog/hint_dialog.dart';
|
||||||
|
|
||||||
|
class PermissionUtil {
|
||||||
|
/// 检测是否有权限
|
||||||
|
/// [permissionList] 权限申请列表
|
||||||
|
static Future<bool> checkPermission(List<Permission> permissionList) async {
|
||||||
|
// 一个新待申请权限列表
|
||||||
|
List<Permission> newPermissionList = [];
|
||||||
|
// 遍历当前权限申请列表
|
||||||
|
for (Permission permission in permissionList) {
|
||||||
|
PermissionStatus status = await permission.status;
|
||||||
|
// 如果不是允许状态就添加到新的申请列表中
|
||||||
|
if (!status.isGranted) {
|
||||||
|
newPermissionList.add(permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果需要重新申请的列表不是空的
|
||||||
|
if (newPermissionList.isNotEmpty) {
|
||||||
|
PermissionStatus permissionStatus = await _requestPermission(newPermissionList);
|
||||||
|
switch (permissionStatus) {
|
||||||
|
// 拒绝状态
|
||||||
|
case PermissionStatus.denied:
|
||||||
|
_showFailedDialog(newPermissionList);
|
||||||
|
return false;
|
||||||
|
// 允许状态
|
||||||
|
case PermissionStatus.granted:
|
||||||
|
case PermissionStatus.limited:
|
||||||
|
case PermissionStatus.provisional:
|
||||||
|
return true;
|
||||||
|
// 永久拒绝 活动限制
|
||||||
|
case PermissionStatus.restricted:
|
||||||
|
case PermissionStatus.permanentlyDenied:
|
||||||
|
_showFailedDialog(newPermissionList, isPermanentlyDenied: true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取新列表中的权限 如果有一项不合格就返回false
|
||||||
|
static Future<PermissionStatus> _requestPermission(List<Permission> permissionList) async {
|
||||||
|
Map<Permission, PermissionStatus> statuses = await permissionList.request();
|
||||||
|
PermissionStatus currentPermissionStatus = PermissionStatus.granted;
|
||||||
|
statuses.forEach((key, value) {
|
||||||
|
if (!value.isGranted || !value.isLimited) {
|
||||||
|
currentPermissionStatus = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return currentPermissionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> checkLocationAlways() async {
|
||||||
|
// 获取前置状态
|
||||||
|
// Android没有这一步 ios会先访问这个再访问其他的
|
||||||
|
PermissionStatus status = PermissionStatus.granted;
|
||||||
|
status = await _checkSinglePermission(Permission.locationWhenInUse);
|
||||||
|
|
||||||
|
// 获取第二个状态
|
||||||
|
PermissionStatus status2 = PermissionStatus.denied;
|
||||||
|
|
||||||
|
// 如果前置状态为成功才能执行获取第二个状态
|
||||||
|
if (status.isGranted) {
|
||||||
|
status2 = await _checkSinglePermission(Permission.locationAlways);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果两个都成功那么就返回成功
|
||||||
|
if (status.isGranted && status2.isGranted) {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 如果有一个拒绝那么就失败了
|
||||||
|
} else if (status.isDenied || status2.isDenied) {
|
||||||
|
_showFailedDialog(
|
||||||
|
[Permission.locationWhenInUse, Permission.locationAlways]);
|
||||||
|
} else {
|
||||||
|
_showFailedDialog(
|
||||||
|
[Permission.locationWhenInUse, Permission.locationAlways],
|
||||||
|
isPermanentlyDenied: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _checkSinglePermission(Permission permission) async {
|
||||||
|
// 获取当前状态
|
||||||
|
PermissionStatus status = await permission.status;
|
||||||
|
PermissionStatus currentPermissionStatus = PermissionStatus.granted;
|
||||||
|
|
||||||
|
// 如果它状态不是允许那么就去获取
|
||||||
|
if (!status.isGranted) {
|
||||||
|
currentPermissionStatus = await _requestPermission([permission]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回最终状态
|
||||||
|
return currentPermissionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 权限拒绝后弹窗
|
||||||
|
static _showFailedDialog(List<Permission> permissionList, {bool isPermanentlyDenied = false}) async {
|
||||||
|
Get.dialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
HintDialog(
|
||||||
|
content: await _getInstructions(permissionList),
|
||||||
|
confirmText: isPermanentlyDenied ? 'Go Settings' : 'Confirm',
|
||||||
|
confirmOnTap: () {
|
||||||
|
if (isPermanentlyDenied) {
|
||||||
|
openAppSettings();
|
||||||
|
} else {
|
||||||
|
checkPermission(permissionList);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取权限使用说明
|
||||||
|
static Future<String> _getInstructions(List<Permission> permissionList) async {
|
||||||
|
late Permission failedPermission;
|
||||||
|
|
||||||
|
// 遍历当前权限申请列表
|
||||||
|
for (Permission permission in permissionList) {
|
||||||
|
PermissionStatus status = await permission.status;
|
||||||
|
|
||||||
|
// 如果不是允许状态就添加到新的申请列表中
|
||||||
|
if (!status.isGranted || !status.isLimited) {
|
||||||
|
failedPermission = permission;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String explain = '';
|
||||||
|
if (failedPermission == Permission.storage) {
|
||||||
|
explain = 'Please allow the APP to save photos to the album';
|
||||||
|
}
|
||||||
|
return explain;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
lib/generated/assets.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
///This file is automatically generated. DO NOT EDIT, all your changes would be lost.
|
||||||
|
class Assets {
|
||||||
|
Assets._();
|
||||||
|
|
||||||
|
static const String imagesError = 'assets/images/error.png';
|
||||||
|
static const String imagesPlaceholder = 'assets/images/placeholder.png';
|
||||||
|
static const String jsonWallpaper = 'assets/json/wallpaper.json';
|
||||||
|
|
||||||
|
}
|
||||||
186
lib/generated/json/base/json_convert_content.dart
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// ignore_for_file: non_constant_identifier_names
|
||||||
|
// ignore_for_file: camel_case_types
|
||||||
|
// ignore_for_file: prefer_single_quotes
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
|
||||||
|
JsonConvert jsonConvert = JsonConvert();
|
||||||
|
|
||||||
|
typedef JsonConvertFunction<T> = T Function(Map<String, dynamic> json);
|
||||||
|
typedef EnumConvertFunction<T> = T Function(String value);
|
||||||
|
typedef ConvertExceptionHandler = void Function(Object error, StackTrace stackTrace);
|
||||||
|
extension MapSafeExt<K, V> on Map<K, V> {
|
||||||
|
T? getOrNull<T>(K? key) {
|
||||||
|
if (!containsKey(key) || key == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return this[key] as T?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonConvert {
|
||||||
|
static ConvertExceptionHandler? onError;
|
||||||
|
JsonConvertClassCollection convertFuncMap = JsonConvertClassCollection();
|
||||||
|
|
||||||
|
/// When you are in the development, to generate a new model class, hot-reload doesn't find new generation model class, you can build on MaterialApp method called jsonConvert. ReassembleConvertFuncMap (); This method only works in a development environment
|
||||||
|
/// https://flutter.cn/docs/development/tools/hot-reload
|
||||||
|
/// class MyApp extends StatelessWidget {
|
||||||
|
/// const MyApp({Key? key})
|
||||||
|
/// : super(key: key);
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Widget build(BuildContext context) {
|
||||||
|
/// jsonConvert.reassembleConvertFuncMap();
|
||||||
|
/// return MaterialApp();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
void reassembleConvertFuncMap() {
|
||||||
|
bool isReleaseMode = const bool.fromEnvironment('dart.vm.product');
|
||||||
|
if (!isReleaseMode) {
|
||||||
|
convertFuncMap = JsonConvertClassCollection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T? convert<T>(dynamic value, {EnumConvertFunction? enumConvert}) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value is T) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return _asT<T>(value, enumConvert: enumConvert);
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
debugPrint('asT<$T> $e $stackTrace');
|
||||||
|
if (onError != null) {
|
||||||
|
onError!(e, stackTrace);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T?>? convertList<T>(List<dynamic>? value,
|
||||||
|
{EnumConvertFunction? enumConvert}) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return value.map((dynamic e) => _asT<T>(e, enumConvert: enumConvert))
|
||||||
|
.toList();
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
debugPrint('asT<$T> $e $stackTrace');
|
||||||
|
if (onError != null) {
|
||||||
|
onError!(e, stackTrace);
|
||||||
|
}
|
||||||
|
return <T>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T>? convertListNotNull<T>(dynamic value,
|
||||||
|
{EnumConvertFunction? enumConvert}) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (value as List<dynamic>).map((dynamic e) =>
|
||||||
|
_asT<T>(e, enumConvert: enumConvert)!).toList();
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
debugPrint('asT<$T> $e $stackTrace');
|
||||||
|
if (onError != null) {
|
||||||
|
onError!(e, stackTrace);
|
||||||
|
}
|
||||||
|
return <T>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T? _asT<T extends Object?>(dynamic value,
|
||||||
|
{EnumConvertFunction? enumConvert}) {
|
||||||
|
final String type = T.toString();
|
||||||
|
final String valueS = value.toString();
|
||||||
|
if (enumConvert != null) {
|
||||||
|
return enumConvert(valueS) as T;
|
||||||
|
} else if (type == "String") {
|
||||||
|
return valueS as T;
|
||||||
|
} else if (type == "int") {
|
||||||
|
final int? intValue = int.tryParse(valueS);
|
||||||
|
if (intValue == null) {
|
||||||
|
return double.tryParse(valueS)?.toInt() as T?;
|
||||||
|
} else {
|
||||||
|
return intValue as T;
|
||||||
|
}
|
||||||
|
} else if (type == "double") {
|
||||||
|
return double.parse(valueS) as T;
|
||||||
|
} else if (type == "DateTime") {
|
||||||
|
return DateTime.parse(valueS) as T;
|
||||||
|
} else if (type == "bool") {
|
||||||
|
if (valueS == '0' || valueS == '1') {
|
||||||
|
return (valueS == '1') as T;
|
||||||
|
}
|
||||||
|
return (valueS == 'true') as T;
|
||||||
|
} else if (type == "Map" || type.startsWith("Map<")) {
|
||||||
|
return value as T;
|
||||||
|
} else {
|
||||||
|
if (convertFuncMap.containsKey(type)) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return convertFuncMap[type]!(value as Map<String, dynamic>) as T;
|
||||||
|
} else {
|
||||||
|
throw UnimplementedError(
|
||||||
|
'$type unimplemented,you can try running the app again');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//list is returned by type
|
||||||
|
static M? _getListChildType<M>(List<Map<String, dynamic>> data) {
|
||||||
|
if (<BaseRespModel>[] is M) {
|
||||||
|
return data.map<BaseRespModel>((Map<String, dynamic> e) =>
|
||||||
|
BaseRespModel.fromJson(e)).toList() as M;
|
||||||
|
}
|
||||||
|
if (<WallpaperModel>[] is M) {
|
||||||
|
return data.map<WallpaperModel>((Map<String, dynamic> e) =>
|
||||||
|
WallpaperModel.fromJson(e)).toList() as M;
|
||||||
|
}
|
||||||
|
if (<WallpaperData>[] is M) {
|
||||||
|
return data.map<WallpaperData>((Map<String, dynamic> e) =>
|
||||||
|
WallpaperData.fromJson(e)).toList() as M;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("$M not found");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static M? fromJsonAsT<M>(dynamic json) {
|
||||||
|
if (json is M) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
if (json is List) {
|
||||||
|
return _getListChildType<M>(
|
||||||
|
json.map((dynamic e) => e as Map<String, dynamic>).toList());
|
||||||
|
} else {
|
||||||
|
return jsonConvert.convert<M>(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonConvertClassCollection {
|
||||||
|
Map<String, JsonConvertFunction> convertFuncMap = {
|
||||||
|
(BaseRespModel).toString(): BaseRespModel.fromJson,
|
||||||
|
(WallpaperModel).toString(): WallpaperModel.fromJson,
|
||||||
|
(WallpaperData).toString(): WallpaperData.fromJson,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool containsKey(String type) {
|
||||||
|
return convertFuncMap.containsKey(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonConvertFunction? operator [](String key) {
|
||||||
|
return convertFuncMap[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
32
lib/generated/json/base/json_field.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// ignore_for_file: non_constant_identifier_names
|
||||||
|
// ignore_for_file: camel_case_types
|
||||||
|
// ignore_for_file: prefer_single_quotes
|
||||||
|
|
||||||
|
// This file is automatically generated. DO NOT EDIT, all your changes would be lost.
|
||||||
|
|
||||||
|
import 'package:meta/meta_meta.dart';
|
||||||
|
|
||||||
|
@Target({TargetKind.classType})
|
||||||
|
class JsonSerializable {
|
||||||
|
const JsonSerializable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target({TargetKind.field})
|
||||||
|
class JSONField {
|
||||||
|
//Specify the parse field name
|
||||||
|
final String? name;
|
||||||
|
|
||||||
|
//Whether to participate in toJson
|
||||||
|
final bool? serialize;
|
||||||
|
|
||||||
|
//Whether to participate in fromMap
|
||||||
|
final bool? deserialize;
|
||||||
|
|
||||||
|
//Whether to participate in copyWith
|
||||||
|
final bool? copyWith;
|
||||||
|
|
||||||
|
//Enumeration or not
|
||||||
|
final bool? isEnum;
|
||||||
|
|
||||||
|
const JSONField({this.name, this.serialize, this.deserialize, this.isEnum, this.copyWith});
|
||||||
|
}
|
||||||
40
lib/generated/json/base_resp_model.g.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||||
|
import 'package:wallpaper/common/models/base_resp_model.dart';
|
||||||
|
|
||||||
|
BaseRespModel $BaseRespModelFromJson(Map<String, dynamic> json) {
|
||||||
|
final BaseRespModel baseRespModel = BaseRespModel();
|
||||||
|
final int? code = jsonConvert.convert<int>(json['code']);
|
||||||
|
if (code != null) {
|
||||||
|
baseRespModel.code = code;
|
||||||
|
}
|
||||||
|
final String? message = jsonConvert.convert<String>(json['message']);
|
||||||
|
if (message != null) {
|
||||||
|
baseRespModel.message = message;
|
||||||
|
}
|
||||||
|
final dynamic data = json['data'];
|
||||||
|
if (data != null) {
|
||||||
|
baseRespModel.data = data;
|
||||||
|
}
|
||||||
|
return baseRespModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> $BaseRespModelToJson(BaseRespModel entity) {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['code'] = entity.code;
|
||||||
|
data['message'] = entity.message;
|
||||||
|
data['data'] = entity.data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BaseRespModelExtension on BaseRespModel {
|
||||||
|
BaseRespModel copyWith({
|
||||||
|
int? code,
|
||||||
|
String? message,
|
||||||
|
dynamic data,
|
||||||
|
}) {
|
||||||
|
return BaseRespModel()
|
||||||
|
..code = code ?? this.code
|
||||||
|
..message = message ?? this.message
|
||||||
|
..data = data ?? this.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
84
lib/generated/json/wallpaper_model.g.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
import 'package:hive/hive.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)
|
||||||
|
.toList();
|
||||||
|
if (data != null) {
|
||||||
|
wallpaperModel.data = data;
|
||||||
|
}
|
||||||
|
final String? name = jsonConvert.convert<String>(json['name']);
|
||||||
|
if (name != null) {
|
||||||
|
wallpaperModel.name = name;
|
||||||
|
}
|
||||||
|
return wallpaperModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> $WallpaperModelToJson(WallpaperModel entity) {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['data'] = entity.data?.map((v) => v.toJson()).toList();
|
||||||
|
data['name'] = entity.name;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WallpaperModelExtension on WallpaperModel {
|
||||||
|
WallpaperModel copyWith({
|
||||||
|
List<WallpaperData>? data,
|
||||||
|
String? name,
|
||||||
|
}) {
|
||||||
|
return WallpaperModel()
|
||||||
|
..data = data ?? this.data
|
||||||
|
..name = name ?? this.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperData $WallpaperDataFromJson(Map<String, dynamic> json) {
|
||||||
|
final WallpaperData wallpaperData = WallpaperData();
|
||||||
|
final String? banner = jsonConvert.convert<String>(json['banner']);
|
||||||
|
if (banner != null) {
|
||||||
|
wallpaperData.banner = banner;
|
||||||
|
}
|
||||||
|
final String? original = jsonConvert.convert<String>(json['original']);
|
||||||
|
if (original != null) {
|
||||||
|
wallpaperData.original = original;
|
||||||
|
}
|
||||||
|
final String? previewThumb = jsonConvert.convert<String>(
|
||||||
|
json['previewThumb']);
|
||||||
|
if (previewThumb != null) {
|
||||||
|
wallpaperData.previewThumb = previewThumb;
|
||||||
|
}
|
||||||
|
final String? source = jsonConvert.convert<String>(json['source']);
|
||||||
|
if (source != null) {
|
||||||
|
wallpaperData.source = source;
|
||||||
|
}
|
||||||
|
return wallpaperData;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> $WallpaperDataToJson(WallpaperData entity) {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['banner'] = entity.banner;
|
||||||
|
data['original'] = entity.original;
|
||||||
|
data['previewThumb'] = entity.previewThumb;
|
||||||
|
data['source'] = entity.source;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WallpaperDataExtension on WallpaperData {
|
||||||
|
WallpaperData copyWith({
|
||||||
|
String? banner,
|
||||||
|
String? original,
|
||||||
|
String? previewThumb,
|
||||||
|
String? source,
|
||||||
|
}) {
|
||||||
|
return WallpaperData()
|
||||||
|
..banner = banner ?? this.banner
|
||||||
|
..original = original ?? this.original
|
||||||
|
..previewThumb = previewThumb ?? this.previewThumb
|
||||||
|
..source = source ?? this.source;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,28 @@
|
|||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'common/components/easy_loading.dart';
|
import 'package:wallpaper/common/components/easy_loading.dart';
|
||||||
import 'common/config/strings.dart';
|
import 'package:wallpaper/common/storage/hive_storage.dart';
|
||||||
import 'common/theme/themes.dart';
|
import 'package:wallpaper/res/themes/app_themes.dart';
|
||||||
import 'routes/app_pages.dart';
|
import 'package:wallpaper/res/values/strings.dart';
|
||||||
|
import 'package:wallpaper/routes/app_pages.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
// 初始化Hive
|
||||||
|
await initHive();
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
|
|
||||||
// EasyLoading配置
|
// EasyLoading配置
|
||||||
configLoading();
|
configLoading();
|
||||||
|
|
||||||
|
// 沉浸式状态栏
|
||||||
|
if(Platform.isAndroid) {
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(statusBarColor: Colors.transparent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
@ -22,7 +33,7 @@ class MyApp extends StatelessWidget {
|
|||||||
final easyLoading = EasyLoading.init();
|
final easyLoading = EasyLoading.init();
|
||||||
return ScreenUtilInit(
|
return ScreenUtilInit(
|
||||||
// 设计稿中设备的尺寸(单位随意,建议dp,但在使用过程中必须保持一致)
|
// 设计稿中设备的尺寸(单位随意,建议dp,但在使用过程中必须保持一致)
|
||||||
designSize: const Size(360, 690),
|
designSize: const Size(375, 812),
|
||||||
// 是否根据宽度/高度中的最小值适配文字
|
// 是否根据宽度/高度中的最小值适配文字
|
||||||
minTextAdapt: true,
|
minTextAdapt: true,
|
||||||
// 支持分屏尺寸
|
// 支持分屏尺寸
|
||||||
@ -30,11 +41,11 @@ class MyApp extends StatelessWidget {
|
|||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
title: appName,
|
title: appName,
|
||||||
theme: lightTheme,
|
theme: darkTheme,
|
||||||
darkTheme: darkTheme,
|
darkTheme: darkTheme,
|
||||||
themeMode: ThemeMode.light,
|
themeMode: ThemeMode.dark,
|
||||||
getPages: AppPages.routes,
|
getPages: AppPages.routes,
|
||||||
initialRoute: AppPages.home,
|
initialRoute: AppPages.root,
|
||||||
builder: (context, widget) {
|
builder: (context, widget) {
|
||||||
widget = easyLoading(context, widget);
|
widget = easyLoading(context, widget);
|
||||||
return MediaQuery(
|
return MediaQuery(
|
||||||
|
|||||||
10
lib/modules/about/about_binding.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'about_controller.dart';
|
||||||
|
|
||||||
|
class AboutBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => AboutController());
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lib/modules/about/about_controller.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
import 'package:wallpaper/res/values/strings.dart';
|
||||||
|
|
||||||
|
class AboutController extends GetxController {
|
||||||
|
var versionName = ''.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
super.onReady();
|
||||||
|
_getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取版本号
|
||||||
|
void _getVersion() async {
|
||||||
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
versionName.value = '$appName\nversion:${packageInfo.version}';
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/modules/about/about_view.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
class AboutView extends GetView<AboutController> {
|
||||||
|
const AboutView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const BaseAppBar('About'),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 100.h),
|
||||||
|
Obx(() {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text(
|
||||||
|
controller.versionName.value,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
74
lib/modules/favorite/favorite_controller.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
class FavoriteController extends GetxController {
|
||||||
|
static FavoriteController get to => Get.find<FavoriteController>();
|
||||||
|
var viewState = ViewState.loading;
|
||||||
|
late List<WallpaperData> wallpaperList;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
_getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取数据
|
||||||
|
void _getData() {
|
||||||
|
wallpaperList = FavoriteData().getWallpaperData().reversed.toList();
|
||||||
|
_changeViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 改变页面状态
|
||||||
|
void _changeViewState() {
|
||||||
|
viewState = wallpaperList.isNotEmpty ? ViewState.normal : ViewState.empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 点击壁纸
|
||||||
|
void itemOnTap(int index) async {
|
||||||
|
Get.toNamed(AppPages.wallpaperDet, arguments: {
|
||||||
|
'isFavorite': true,
|
||||||
|
'position': index,
|
||||||
|
'wallpaperList': wallpaperList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 长按壁纸
|
||||||
|
void onLongPress(int index) {
|
||||||
|
Get.dialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
HintDialog(
|
||||||
|
content: 'Are you sure you want to delete the wallpaper?',
|
||||||
|
confirmOnTap: () {
|
||||||
|
FavoriteData().delete(index);
|
||||||
|
refreshList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除所有壁纸
|
||||||
|
void deleteAll() {
|
||||||
|
Get.dialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
HintDialog(
|
||||||
|
content: 'Are you sure to delete all wallpapers?',
|
||||||
|
confirmOnTap: () {
|
||||||
|
FavoriteData().clear();
|
||||||
|
wallpaperList.clear();
|
||||||
|
_changeViewState();
|
||||||
|
refresh();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 刷新
|
||||||
|
void refreshList() {
|
||||||
|
_getData();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
lib/modules/favorite/favorite_view.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/base_masonry_gridView.dart';
|
||||||
|
import 'package:wallpaper/common/components/title_bar_widget.dart';
|
||||||
|
import 'package:wallpaper/modules/favorite/favorite_controller.dart';
|
||||||
|
|
||||||
|
class FavoriteView extends GetView<FavoriteController> {
|
||||||
|
const FavoriteView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Get.lazyPut(() => FavoriteController());
|
||||||
|
return SafeArea(
|
||||||
|
child: GetBuilder<FavoriteController>(
|
||||||
|
builder: (logic) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
TitleBarWidget(
|
||||||
|
title: 'Favorites',
|
||||||
|
showMenuBtn: controller.wallpaperList.isNotEmpty,
|
||||||
|
deleteOnTap: controller.deleteAll,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: BaseMasonryGridView(
|
||||||
|
viewState: controller.viewState,
|
||||||
|
wallpaperList: controller.wallpaperList,
|
||||||
|
itemOnTap: (index) => controller.itemOnTap(index),
|
||||||
|
onLongPress: (index) => controller.onLongPress(index),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
import 'home_controller.dart';
|
|
||||||
|
|
||||||
class HomeBinding extends Bindings {
|
|
||||||
@override
|
|
||||||
void dependencies() {
|
|
||||||
Get.lazyPut(() => HomeController());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
lib/modules/home/home_cls/home_cls_controller.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
import 'package:wallpaper/modules/home/home_controller.dart';
|
||||||
|
import 'package:wallpaper/routes/app_pages.dart';
|
||||||
|
|
||||||
|
class HomeClsController extends GetxController {
|
||||||
|
late List<WallpaperData> wallpaperList;
|
||||||
|
|
||||||
|
/// 通过下标获取对应分类的壁纸
|
||||||
|
void initData(int index) {
|
||||||
|
WallpaperModel wallpaperModel = HomeController.to.getWallpaperData(index);
|
||||||
|
wallpaperList = wallpaperModel.data ?? <WallpaperData>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 点击壁纸
|
||||||
|
void itemOnTap(int index) async {
|
||||||
|
Get.toNamed(AppPages.wallpaperDet, arguments: {'position': index, 'wallpaperList': wallpaperList});
|
||||||
|
}
|
||||||
|
}
|
||||||
36
lib/modules/home/home_cls/home_cls_view.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/base_masonry_gridView.dart';
|
||||||
|
import 'package:wallpaper/modules/home/home_cls/home_cls_controller.dart';
|
||||||
|
|
||||||
|
class HomeClsView extends GetView<HomeClsController> {
|
||||||
|
const HomeClsView({super.key, required this.index});
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get tag => index.toString();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Get.lazyPut(() => HomeClsController(), tag: tag);
|
||||||
|
controller.initData(index);
|
||||||
|
// return GridView.builder(
|
||||||
|
// physics: const BouncingScrollPhysics(),
|
||||||
|
// padding: EdgeInsets.zero,
|
||||||
|
// itemCount: controller.wallpaperList.length,
|
||||||
|
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
// crossAxisCount: 2,
|
||||||
|
// mainAxisSpacing: ScreenAdapter.w(8),
|
||||||
|
// crossAxisSpacing: ScreenAdapter.w(8),
|
||||||
|
// childAspectRatio: 0.6,
|
||||||
|
// ),
|
||||||
|
// itemBuilder: (context, index) {
|
||||||
|
// return _wallpaperItem(controller.wallpaperList[index]);
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
return BaseMasonryGridView(
|
||||||
|
wallpaperList: controller.wallpaperList,
|
||||||
|
itemOnTap: (index) => controller.itemOnTap(index),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,40 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
import 'package:wallpaper/generated/assets.dart';
|
||||||
|
import 'package:wallpaper/generated/json/base/json_convert_content.dart';
|
||||||
|
import 'package:wallpaper/modules/home/home_cls/home_cls_view.dart';
|
||||||
|
|
||||||
class HomeController extends GetxController {
|
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||||
|
static HomeController get to => Get.find<HomeController>();
|
||||||
|
late TabController tabController;
|
||||||
|
var wallpaperModelList = <WallpaperModel>[];
|
||||||
|
var clsPages = <HomeClsView>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() async {
|
||||||
|
super.onReady();
|
||||||
|
// 读取json文件,获取数据
|
||||||
|
var data = jsonDecode(await rootBundle.loadString(Assets.jsonWallpaper));
|
||||||
|
if (data != null && data is List) {
|
||||||
|
wallpaperModelList = data.map((e) => WallpaperModel.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
wallpaperModelList = JsonConvert.fromJsonAsT<List<WallpaperModel>>(data) ?? <WallpaperModel>[];
|
||||||
|
tabController = TabController(length: wallpaperModelList.length, vsync: this);
|
||||||
|
clsPages = wallpaperModelList.asMap().entries.map((e) => HomeClsView(index: e.key)).toList();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
tabController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取分类下的壁纸
|
||||||
|
WallpaperModel getWallpaperData(int index) {
|
||||||
|
return wallpaperModelList[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,47 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/keep_alive_wrapper.dart';
|
||||||
import 'home_controller.dart';
|
import 'package:wallpaper/modules/home/home_controller.dart';
|
||||||
|
|
||||||
class HomeView extends GetView<HomeController> {
|
class HomeView extends GetView<HomeController> {
|
||||||
const HomeView({super.key});
|
const HomeView({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
Get.lazyPut(() => HomeController());
|
||||||
appBar: AppBar(
|
return SafeArea(
|
||||||
title: const Text('Wallpaper'),
|
child: GetBuilder<HomeController>(
|
||||||
|
builder: (controller) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (controller.wallpaperModelList.isNotEmpty) ...[
|
||||||
|
_tabBar(),
|
||||||
|
_tabBarView(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
body: const Center(
|
);
|
||||||
child: Text('Wallpaper'),
|
}
|
||||||
|
|
||||||
|
Widget _tabBar() {
|
||||||
|
return TabBar(
|
||||||
|
isScrollable: true,
|
||||||
|
labelPadding: const EdgeInsets.symmetric(horizontal: 8.0).w,
|
||||||
|
indicatorPadding: const EdgeInsets.only(bottom: 6).h,
|
||||||
|
controller: controller.tabController,
|
||||||
|
tabs: controller.wallpaperModelList.map((e) => Tab(text: '${e.name}')).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _tabBarView() {
|
||||||
|
return Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
controller: controller.tabController,
|
||||||
|
children: controller.clsPages.map((e) => KeepAliveWrapper(child: e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
16
lib/modules/me/me_controller.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/routes/app_pages.dart';
|
||||||
|
|
||||||
|
class MeController extends GetxController {
|
||||||
|
// final options = ['About', 'Feedback', 'Share', 'Privacy Policy', 'Terms of Service'];
|
||||||
|
final options = ['About', 'Privacy Policy', 'Terms of Service'];
|
||||||
|
|
||||||
|
void itemOnTap(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
Get.toNamed(AppPages.about);
|
||||||
|
}
|
||||||
|
if (index == 1 || index == 2) {
|
||||||
|
Get.toNamed(AppPages.webPage, arguments: {'title': options[index], 'url': null});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
lib/modules/me/me_view.dart
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/title_bar_widget.dart';
|
||||||
|
import 'package:wallpaper/modules/me/me_controller.dart';
|
||||||
|
|
||||||
|
class MeView extends GetView<MeController> {
|
||||||
|
const MeView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Get.lazyPut(() => MeController());
|
||||||
|
return SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const TitleBarWidget(title: 'Me'),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16).w,
|
||||||
|
itemCount: controller.options.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return _optionItem(index);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
return SizedBox(height: 10.h);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _optionItem(index) {
|
||||||
|
return Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
onTap: () => controller.itemOnTap(index),
|
||||||
|
child: Container(
|
||||||
|
height: 50.h,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16).w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white10,
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
controller.options[index],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.keyboard_arrow_right, color: Colors.white),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
lib/modules/root/root_binding.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/modules/root/root_controller.dart';
|
||||||
|
|
||||||
|
class RootBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => RootController());
|
||||||
|
}
|
||||||
|
}
|
||||||
46
lib/modules/root/root_controller.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/modules/favorite/favorite_view.dart';
|
||||||
|
import 'package:wallpaper/modules/home/home_view.dart';
|
||||||
|
import 'package:wallpaper/modules/me/me_view.dart';
|
||||||
|
|
||||||
|
class RootController extends GetxController {
|
||||||
|
static RootController get to => Get.find<RootController>();
|
||||||
|
final pages = [
|
||||||
|
PageItem('Home', Icons.home, const HomeView()),
|
||||||
|
PageItem('Favorite', Icons.favorite, const FavoriteView()),
|
||||||
|
PageItem('Me', Icons.person, const MeView()),
|
||||||
|
];
|
||||||
|
late PageController pageController;
|
||||||
|
var currentIndex = 0.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
pageController = PageController(initialPage: currentIndex.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
pageController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PageView页面变化
|
||||||
|
void onPageChanged(int index) {
|
||||||
|
currentIndex.value = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BottomNavigationBar切换
|
||||||
|
void onTabTapped(int index) {
|
||||||
|
pageController.jumpToPage(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageItem {
|
||||||
|
late final String label;
|
||||||
|
late final IconData icons;
|
||||||
|
late final StatelessWidget widget;
|
||||||
|
|
||||||
|
PageItem(this.label, this.icons, this.widget);
|
||||||
|
}
|
||||||
36
lib/modules/root/root_view.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/keep_alive_wrapper.dart';
|
||||||
|
import 'package:wallpaper/modules/root/root_controller.dart';
|
||||||
|
|
||||||
|
class RootView extends GetView<RootController> {
|
||||||
|
const RootView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: PageView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
controller: controller.pageController,
|
||||||
|
onPageChanged: (index) => controller.onPageChanged(index),
|
||||||
|
children: controller.pages.map((e) => KeepAliveWrapper(child: e.widget)).toList(),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: Obx(() {
|
||||||
|
return BottomNavigationBar(
|
||||||
|
currentIndex: controller.currentIndex.value,
|
||||||
|
onTap: (index) => controller.onTabTapped(index),
|
||||||
|
items: _bottomNavigationBarItems(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BottomNavigationBarItem> _bottomNavigationBarItems() {
|
||||||
|
return controller.pages.map((e) {
|
||||||
|
return BottomNavigationBarItem(
|
||||||
|
icon: Icon(e.icons),
|
||||||
|
label: e.label,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/modules/wallpaper_det/wallpaper_det_binding.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'wallpaper_det_controller.dart';
|
||||||
|
|
||||||
|
class WallpaperDetBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => WallpaperDetController());
|
||||||
|
}
|
||||||
|
}
|
||||||
105
lib/modules/wallpaper_det/wallpaper_det_controller.dart
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:wallpaper/common/components/easy_loading.dart';
|
||||||
|
import 'package:wallpaper/common/components/wallpaper_picker.dart';
|
||||||
|
import 'package:wallpaper/common/models/wallpaper_model.dart';
|
||||||
|
import 'package:wallpaper/common/storage/favorite_data.dart';
|
||||||
|
import 'package:wallpaper/common/utils/device_info_util.dart';
|
||||||
|
import 'package:wallpaper/common/utils/download_util.dart';
|
||||||
|
import 'package:wallpaper/common/utils/obj_util.dart';
|
||||||
|
import 'package:wallpaper/common/utils/permission_util.dart';
|
||||||
|
import 'package:wallpaper/modules/favorite/favorite_controller.dart';
|
||||||
|
|
||||||
|
class WallpaperDetController extends GetxController {
|
||||||
|
late int position;
|
||||||
|
late final List<WallpaperData> wallpaperList;
|
||||||
|
late final PageController pageController;
|
||||||
|
final options = [OptionItem('Download', Icons.download)];
|
||||||
|
var filePath = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
bool isFavorite = Get.arguments['isFavorite'] ?? false;
|
||||||
|
position = Get.arguments['position'] ?? 0;
|
||||||
|
wallpaperList = Get.arguments['wallpaperList'] ?? <WallpaperData>[];
|
||||||
|
pageController = PageController(initialPage: position);
|
||||||
|
if (!isFavorite) {
|
||||||
|
options.add(OptionItem('Favorite', Icons.favorite));
|
||||||
|
}
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
options.add(OptionItem('Set Wallpaper', Icons.wallpaper));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 图片切换
|
||||||
|
void onPageChanged(int index) {
|
||||||
|
position = index;
|
||||||
|
filePath = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 选项事件
|
||||||
|
void optionOnTap(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
// 下载
|
||||||
|
if (Get.isSnackbarOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownloadUtil.downloadWallpaper('${wallpaperList[position].original}', (savePath) async {
|
||||||
|
filePath = savePath;
|
||||||
|
bool canSave = true;
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
// 在Android 10及以上版本,由于引入了分区存储(Scoped Storage)机制,如果应用只是需要访问媒体文件,而不是整个外部存储
|
||||||
|
// ImageGallerySaver使用MediaStore API来添加图片到相册,无需请求存储权限
|
||||||
|
// Android 10以下版本,需要申请存储权限
|
||||||
|
int sdkVersion = await DeviceInfoUtil.getSDKVersion();
|
||||||
|
if (sdkVersion < 29) {
|
||||||
|
bool status = await PermissionUtil.checkPermission([Permission.storage]);
|
||||||
|
canSave = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canSave) {
|
||||||
|
final result = await ImageGallerySaver.saveFile(savePath);
|
||||||
|
if (result['isSuccess']) {
|
||||||
|
toast('Saved to album');
|
||||||
|
} else {
|
||||||
|
toast('Unable to save to album');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (index == 1) {
|
||||||
|
// 收藏
|
||||||
|
WallpaperData wallpaperData = wallpaperList[position];
|
||||||
|
if (FavoriteData().getWallpaperData().firstWhereOrNull((element) => element.original == wallpaperData.original) != null) {
|
||||||
|
toast('Wallpaper has been collected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FavoriteData().setWallpaperData(wallpaperData);
|
||||||
|
toast('Wallpaper has been collected');
|
||||||
|
// 刷新收藏列表
|
||||||
|
if (Get.isRegistered<FavoriteController>()) {
|
||||||
|
FavoriteController.to.refreshList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 设置壁纸
|
||||||
|
if (ObjUtil.isNotEmptyStr(filePath)) {
|
||||||
|
WallpaperPicker.picker(filePath);
|
||||||
|
} else {
|
||||||
|
DownloadUtil.downloadWallpaper('${wallpaperList[position].original}', (savePath) async {
|
||||||
|
filePath = savePath;
|
||||||
|
WallpaperPicker.picker(savePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionItem {
|
||||||
|
late final String option;
|
||||||
|
late final IconData icons;
|
||||||
|
|
||||||
|
OptionItem(this.option, this.icons);
|
||||||
|
}
|
||||||
95
lib/modules/wallpaper_det/wallpaper_det_view.dart
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
|
import 'package:wallpaper/common/components/base_appbar.dart';
|
||||||
|
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||||
|
import 'package:wallpaper/modules/wallpaper_det/wallpaper_det_controller.dart';
|
||||||
|
|
||||||
|
class WallpaperDetView extends GetView<WallpaperDetController> {
|
||||||
|
const WallpaperDetView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
extendBodyBehindAppBar: true,
|
||||||
|
appBar: const BaseAppBar('', backgroundColor: Colors.transparent, iconColor: Colors.white),
|
||||||
|
body: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
_photoView(),
|
||||||
|
_bottomOption(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 图片查看器
|
||||||
|
Widget _photoView() {
|
||||||
|
return PhotoViewGallery.builder(
|
||||||
|
itemCount: controller.wallpaperList.length,
|
||||||
|
builder: (BuildContext context, int index) {
|
||||||
|
return PhotoViewGalleryPageOptions(
|
||||||
|
imageProvider: NetworkImage('${controller.wallpaperList[index].original}'),
|
||||||
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
|
minScale: PhotoViewComputedScale.contained * 0.5,
|
||||||
|
maxScale: PhotoViewComputedScale.covered * 3,
|
||||||
|
heroAttributes: PhotoViewHeroAttributes(tag: controller.wallpaperList[index]),
|
||||||
|
// onTapUp: (context, details, controllerValue) => Get.back(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loadingBuilder: (context, event) => loadingView(
|
||||||
|
// value: event == null ? 0 : event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 1),
|
||||||
|
),
|
||||||
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
|
backgroundDecoration: const BoxDecoration(color: Colors.black),
|
||||||
|
pageController: controller.pageController,
|
||||||
|
onPageChanged: (i) => controller.onPageChanged(i),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 底部选项
|
||||||
|
Widget _bottomOption() {
|
||||||
|
return Positioned(
|
||||||
|
bottom: ScreenUtil().bottomBarHeight + 20.h,
|
||||||
|
child: Container(
|
||||||
|
width: 0.94.sw,
|
||||||
|
height: kBottomNavigationBarHeight,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.9),
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: controller.options.asMap().entries.map((e) {
|
||||||
|
return Expanded(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(8).r,
|
||||||
|
onTap: () => controller.optionOnTap(e.key),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(e.value.icons, color: Colors.black),
|
||||||
|
Text(
|
||||||
|
e.value.option,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 14.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/modules/web_page/web_page_binding.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'web_page_controller.dart';
|
||||||
|
|
||||||
|
class WebPageBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut(() => WebPageController());
|
||||||
|
}
|
||||||
|
}
|
||||||
38
lib/modules/web_page/web_page_controller.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
|
class WebPageController extends GetxController {
|
||||||
|
var title = '';
|
||||||
|
var url = '';
|
||||||
|
late final WebViewController webViewController;
|
||||||
|
var viewState = ViewState.loading.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
title = Get.arguments['title'];
|
||||||
|
url = Get.arguments['url'] ?? 'https://flutter.cn';
|
||||||
|
webViewController = WebViewController()
|
||||||
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..setBackgroundColor(Get.theme.scaffoldBackgroundColor)
|
||||||
|
..setNavigationDelegate(
|
||||||
|
NavigationDelegate(
|
||||||
|
onProgress: (int progress) {
|
||||||
|
// Update loading bar.
|
||||||
|
},
|
||||||
|
onPageStarted: (String url) {},
|
||||||
|
onPageFinished: (String url) {
|
||||||
|
viewState.value = ViewState.normal;
|
||||||
|
},
|
||||||
|
onWebResourceError: (WebResourceError error) {
|
||||||
|
viewState.value = ViewState.error;
|
||||||
|
},
|
||||||
|
onNavigationRequest: (NavigationRequest request) {
|
||||||
|
return NavigationDecision.navigate;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(Uri.parse(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
23
lib/modules/web_page/web_page_view.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:wallpaper/common/components/base_appbar.dart';
|
||||||
|
import 'package:wallpaper/common/components/view_state_widget.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
import 'web_page_controller.dart';
|
||||||
|
|
||||||
|
class WebPageView extends GetView<WebPageController> {
|
||||||
|
const WebPageView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: BaseAppBar(controller.title),
|
||||||
|
body: Obx(() {
|
||||||
|
return ViewStateWidget(
|
||||||
|
state: controller.viewState.value,
|
||||||
|
child: WebViewWidget(controller: controller.webViewController),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
lib/res/themes/app_colors.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: 颜色
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// 主要颜色
|
||||||
|
const seedColor = Colors.black;
|
||||||
9
lib/res/themes/app_sizes.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/8
|
||||||
|
// Description: 大小
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
/// 状态栏+导航栏
|
||||||
|
double statusToolBarHeight = kToolbarHeight + ScreenUtil().statusBarHeight;
|
||||||
48
lib/res/themes/app_themes.dart
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Author: fengshengxiong
|
||||||
|
// Date: 2024/5/7
|
||||||
|
// Description: 主题
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:wallpaper/res/themes/app_colors.dart';
|
||||||
|
|
||||||
|
/// 深色主题
|
||||||
|
ThemeData darkTheme = ThemeData.dark(useMaterial3: true).copyWith(
|
||||||
|
colorScheme: ColorScheme.fromSeed(seedColor: seedColor, primary: Colors.white),
|
||||||
|
scaffoldBackgroundColor: Colors.black54,
|
||||||
|
appBarTheme: AppBarTheme(
|
||||||
|
elevation: 0.0,
|
||||||
|
centerTitle: true,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
iconTheme: const IconThemeData(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
titleTextStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
tabBarTheme: TabBarTheme(
|
||||||
|
dividerHeight: 0,
|
||||||
|
tabAlignment: TabAlignment.center,
|
||||||
|
indicatorColor: Colors.white,
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 15.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
unselectedLabelStyle: TextStyle(
|
||||||
|
color: const Color(0xFF757575),
|
||||||
|
fontSize: 15.sp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||||
|
elevation: 0.0,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
// selectedFontSize字体默认14会让BottomNavigationBar高度增加,56->58,因此将大小改为12
|
||||||
|
selectedLabelStyle: TextStyle(fontSize: 12.sp, fontWeight: FontWeight.w500),
|
||||||
|
selectedItemColor: Colors.white,
|
||||||
|
unselectedItemColor: const Color(0xFF757575),
|
||||||
|
),
|
||||||
|
);
|
||||||
@ -3,22 +3,47 @@
|
|||||||
// Description: 路由配置
|
// Description: 路由配置
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../modules/home/home_binding.dart';
|
import 'package:wallpaper/modules/about/about_binding.dart';
|
||||||
import '../modules/home/home_view.dart';
|
import 'package:wallpaper/modules/about/about_view.dart';
|
||||||
|
import 'package:wallpaper/modules/root/root_binding.dart';
|
||||||
|
import 'package:wallpaper/modules/root/root_view.dart';
|
||||||
|
import 'package:wallpaper/modules/wallpaper_det/wallpaper_det_binding.dart';
|
||||||
|
import 'package:wallpaper/modules/wallpaper_det/wallpaper_det_view.dart';
|
||||||
|
import 'package:wallpaper/modules/web_page/web_page_binding.dart';
|
||||||
|
import 'package:wallpaper/modules/web_page/web_page_view.dart';
|
||||||
|
|
||||||
class AppPages {
|
class AppPages {
|
||||||
AppPages._();
|
AppPages._();
|
||||||
|
|
||||||
/// 首页
|
/// 根路由
|
||||||
static const home = '/home';
|
static const root = '/';
|
||||||
|
/// 壁纸详情
|
||||||
|
static const wallpaperDet = '/wallpaper_det';
|
||||||
|
/// 关于
|
||||||
|
static const about = '/about';
|
||||||
|
/// WebView页面
|
||||||
|
static const webPage = '/web_page';
|
||||||
|
|
||||||
static final routes = [
|
static final routes = [
|
||||||
GetPage(
|
GetPage(
|
||||||
name: home,
|
name: root,
|
||||||
page: () => const HomeView(),
|
page: () => const RootView(),
|
||||||
bindings: [HomeBinding()],
|
binding: RootBinding(),
|
||||||
participatesInRootNavigator: true,
|
),
|
||||||
preventDuplicates: true,
|
GetPage(
|
||||||
|
name: wallpaperDet,
|
||||||
|
page: () => const WallpaperDetView(),
|
||||||
|
binding: WallpaperDetBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: about,
|
||||||
|
page: () => const AboutView(),
|
||||||
|
binding: AboutBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: webPage,
|
||||||
|
page: () => const WebPageView(),
|
||||||
|
binding: WebPageBinding(),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
// Author: fengshengxiong
|
|
||||||
// Date: 2024/5/7
|
|
||||||
// Description: 屏幕适配器
|
|
||||||
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
|
|
||||||
class ScreenAdapter {
|
|
||||||
/// 当前设备宽度 dp
|
|
||||||
static double getScreenWidth() {
|
|
||||||
return ScreenUtil().screenWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 当前设备高度 dp
|
|
||||||
static double getScreenHeight() {
|
|
||||||
return ScreenUtil().screenHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 状态栏高度 dp 刘海屏会更高
|
|
||||||
static double getStatusBarHeight() {
|
|
||||||
return ScreenUtil().statusBarHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 底部安全区距离 dp
|
|
||||||
static double getBottomBarHeight() {
|
|
||||||
return ScreenUtil().bottomBarHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 屏幕宽度的倍数
|
|
||||||
static double sw(num value) {
|
|
||||||
return value.sw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 屏幕高度的倍数
|
|
||||||
static double sh(num value) {
|
|
||||||
return value.sh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [ScreenUtil.setWidth]
|
|
||||||
static double w(num value) {
|
|
||||||
return value.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [ScreenUtil.setHeight]
|
|
||||||
static double h(num value) {
|
|
||||||
return value.h;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [ScreenUtil.radius]
|
|
||||||
static double r(num value) {
|
|
||||||
return value.r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [ScreenUtil.setSp]
|
|
||||||
static double sp(num value) {
|
|
||||||
return value.sp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||