mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
217 lines
5.3 KiB
Dart
217 lines
5.3 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
const _firstLayoutMaxHeight = 10000.0;
|
|
|
|
class PageViewHeightAdaptable extends StatefulWidget {
|
|
const PageViewHeightAdaptable({
|
|
super.key,
|
|
required this.controller,
|
|
required this.children,
|
|
}) : assert(children.length > 0, 'children must not be empty');
|
|
|
|
final PageController controller;
|
|
final List<Widget> children;
|
|
|
|
@override
|
|
State<PageViewHeightAdaptable> createState() => _PageViewHeightAdaptableState();
|
|
}
|
|
|
|
class _PageViewHeightAdaptableState extends State<PageViewHeightAdaptable> {
|
|
final _sizes = <int, Size>{};
|
|
|
|
@override
|
|
void didUpdateWidget(PageViewHeightAdaptable oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
|
|
_sizes.clear();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListenableBuilder(
|
|
listenable: widget.controller,
|
|
builder: (context, child) => _SizingContainer(
|
|
sizes: _sizes,
|
|
page: widget.controller.hasClients ? widget.controller.page ?? 0 : 0,
|
|
child: child!,
|
|
),
|
|
child: LayoutBuilder(
|
|
builder: (context, constraints) => PageView(
|
|
controller: widget.controller,
|
|
children: [
|
|
for (final (i, child) in widget.children.indexed)
|
|
Stack(
|
|
alignment: Alignment.topCenter,
|
|
clipBehavior: Clip.hardEdge,
|
|
children: [
|
|
SizedBox.fromSize(size: _sizes[i]),
|
|
Positioned(
|
|
left: 0,
|
|
top: 0,
|
|
right: 0,
|
|
child: _SizeAware(
|
|
child: child,
|
|
// don't setState, we'll use it in the layout phase
|
|
onSizeLaidOut: (size) {
|
|
_sizes[i] = size;
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
typedef _OnSizeLaidOutCallback = void Function(Size);
|
|
|
|
class _SizingContainer extends SingleChildRenderObjectWidget {
|
|
const _SizingContainer({
|
|
super.child,
|
|
required this.sizes,
|
|
required this.page,
|
|
});
|
|
|
|
final Map<int, Size> sizes;
|
|
final double page;
|
|
|
|
@override
|
|
_RenderSizingContainer createRenderObject(BuildContext context) {
|
|
return _RenderSizingContainer(
|
|
sizes: sizes,
|
|
page: page,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(
|
|
BuildContext context,
|
|
_RenderSizingContainer renderObject,
|
|
) {
|
|
renderObject
|
|
..sizes = sizes
|
|
..page = page;
|
|
}
|
|
}
|
|
|
|
class _RenderSizingContainer extends RenderProxyBox {
|
|
_RenderSizingContainer({
|
|
RenderBox? child,
|
|
required Map<int, Size> sizes,
|
|
required double page,
|
|
}) : _sizes = sizes,
|
|
_page = page,
|
|
super(child);
|
|
|
|
Map<int, Size> _sizes;
|
|
Map<int, Size> get sizes => _sizes;
|
|
set sizes(Map<int, Size> value) {
|
|
if (_sizes == value) return;
|
|
_sizes = value;
|
|
markNeedsLayout();
|
|
}
|
|
|
|
double _page;
|
|
double get page => _page;
|
|
set page(double value) {
|
|
if (_page == value) return;
|
|
_page = value;
|
|
markNeedsLayout();
|
|
}
|
|
|
|
@override
|
|
void performLayout() {
|
|
if (child case final child?) {
|
|
child.layout(
|
|
constraints.copyWith(
|
|
minWidth: constraints.maxWidth,
|
|
minHeight: 0,
|
|
maxHeight: constraints.hasBoundedHeight ? null : _firstLayoutMaxHeight,
|
|
),
|
|
parentUsesSize: true,
|
|
);
|
|
|
|
final int floorPage = page.floor();
|
|
final int ceilPage = page.ceil();
|
|
|
|
Size? a = sizes[floorPage];
|
|
Size? b = sizes[ceilPage];
|
|
|
|
if (a == null && sizes.isNotEmpty) {
|
|
a = sizes.values.first;
|
|
}
|
|
if (b == null && sizes.isNotEmpty) {
|
|
b = sizes.values.first;
|
|
}
|
|
|
|
a ??= child.getDryLayout(constraints);
|
|
b ??= a;
|
|
|
|
final double t = (page - floorPage).clamp(0.0, 1.0);
|
|
final double height = lerpDouble(a.height, b.height, t) ?? a.height;
|
|
|
|
child.layout(
|
|
constraints.copyWith(minHeight: height, maxHeight: height),
|
|
parentUsesSize: true,
|
|
);
|
|
size = child.size;
|
|
} else {
|
|
size = computeSizeForNoChild(constraints);
|
|
}
|
|
}
|
|
}
|
|
|
|
class _SizeAware extends SingleChildRenderObjectWidget {
|
|
const _SizeAware({
|
|
required Widget child,
|
|
required this.onSizeLaidOut,
|
|
}) : super(child: child);
|
|
|
|
final _OnSizeLaidOutCallback onSizeLaidOut;
|
|
|
|
@override
|
|
_RenderSizeAware createRenderObject(BuildContext context) {
|
|
return _RenderSizeAware(
|
|
onSizeLaidOut: onSizeLaidOut,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, _RenderSizeAware renderObject) {
|
|
renderObject.onSizeLaidOut = onSizeLaidOut;
|
|
}
|
|
}
|
|
|
|
class _RenderSizeAware extends RenderProxyBox {
|
|
_RenderSizeAware({
|
|
RenderBox? child,
|
|
required _OnSizeLaidOutCallback onSizeLaidOut,
|
|
}) : _onSizeLaidOut = onSizeLaidOut,
|
|
super(child);
|
|
|
|
_OnSizeLaidOutCallback? _onSizeLaidOut;
|
|
_OnSizeLaidOutCallback get onSizeLaidOut => _onSizeLaidOut!;
|
|
set onSizeLaidOut(_OnSizeLaidOutCallback value) {
|
|
if (_onSizeLaidOut == value) return;
|
|
_onSizeLaidOut = value;
|
|
markNeedsLayout();
|
|
}
|
|
|
|
@override
|
|
void performLayout() {
|
|
super.performLayout();
|
|
|
|
onSizeLaidOut(
|
|
getDryLayout(
|
|
constraints.copyWith(maxHeight: double.infinity),
|
|
),
|
|
);
|
|
}
|
|
}
|