Files
PiliPlus/lib/common/widgets/main_layout.dart
dom e892411092 opt ui
Signed-off-by: dom <githubaccount56556@proton.me>
2026-07-30 15:01:52 +08:00

122 lines
3.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'
show BoxParentData, BoxHitTestResult, ChildLayoutHelper;
enum MainType { sideBar, body, bottomNav }
class MainLayout
extends SlottedMultiChildRenderObjectWidget<MainType, RenderBox> {
const MainLayout({
super.key,
required this.sideBar,
required this.bottomNav,
required this.body,
});
final Widget? sideBar;
final Widget? bottomNav;
final Widget body;
@override
Iterable<MainType> get slots => MainType.values;
@override
Widget? childForSlot(slot) => switch (slot) {
.sideBar => sideBar,
.bottomNav => bottomNav,
.body => body,
};
@override
SlottedContainerRenderObjectMixin<MainType, RenderBox> createRenderObject(
BuildContext context,
) {
return _RenderMainLayout();
}
}
class _RenderMainLayout extends RenderBox
with SlottedContainerRenderObjectMixin<MainType, RenderBox> {
Offset _getOffset(RenderBox child) {
return (child.parentData as BoxParentData).offset;
}
void _setOffset(RenderBox child, Offset offset) {
(child.parentData as BoxParentData).offset = offset;
}
@override
void performLayout() {
final constraints = this.constraints;
size = constraints.biggest;
final Offset bodyOffset;
final BoxConstraints bodyConstraints;
final sideBar = childForSlot(.sideBar);
if (sideBar != null) {
final sideBarWidth = ChildLayoutHelper.layoutChild(
sideBar,
BoxConstraints.tightFor(height: constraints.maxHeight),
).width;
_setOffset(sideBar, .zero);
bodyOffset = Offset(sideBarWidth, 0);
bodyConstraints = BoxConstraints.tightFor(
width: constraints.maxWidth - sideBarWidth,
height: constraints.maxHeight,
);
} else {
final bottomNav = childForSlot(.bottomNav);
if (bottomNav != null) {
final bottomNavSize = ChildLayoutHelper.layoutChild(
bottomNav,
constraints.loosen(),
);
_setOffset(
bottomNav,
Offset(
(constraints.maxWidth - bottomNavSize.width) / 2,
constraints.maxHeight - bottomNavSize.height,
),
);
}
bodyOffset = .zero;
bodyConstraints = BoxConstraints.tightFor(
width: constraints.maxWidth,
height: constraints.maxHeight,
);
}
final body = childForSlot(.body)!..layout(bodyConstraints);
_setOffset(body, bodyOffset);
}
@override
void paint(PaintingContext context, Offset offset) {
for (final child in children) {
context.paintChild(child, _getOffset(child) + offset);
}
}
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
for (final type in MainType.values.reversed) {
final child = childForSlot(type);
if (child == null) continue;
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;
}
}