Skip to content

Instantly share code, notes, and snippets.

@callmephil
Created January 22, 2025 21:31
Show Gist options
  • Save callmephil/1b5003478f4f8aa4d1d1640d6b58174e to your computer and use it in GitHub Desktop.
Save callmephil/1b5003478f4f8aa4d1d1640d6b58174e to your computer and use it in GitHub Desktop.
bi-directional-page-overflow
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RenderAligningShiftedBox;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: PageLayout(
children: [
Image.network('https://picsum.photos/1920'),
],
),
);
}
}
class PageLayout extends StatelessWidget {
const PageLayout({
super.key,
required this.children,
});
final List<Widget> children;
@override
Widget build(BuildContext context) {
return PageOverflow(
width: 450.0,
child: Material(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: children),
),
),
),
);
}
}
class PageOverflow extends SingleChildRenderObjectWidget {
const PageOverflow({
super.key,
this.width,
this.height,
this.alignment = Alignment.topLeft,
required super.child,
});
final double? width;
final double? height;
final AlignmentGeometry alignment;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderPageOverflow(
width: width,
height: height,
alignment: alignment,
textDirection: Directionality.of(context),
);
}
@override
void updateRenderObject(
BuildContext context,
RenderPageOverflow renderObject,
) {
renderObject
..width = width
..height = height
..alignment = alignment
..textDirection = Directionality.of(context);
}
}
class RenderPageOverflow extends RenderAligningShiftedBox {
RenderPageOverflow({
double? width,
double? height,
required super.alignment,
required TextDirection super.textDirection,
super.child,
});
double? _width;
double? get width => _width;
set width(double? value) {
if (value != _width) {
_width = value;
markNeedsLayout();
}
}
double? _height;
double? get height => _height;
set height(double? value) {
if (value != _height) {
_height = value;
markNeedsLayout();
}
}
@override
void performLayout() {
// Clamp width/height to parent constraints
final clampedWidth = _width != null
? constraints.constrainWidth(_width!)
: constraints.maxWidth;
final clampedHeight = _height != null
? constraints.constrainHeight(_height!)
: constraints.maxHeight;
final childConstraints = constraints
.copyWith(
minWidth: clampedWidth,
minHeight: clampedHeight,
)
.normalize();
// Safer null-check with pattern matching
if (child case final it?) {
it.layout(childConstraints, parentUsesSize: true);
final maxAllowedHeight = constraints.constrainHeight(
math.max(clampedHeight, it.size.height),
);
size = Size(constraints.maxWidth, maxAllowedHeight);
} else {
// Handle null child case explicitly
size = Size(constraints.maxWidth, clampedHeight);
}
alignChild();
}
}
@callmephil
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment