mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-30 23:58:13 +08:00
opt: HeaderLayout with SlottedMultiChild (#1850)
* opt: HeaderLayout with SlottedMultiChild * ordered * update [skip ci]
This commit is contained in:
committed by
GitHub
parent
b55e102dc3
commit
6782bee11a
@@ -18,12 +18,7 @@
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart'
|
import 'package:flutter/rendering.dart' show BoxHitTestResult, BoxParentData;
|
||||||
show
|
|
||||||
ContainerRenderObjectMixin,
|
|
||||||
MultiChildLayoutParentData,
|
|
||||||
RenderBoxContainerDefaultsMixin,
|
|
||||||
BoxHitTestResult;
|
|
||||||
|
|
||||||
const double kHeaderHeight = 135.0;
|
const double kHeaderHeight = 135.0;
|
||||||
|
|
||||||
@@ -39,79 +34,105 @@ const double _kActionsRightPadding = 15.0;
|
|||||||
|
|
||||||
enum HeaderType { header, avatar, actions }
|
enum HeaderType { header, avatar, actions }
|
||||||
|
|
||||||
class HeaderLayoutWidget extends MultiChildRenderObjectWidget {
|
class HeaderLayoutWidget
|
||||||
|
extends SlottedMultiChildRenderObjectWidget<HeaderType, RenderBox> {
|
||||||
|
final Widget header;
|
||||||
|
final Widget avatar;
|
||||||
|
final Widget actions;
|
||||||
|
|
||||||
const HeaderLayoutWidget({
|
const HeaderLayoutWidget({
|
||||||
super.key,
|
super.key,
|
||||||
super.children,
|
required this.header,
|
||||||
|
required this.avatar,
|
||||||
|
required this.actions,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) {
|
Iterable<HeaderType> get slots => HeaderType.values;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget childForSlot(HeaderType slot) => switch (slot) {
|
||||||
|
.header => header,
|
||||||
|
.avatar => avatar,
|
||||||
|
.actions => actions,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderHeaderWidget createRenderObject(BuildContext context) {
|
||||||
return RenderHeaderWidget();
|
return RenderHeaderWidget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderHeaderWidget extends RenderBox
|
class RenderHeaderWidget extends RenderBox
|
||||||
with
|
with SlottedContainerRenderObjectMixin<HeaderType, RenderBox> {
|
||||||
ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
|
Offset _getOffset(RenderBox child) {
|
||||||
RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
|
return (child.parentData as BoxParentData).offset;
|
||||||
@override
|
}
|
||||||
void setupParentData(RenderBox child) {
|
|
||||||
if (child.parentData is! MultiChildLayoutParentData) {
|
void _setOffset(RenderBox child, Offset offset) {
|
||||||
child.parentData = MultiChildLayoutParentData();
|
(child.parentData as BoxParentData).offset = offset;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performLayout() {
|
void performLayout() {
|
||||||
double height = kHeaderHeight;
|
double height = kHeaderHeight;
|
||||||
RenderBox? child = firstChild;
|
|
||||||
final maxWidth = constraints.maxWidth;
|
final maxWidth = constraints.maxWidth;
|
||||||
while (child != null) {
|
|
||||||
final childParentData = child.parentData! as MultiChildLayoutParentData;
|
|
||||||
|
|
||||||
switch (childParentData.id as HeaderType) {
|
_setOffset(
|
||||||
case HeaderType.header:
|
childForSlot(HeaderType.header)!..layout(constraints),
|
||||||
child.layout(constraints);
|
Offset.zero,
|
||||||
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;
|
_setOffset(
|
||||||
}
|
childForSlot(HeaderType.avatar)!..layout(constraints),
|
||||||
|
const Offset(_kAvatarLeftPadding, _kAvatarTopPadding),
|
||||||
|
);
|
||||||
|
|
||||||
|
final actions = childForSlot(HeaderType.actions)!;
|
||||||
|
final childSize =
|
||||||
|
(actions..layout(
|
||||||
|
BoxConstraints(
|
||||||
|
maxWidth:
|
||||||
|
maxWidth - _kActionsLeftPadding - _kActionsRightPadding,
|
||||||
|
),
|
||||||
|
parentUsesSize: true,
|
||||||
|
))
|
||||||
|
.size;
|
||||||
|
height += (math.max(_kAvatarEffectiveHeight, childSize.height)) + 5.0;
|
||||||
|
_setOffset(
|
||||||
|
actions,
|
||||||
|
Offset(
|
||||||
|
maxWidth - childSize.width - _kActionsRightPadding,
|
||||||
|
_kActionsTopPadding,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
size = constraints.constrainDimensions(maxWidth, height);
|
size = constraints.constrainDimensions(maxWidth, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
defaultPaint(context, offset);
|
for (var slot in HeaderType.values) {
|
||||||
|
final child = childForSlot(slot)!;
|
||||||
|
context.paintChild(child, _getOffset(child) + offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||||
return defaultHitTestChildren(result, position: position);
|
for (var slot in HeaderType.values.reversed) {
|
||||||
|
final child = childForSlot(slot)!;
|
||||||
|
final bool isHit = result.addWithPaintOffset(
|
||||||
|
offset: _getOffset(child),
|
||||||
|
position: position,
|
||||||
|
hitTest: (BoxHitTestResult result, Offset transformed) {
|
||||||
|
return child.hitTest(result, position: transformed);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (isHit) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -481,20 +481,9 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
HeaderLayoutWidget(
|
HeaderLayoutWidget(
|
||||||
children: [
|
header: _buildHeader(context, colorScheme, isLight, width),
|
||||||
LayoutId(
|
avatar: _buildAvatar,
|
||||||
id: HeaderType.header,
|
actions: _buildRight(colorScheme),
|
||||||
child: _buildHeader(context, colorScheme, isLight, width),
|
|
||||||
),
|
|
||||||
LayoutId(
|
|
||||||
id: HeaderType.avatar,
|
|
||||||
child: _buildAvatar,
|
|
||||||
),
|
|
||||||
LayoutId(
|
|
||||||
id: HeaderType.actions,
|
|
||||||
child: _buildRight(colorScheme),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
..._buildLeft(context, colorScheme, isLight),
|
..._buildLeft(context, colorScheme, isLight),
|
||||||
|
|||||||
Reference in New Issue
Block a user