From 6668a35f027d5ce182b0363330a9a88d5ab18d23 Mon Sep 17 00:00:00 2001 From: xuhang-x <36baea72@gmail.com> Date: Mon, 22 Jul 2024 10:45:47 +0800 Subject: [PATCH] 1 --- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 2 +- ios/Runner.xcodeproj/project.pbxproj | 27 +++-- ios/Runner/Info.plist | 4 +- .../custom_stacked_list_widget.dart | 106 ++++++++++++++++++ .../custom_transformed_list_item_widget.dart | 93 +++++++++++++++ lib/page/discover/discover_controller.dart | 10 +- lib/page/discover/discover_view.dart | 87 ++++++++++---- pubspec.yaml | 1 + 9 files changed, 295 insertions(+), 37 deletions(-) create mode 100644 lib/common/components/custom_stacked_list_widget.dart create mode 100644 lib/common/components/custom_transformed_list_item_widget.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 3c288dd..97a1c4f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -38,7 +38,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.ai.wallpaper.genie" + applicationId = "com.live.fantasy.wallpaper" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = flutter.minSdkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e9c60c3..5363b0c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 732bf90..e9a873d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -488,18 +488,21 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 34W9G5KLNH; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8B4CW938L2; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.ai.wallpaper.genie; + PRODUCT_BUNDLE_IDENTIFIER = com.live.fantasy.wallpaper; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Fantasy_Wallpaper_Release; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -679,18 +682,21 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 34W9G5KLNH; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8B4CW938L2; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.ai.wallpaper.genie; + PRODUCT_BUNDLE_IDENTIFIER = com.live.fantasy.wallpaper; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Fantasy_Wallpaper_Release; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -710,18 +716,21 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 3; - DEVELOPMENT_TEAM = 34W9G5KLNH; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 8B4CW938L2; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.ai.wallpaper.genie; + PRODUCT_BUNDLE_IDENTIFIER = com.live.fantasy.wallpaper; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Fantasy_Wallpaper_Release; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6004b16..d85a32e 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Wallpaper Genie + Fantasy Wallpaper CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - wallpaper_genie + fantasy_wallpaper CFBundlePackageType APPL CFBundleShortVersionString diff --git a/lib/common/components/custom_stacked_list_widget.dart b/lib/common/components/custom_stacked_list_widget.dart new file mode 100644 index 0000000..a5d2935 --- /dev/null +++ b/lib/common/components/custom_stacked_list_widget.dart @@ -0,0 +1,106 @@ +library stacked_animated_list; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:stacked_animated_list/models/stacked_item.dart'; +import 'package:stacked_animated_list/utils/animated_stack_list_mixin.dart'; +import 'package:wallpaperx/common/components/custom_transformed_list_item_widget.dart'; + +class StackedListWidget extends StatefulWidget { + final List listItems; + final double listItemWidth; + final Duration animationDuration; + final BorderRadiusGeometry? borderRadius; + final double rotationAngle; + final double additionalTranslateOffsetBeyondScreen; + final List? focusedItemShadow; + final ValueChanged? onIndexChanged; + + const StackedListWidget({ + required this.listItems, + required this.listItemWidth, + this.animationDuration = const Duration(milliseconds: 350), + this.borderRadius = const BorderRadius.all(Radius.circular(16)), + this.rotationAngle = 15, + this.additionalTranslateOffsetBeyondScreen = 0, + this.focusedItemShadow, + this.onIndexChanged, + super.key, + }); + + @override + State createState() => _StackedListWidgetState(); +} + +class _StackedListWidgetState extends State + with SingleTickerProviderStateMixin, AnimatedStackListMixin { + final List _stackWidgets = []; + AnimationController? _animationCtr; + Animation? _increaseAnim; + + @override + void initState() { + _animationCtr = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + + _increaseAnim = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationCtr!, curve: Curves.easeOut)); + + _stackWidgets.clear(); + _stackWidgets.addAll(generateStackedItems(widget.listItems)); + _animationCtr?.forward(from: 0); + + super.initState(); + } + + @override + void dispose() { + _animationCtr?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Stack( + children: _stackWidgets + .mapIndexed( + (index, item) { + final isFirstItem = index == 0; + return Center( + child: TransformedListItemWidget( + stackedItem: item, + animation: _increaseAnim!, + widgetWidth: widget.listItemWidth, + focusedWidget: isFirstItem, + borderRadius: widget.borderRadius, + rotationAngle: widget.rotationAngle, + additionalTranslateOffsetBeyondScreen: + widget.additionalTranslateOffsetBeyondScreen, + focusedItemShadow: widget.focusedItemShadow, + onDragEnded: () { + final refreshList = refreshedStackedItems(_stackWidgets); + _stackWidgets.clear(); + _stackWidgets.addAll(refreshList); + _animationCtr?.forward(from: 0); + setState(() { + final String keyValue = + _stackWidgets[0].widget.key is ValueKey + ? (_stackWidgets[0].widget.key as ValueKey).value + : '0'; + widget.onIndexChanged?.call(int.parse(keyValue)); + }); + }, + ), + ); + }, + ) + .toList() + .reversed + .toList(), + ); + } +} diff --git a/lib/common/components/custom_transformed_list_item_widget.dart b/lib/common/components/custom_transformed_list_item_widget.dart new file mode 100644 index 0000000..db23417 --- /dev/null +++ b/lib/common/components/custom_transformed_list_item_widget.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:stacked_animated_list/models/stacked_item.dart'; +import 'package:stacked_animated_list/ui/focused_transformed_item_widget.dart'; +import 'package:stacked_animated_list/ui/unfocused_transformed_item_widget.dart'; +import 'package:stacked_animated_list/utils/animated_stack_list_mixin.dart'; +import 'package:wallpaperx/common/utils/log_print.dart'; +import 'package:wallpaperx/page/discover/discover_controller.dart'; + +class TransformedListItemWidget extends StatelessWidget + with AnimatedStackListMixin { + final StackedItem stackedItem; + final double widgetWidth; + final bool focusedWidget; + final Function() onDragEnded; + final Animation animation; + final BorderRadiusGeometry? borderRadius; + final double rotationAngle; + final double additionalTranslateOffsetBeyondScreen; + final List? focusedItemShadow; + + const TransformedListItemWidget({ + super.key, + required this.stackedItem, + required this.widgetWidth, + required this.focusedWidget, + required this.onDragEnded, + required this.animation, + this.borderRadius = const BorderRadius.all(Radius.circular(16)), + this.rotationAngle = 15, + this.additionalTranslateOffsetBeyondScreen = 0, + this.focusedItemShadow, + }); + + @override + Widget build(BuildContext context) { + final horizontalOffset = getListItemHorizontalOffset( + context, + widgetWidth, + additionalTranslateOffsetBeyondScreen, + ); + + if (focusedWidget) { + final childWidget = Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + boxShadow: focusedItemShadow ?? defaultFocusedItemShadow(), + borderRadius: borderRadius, + ), + child: stackedItem.widget, + ); + + final animatedFromPosType = stackedItem.positionTypeForNextItem.reverse; + + return AnimatedBuilder( + animation: animation, + child: LongPressDraggable( + feedback: childWidget, + childWhenDragging: const SizedBox.shrink(), + delay: const Duration(milliseconds: 10), + child: childWidget, + onDragEnd: (details) { + if (isItemFlicked(details)) { + onDragEnded(); + } else { + if (Get.isRegistered()) { + DiscoverController.to.onTapSwiperItem(); + } + } + }, + ), + builder: (_, child) { + return FocusedTransformedItemWidget( + animation: animation, + rotationAngle: rotationAngle, + horizontalOffset: horizontalOffset, + positionType: animatedFromPosType, + child: child!, + ); + }, + ); + } + + return UnfocusedTransformedItemWidget( + stackedItem: stackedItem, + animation: animation, + rotationAngle: rotationAngle, + horizontalOffset: horizontalOffset, + borderRadius: borderRadius, + positionType: stackedItem.positionType, + ); + } +} diff --git a/lib/page/discover/discover_controller.dart b/lib/page/discover/discover_controller.dart index 522bddd..d4ed5f7 100644 --- a/lib/page/discover/discover_controller.dart +++ b/lib/page/discover/discover_controller.dart @@ -9,12 +9,14 @@ import 'package:get/get_rx/get_rx.dart'; import 'package:wallpaperx/common/components/view_state_widget.dart'; import 'package:wallpaperx/common/http/http_util.dart'; import 'package:wallpaperx/common/http/url.dart'; +import 'package:wallpaperx/common/utils/log_print.dart'; import 'package:wallpaperx/common/utils/shared_util.dart'; import 'package:wallpaperx/entity/image_model.dart'; import 'package:wallpaperx/page/home/home_controller.dart'; import 'package:wallpaperx/routes/app_pages.dart'; class DiscoverController extends GetxController { + static DiscoverController get to => Get.find(); late final EasyRefreshController refreshController; late ScrollController scrollController; @@ -66,7 +68,7 @@ class DiscoverController extends GetxController { if (res["images"] != null) { List list = List.from(res["images"]); images.addAll(list.map((e) => ImageModel.fromJson(e)).toList()); - list = (list..shuffle()).take(10).toList(); + list = (list..shuffle()).take(5).toList(); banners.addAll(list.map((e) => ImageModel.fromJson(e)).toList()); viewState = ViewState.normal.obs; update(["discover_background"]); @@ -75,7 +77,7 @@ class DiscoverController extends GetxController { } _stopTimer(); } - },errorCallback: (e) { + }, errorCallback: (e) { _timer ??= Timer.periodic(const Duration(seconds: 2), (Timer t) { getImages(); }); @@ -99,9 +101,9 @@ class DiscoverController extends GetxController { } /// 点击壁纸 - void onTapSwiperItem(int index) { + void onTapSwiperItem() { Get.toNamed(AppPages.wallpaperDet, arguments: { - 'position': index, + 'position': index.value, 'wallpaperList': banners, }); } diff --git a/lib/page/discover/discover_view.dart b/lib/page/discover/discover_view.dart index 96ff8d5..2704136 100644 --- a/lib/page/discover/discover_view.dart +++ b/lib/page/discover/discover_view.dart @@ -7,10 +7,12 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_swiper_view/flutter_swiper_view.dart'; import 'package:get/get.dart'; import 'package:photo_view/photo_view.dart'; +import 'package:wallpaperx/common/components/custom_stacked_list_widget.dart'; import 'package:wallpaperx/common/components/image_network_widget.dart'; import 'package:wallpaperx/common/components/navigation_bar/search_appbar.dart'; import 'package:wallpaperx/common/components/refresh/base_easyrefresh.dart'; import 'package:wallpaperx/common/components/view_state_widget.dart'; +import 'package:wallpaperx/common/utils/log_print.dart'; import 'package:wallpaperx/entity/image_model.dart'; import 'package:wallpaperx/generated/assets.dart'; import 'package:wallpaperx/page/discover/discover_controller.dart'; @@ -86,27 +88,72 @@ class DiscoverView extends GetView { onTapToCategory: controller.homeController.openDrawer, ), 20.verticalSpace, - Obx(() { - return Expanded( - child: Swiper( - loop: true, - autoplay: true, - scale: .9, - viewportFraction: .85, - itemCount: controller.banners.length, - itemBuilder: (context, index) { - return GestureDetector( - onTap: () => controller.onTapSwiperItem(index), - child: ImageNetworkWidget( - url: controller.banners[index].imageUrl, - radius: 30.r, + // Obx(() { + // return Expanded( + // child: Swiper( + // loop: true, + // autoplay: true, + // scale: .9, + // viewportFraction: .85, + // itemCount: controller.banners.length, + // itemBuilder: (context, index) { + // return GestureDetector( + // onTap: () => controller.onTapSwiperItem(index), + // child: ImageNetworkWidget( + // url: controller.banners[index].imageUrl, + // radius: 30.r, + // ), + // ); + // }, + // onIndexChanged: controller.onIndexChanged, + // ), + // ); + // }), + Obx( + () => controller.banners.isEmpty + ? Container() + : GestureDetector( + onTap: () => LogPrint.d('onTapSwiperItem'), + child: StackedListWidget( + listItems: controller.banners + .asMap() + .entries + .map( + (e) => Container( + key: Key(e.key.toString()), + child: ImageNetworkWidget( + url: controller.banners[e.key].imageUrl, + radius: 30.r, + width: 300.w, + height: 500.w, + fit: BoxFit.fitHeight, + ), + ), + ) + .toList(), + listItemWidth: 200.w, + animationDuration: const Duration(milliseconds: 350), + borderRadius: BorderRadius.circular(30.r), + rotationAngle: 4, + additionalTranslateOffsetBeyondScreen: -93, + onIndexChanged: controller.onIndexChanged, + focusedItemShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.26), + blurRadius: 28, + spreadRadius: 8, + offset: const Offset(8, 16), + ), + BoxShadow( + color: Colors.black.withOpacity(0.26), + blurRadius: 28, + spreadRadius: 8, + offset: const Offset(-8, 2), + ), + ], ), - ); - }, - onIndexChanged: controller.onIndexChanged, - ), - ); - }), + ), + ), 20.verticalSpace, Image.asset( width: 24.w, diff --git a/pubspec.yaml b/pubspec.yaml index 9dfda48..d83c974 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -100,6 +100,7 @@ dependencies: flip_card: ^0.7.0 app_tracking_transparency: ^2.0.5 gradient_borders: ^1.0.1 + stacked_animated_list: ^1.0.1 # Firebase firebase_core: ^2.32.0