From 48b3b06f1746d7386f21752e6c55616dff3c6e15 Mon Sep 17 00:00:00 2001
From: czz <862977248@qq.com>
Date: Sat, 7 Jun 2025 17:49:19 +0800
Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E5=B0=8Fe=E5=8A=A8=E7=94=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
assets/img/icon/xiaoe.svg | 2 +-
lib/pages/mh_page/FloatingSvgIcon.dart | 273 +++++++++++++++++++++++++
lib/pages/mh_page/new_Home_page.dart | 116 ++++++-----
3 files changed, 331 insertions(+), 60 deletions(-)
create mode 100644 lib/pages/mh_page/FloatingSvgIcon.dart
diff --git a/assets/img/icon/xiaoe.svg b/assets/img/icon/xiaoe.svg
index f996a3d..49bf9f9 100644
--- a/assets/img/icon/xiaoe.svg
+++ b/assets/img/icon/xiaoe.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/lib/pages/mh_page/FloatingSvgIcon.dart b/lib/pages/mh_page/FloatingSvgIcon.dart
new file mode 100644
index 0000000..1cacf4f
--- /dev/null
+++ b/lib/pages/mh_page/FloatingSvgIcon.dart
@@ -0,0 +1,273 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+// class FloatingSvgIcon extends StatefulWidget {
+// final String assetPath; // SVG 图标路径
+// final double width; // 宽度
+// final double height; // 高度
+// final Duration duration; // 浮动周期
+// final double floatOffset; // 浮动高度(单位:比例)
+// final VoidCallback? onTap; // 点击事件
+
+// const FloatingSvgIcon({
+// super.key,
+// required this.assetPath,
+// this.width = 60,
+// this.height = 60,
+// this.duration = const Duration(milliseconds: 800),
+// this.floatOffset = 0.02,
+// this.onTap,
+// });
+
+// @override
+// State createState() => _FloatingSvgIconState();
+// }
+
+// class _FloatingSvgIconState extends State
+// with SingleTickerProviderStateMixin {
+// late final AnimationController _controller;
+// late final Animation _animation;
+
+// @override
+// void initState() {
+// super.initState();
+
+// _controller = AnimationController(
+// vsync: this,
+// duration: widget.duration,
+// )..repeat(reverse: true);
+
+// _animation = Tween(
+// begin: Offset(0, -widget.floatOffset),
+// end: Offset(0, widget.floatOffset),
+// ).animate(CurvedAnimation(
+// parent: _controller,
+// curve: Curves.easeInOut,
+// ));
+// }
+
+// @override
+// void dispose() {
+// _controller.dispose();
+// super.dispose();
+// }
+
+// // 计算阴影缩放因子,范围大致从 0.8 ~ 1.2
+// double getShadowScale(double dy) {
+// return 1 - dy * 0.25; // dy 为负时,scale > 1,dy 为正时,scale < 1
+// }
+
+// // 计算阴影透明度,范围大致从 0.3 ~ 0.7
+// double getShadowOpacity(double dy) {
+// return 0.5 - dy * 0.3;
+// }
+
+// @override
+// Widget build(BuildContext context) {
+// return AnimatedBuilder(
+// animation: _controller,
+// builder: (context, child) {
+// final dy = _animation.value.dy;
+
+// final shadowScale = getShadowScale(dy).clamp(0.8, 1.2);
+// final shadowOpacity = getShadowOpacity(dy).clamp(0.3, 0.7);
+
+// return SlideTransition(
+// position: _animation,
+// child: GestureDetector(
+// onTap: widget.onTap,
+// child: SizedBox(
+// width: widget.width,
+// height: widget.height + widget.height * 0.3, // 多留点高度给阴影
+// child: Stack(
+// alignment: Alignment.topCenter,
+// children: [
+// // 阴影在底部,椭圆形,随浮动缩放和透明度变化
+// Positioned(
+// bottom: 0,
+// child: Transform.scale(
+// scale: shadowScale,
+// child: Opacity(
+// opacity: shadowOpacity,
+// child: Container(
+// width: widget.width * 0.6,
+// height: widget.height * 0.08,
+// decoration: BoxDecoration(
+// color: Colors.black54,
+// borderRadius:
+// BorderRadius.circular(widget.height * 0.075),
+// boxShadow: [
+// BoxShadow(
+// color: Colors.black26,
+// blurRadius: 8,
+// spreadRadius: 1,
+// ),
+// ],
+// ),
+// ),
+// ),
+// ),
+// ),
+
+// // SVG图标
+// Positioned(
+// top: 0,
+// child: SizedBox(
+// width: widget.width,
+// height: widget.height,
+// child: SvgPicture.asset(
+// widget.assetPath,
+// fit: BoxFit.fill,
+// ),
+// ),
+// ),
+// ],
+// ),
+// ),
+// ),
+// );
+// },
+// );
+// }
+// }
+
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class FloatingSvgIcon extends StatefulWidget {
+ final String assetPath; // SVG 图标路径
+ final double width; // 宽度
+ final double height; // 高度
+ final Duration duration; // 浮动周期
+ final double floatOffset; // 浮动高度(单位:比例)
+ final VoidCallback? onTap; // 点击事件
+
+ const FloatingSvgIcon({
+ super.key,
+ required this.assetPath,
+ this.width = 60,
+ this.height = 60,
+ this.duration = const Duration(milliseconds: 800),
+ this.floatOffset = 0.02,
+ this.onTap,
+ });
+
+ @override
+ State createState() => _FloatingSvgIconState();
+}
+
+class _FloatingSvgIconState extends State
+ with SingleTickerProviderStateMixin {
+ late final AnimationController _controller;
+ late final Animation _animation;
+
+ @override
+ void initState() {
+ super.initState();
+
+ _controller = AnimationController(
+ vsync: this,
+ duration: widget.duration,
+ )..repeat(reverse: true);
+
+ _animation = Tween(
+ begin: Offset(0, -widget.floatOffset),
+ end: Offset(0, widget.floatOffset),
+ ).animate(CurvedAnimation(
+ parent: _controller,
+ curve: Curves.easeInOut,
+ ));
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ // // 计算阴影缩放因子,范围大致从 0.8 ~ 1.2
+ // double getShadowScale(double dy) {
+ // // dy 是当前偏移,负表示向上,正表示向下
+ // // 上浮时阴影变小,下落时阴影变大
+ // return (1 - dy * 5).clamp(0.8, 1.2);
+ // }
+
+ // // 计算阴影透明度,范围大致从 0.3 ~ 0.7
+ // double getShadowOpacity(double dy) {
+ // return (0.5 - dy * 1.5).clamp(0.3, 0.7);
+ // }
+ double getShadowScale(double dy) {
+ // dy负时(上浮)阴影变小,正时(下落)阴影变大
+ return (1 + dy * 5).clamp(0.8, 1.2);
+ }
+
+ double getShadowOpacity(double dy) {
+ return (0.5 + dy * 1.5).clamp(0.3, 0.7);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedBuilder(
+ animation: _controller,
+ builder: (context, child) {
+ final dy = _animation.value.dy;
+
+ final shadowScale = getShadowScale(dy);
+ final shadowOpacity = getShadowOpacity(dy);
+
+ return SizedBox(
+ width: widget.width,
+ height: widget.height + widget.height * 0.3, // 留出阴影空间
+ child: Stack(
+ alignment: Alignment.topCenter,
+ children: [
+ // 固定底部投影,大小随动画变化
+ Positioned(
+ bottom: 4,
+ child: Transform.scale(
+ scale: shadowScale,
+ child: Opacity(
+ opacity: shadowOpacity,
+ child: Container(
+ width: widget.width * 0.5,
+ height: widget.height * 0.12,
+ decoration: BoxDecoration(
+ color: Colors.black,
+ borderRadius:
+ BorderRadius.circular(widget.height * 0.1),
+ boxShadow: const [
+ BoxShadow(
+ color: Colors.black38,
+ blurRadius: 12,
+ spreadRadius: 0,
+ offset: Offset(0, 2),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+
+ // SVG图标上下浮动
+ SlideTransition(
+ position: _animation,
+ child: GestureDetector(
+ onTap: widget.onTap,
+ child: SizedBox(
+ width: widget.width,
+ height: widget.height,
+ child: SvgPicture.asset(
+ widget.assetPath,
+ fit: BoxFit.fill,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/pages/mh_page/new_Home_page.dart b/lib/pages/mh_page/new_Home_page.dart
index 046ac2d..4ac2df3 100644
--- a/lib/pages/mh_page/new_Home_page.dart
+++ b/lib/pages/mh_page/new_Home_page.dart
@@ -9,7 +9,7 @@ import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/device/body_device_controller.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/mh_controller/muser_info_controller.dart';
-
+import 'package:vbvs_app/pages/mh_page/FloatingSvgIcon.dart';
class NewHomePage extends StatefulWidget {
const NewHomePage({super.key});
@@ -96,64 +96,64 @@ class _NewHomePageState extends State {
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
- iconTheme: IconThemeData(color: themeController.currentColor.sc3),
- backgroundColor: Colors.transparent,
- automaticallyImplyLeading: false,
- titleSpacing: 0,
- title: Row(
- children: [
- // 左侧头像
- SizedBox(width: 40.rpx),
- CircleAvatar(
- radius: 27.rpx, // 可根据需求调整
- backgroundImage: login == 1
- ? (userInfoController.model.user!.avatar == null ||
- userInfoController.model.user!.avatar!.isEmpty
- ? const AssetImage(
+ iconTheme:
+ IconThemeData(color: themeController.currentColor.sc3),
+ backgroundColor: Colors.transparent,
+ automaticallyImplyLeading: false,
+ titleSpacing: 0,
+ title: Container(
+ height: 180.rpx,
+ child: Row(
+ children: [
+ // 左侧头像
+ SizedBox(width: 40.rpx),
+ CircleAvatar(
+ radius: 27.rpx, // 可根据需求调整
+ backgroundImage: login == 1
+ ? (userInfoController.model.user!.avatar == null ||
+ userInfoController
+ .model.user!.avatar!.isEmpty
+ ? const AssetImage(
+ "assets/images/people_avatar.png",
+ )
+ : NetworkImage(
+ userInfoController.model.user!.avatar!,
+ ))
+ : const AssetImage(
"assets/images/people_avatar.png",
- )
- : NetworkImage(
- userInfoController.model.user!.avatar!,
- ))
- : const AssetImage(
- "assets/images/people_avatar.png",
- ),
- ),
- SizedBox(width: 23.rpx), // 左侧头像和文本之间的间距
- Text(
- 'Eason Chan',
- style: TextStyle(fontSize: 30.rpx, color: Colors.white),
- ),
+ ),
+ ),
+ SizedBox(width: 23.rpx), // 左侧头像和文本之间的间距
+ Text(
+ 'Eason Chan',
+ style: TextStyle(fontSize: 30.rpx, color: Colors.white),
+ ),
- const Spacer(), // 左右分隔
- // Container(
- // width: 61.rpx,
- // height: 78.rpx,
- // alignment: const Alignment(0, 0),
- // child: Image.asset(
- // "assets/images/xiaoe.png",
- // fit: BoxFit.cover,
- // ),
- // ),
- // SizedBox(width: 46.rpx), // icon 之间的间距
- ClickableContainer(
- backgroundColor: Colors.transparent,
- highlightColor: Colors.transparent,
- padding: EdgeInsets.only(right: 0),
- onTap: () {},
- child: Container(
- height: 60.rpx,
- width: 75.rpx,
- child: SvgPicture.asset(
- 'assets/img/icon/xiaoe.svg',
- // color: Colors.white,
- ))),
+ const Spacer(), // 左右分隔
+ // Container(
+ // width: 61.rpx,
+ // height: 78.rpx,
+ // alignment: const Alignment(0, 0),
+ // child: Image.asset(
+ // "assets/images/xiaoe.png",
+ // fit: BoxFit.cover,
+ // ),
+ // ),
+ // SizedBox(width: 46.rpx), // icon 之间的间距
- // SizedBox(width: 40.rpx),
- ],
- ),
- centerTitle: false,
- ),
+ FloatingSvgIcon(
+ assetPath: 'assets/img/icon/xiaoe.svg',
+ width: 60.rpx,
+ height: 60.rpx,
+ onTap: () {
+ print("点击了小鹅图标");
+ },
+ ),
+
+ SizedBox(width: 40.rpx),
+ ],
+ ),
+ )),
body: SafeArea(
child: Container(
width: MediaQuery.sizeOf(context).width,
@@ -165,9 +165,7 @@ class _NewHomePageState extends State {
// fit: BoxFit.cover,
// ),
// ),
- decoration: BoxDecoration(
-
- ),
+ decoration: BoxDecoration(),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,