diff --git a/lib/pages/member/widget/header_layout_widget.dart b/lib/pages/member/widget/header_layout_widget.dart
new file mode 100644
index 000000000..bf444ccf9
--- /dev/null
+++ b/lib/pages/member/widget/header_layout_widget.dart
@@ -0,0 +1,117 @@
+/*
+ * This file is part of PiliPlus
+ *
+ * PiliPlus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PiliPlus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PiliPlus. If not, see .
+ */
+
+import 'dart:math' as math;
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart'
+ show
+ ContainerRenderObjectMixin,
+ MultiChildLayoutParentData,
+ RenderBoxContainerDefaultsMixin,
+ BoxHitTestResult;
+
+const double kHeaderHeight = 135.0;
+
+const double kAvatarSize = 80.0;
+const double _kAvatarLeftPadding = 20.0;
+const double _kAvatarTopPadding = 110.0;
+const double _kAvatarEffectiveHeight =
+ kAvatarSize - (kHeaderHeight - _kAvatarTopPadding);
+
+const double _kActionsTopPadding = 140.0;
+const double _kActionsLeftPadding = 160.0;
+const double _kActionsRightPadding = 15.0;
+
+enum HeaderType { header, avatar, actions }
+
+class HeaderLayoutWidget extends MultiChildRenderObjectWidget {
+ const HeaderLayoutWidget({
+ super.key,
+ super.children,
+ });
+
+ @override
+ RenderObject createRenderObject(BuildContext context) {
+ return RenderHeaderWidget();
+ }
+}
+
+class RenderHeaderWidget extends RenderBox
+ with
+ ContainerRenderObjectMixin,
+ RenderBoxContainerDefaultsMixin {
+ @override
+ void setupParentData(RenderBox child) {
+ if (child.parentData is! MultiChildLayoutParentData) {
+ child.parentData = MultiChildLayoutParentData();
+ }
+ }
+
+ @override
+ void performLayout() {
+ double height = kHeaderHeight;
+ RenderBox? child = firstChild;
+ final maxWidth = constraints.maxWidth;
+ while (child != null) {
+ final childParentData = child.parentData! as MultiChildLayoutParentData;
+
+ switch (childParentData.id as HeaderType) {
+ case HeaderType.header:
+ child.layout(constraints);
+ childParentData.offset = .zero;
+ case HeaderType.avatar:
+ child.layout(constraints);
+ childParentData.offset = const Offset(
+ _kAvatarLeftPadding,
+ _kAvatarTopPadding,
+ );
+ case HeaderType.actions:
+ final childSize =
+ (child..layout(
+ BoxConstraints(
+ maxWidth:
+ maxWidth -
+ _kActionsLeftPadding -
+ _kActionsRightPadding,
+ ),
+ parentUsesSize: true,
+ ))
+ .size;
+ height += (math.max(_kAvatarEffectiveHeight, childSize.height)) + 5.0;
+ childParentData.offset = Offset(
+ maxWidth - childSize.width - _kActionsRightPadding,
+ _kActionsTopPadding,
+ );
+ }
+
+ child = childParentData.nextSibling;
+ }
+
+ size = constraints.constrainDimensions(maxWidth, height);
+ }
+
+ @override
+ void paint(PaintingContext context, Offset offset) {
+ defaultPaint(context, offset);
+ }
+
+ @override
+ bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
+ return defaultHitTestChildren(result, position: position);
+ }
+}
diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart
index 13fb5ceb3..b6b0a5e19 100644
--- a/lib/pages/member/widget/user_info_card.dart
+++ b/lib/pages/member/widget/user_info_card.dart
@@ -12,6 +12,7 @@ import 'package:PiliPlus/models_new/space/space/pr_info.dart';
import 'package:PiliPlus/pages/fan/view.dart';
import 'package:PiliPlus/pages/follow/view.dart';
import 'package:PiliPlus/pages/follow_type/followed/view.dart';
+import 'package:PiliPlus/pages/member/widget/header_layout_widget.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart';
@@ -128,7 +129,7 @@ class UserInfoCard extends StatelessWidget {
tag: imgUrl,
child: CachedNetworkImage(
fit: .cover,
- height: 135,
+ height: kHeaderHeight,
width: width,
memCacheWidth: width.cacheSize(context),
imageUrl: ImageUtils.thumbnailUrl(imgUrl),
@@ -457,7 +458,7 @@ class UserInfoCard extends StatelessWidget {
tag: card.face ?? '',
child: PendantAvatar(
avatar: card.face,
- size: 80,
+ size: kAvatarSize,
badgeSize: 20,
officialType: card.officialVerify?.type,
isVip: (card.vip?.status ?? -1) > 0,
@@ -478,29 +479,18 @@ class UserInfoCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Stack(
- clipBehavior: Clip.none,
+ HeaderLayoutWidget(
children: [
- Column(
- crossAxisAlignment: .stretch,
- mainAxisSize: MainAxisSize.min,
- children: [
- _buildHeader(context, colorScheme, isLight, width),
- SizedBox(
- height: MediaQuery.textScalerOf(context).scale(30) + 60,
- ),
- ],
+ LayoutId(
+ id: HeaderType.header,
+ child: _buildHeader(context, colorScheme, isLight, width),
),
- Positioned(
- top: 110,
- left: 20,
+ LayoutId(
+ id: HeaderType.avatar,
child: _buildAvatar,
),
- Positioned(
- left: 160,
- top: 140,
- right: 15,
- bottom: 0,
+ LayoutId(
+ id: HeaderType.actions,
child: _buildRight(colorScheme),
),
],