Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-23 19:50:11 +08:00
parent 7eaf05839a
commit 0ab07a713e
22 changed files with 443 additions and 470 deletions

View File

@@ -1,8 +1,8 @@
import 'package:PiliPlus/common/widgets/only_layout_widget.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// https://github.com/flutter/flutter/issues/18345#issuecomment-1627644396
class DynamicSliverAppBarMedium extends StatefulWidget {
const DynamicSliverAppBarMedium({
this.flexibleSpace,
@@ -93,56 +93,45 @@ class DynamicSliverAppBarMedium extends StatefulWidget {
}
class _DynamicSliverAppBarMediumState extends State<DynamicSliverAppBarMedium> {
final GlobalKey _childKey = GlobalKey();
// As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
// to calculate dynamically the size for the sliver app bar
double _height = 0;
void _updateHeight() {
// Gets the new height and updates the sliver app bar. Needs to be called after the last frame has been rebuild
// otherwise this will throw an error
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (_childKey.currentContext == null) return;
setState(() {
_height = (_childKey.currentContext!.findRenderObject()! as RenderBox)
.size
.height;
widget.afterCalc?.call(_height);
});
});
}
final GlobalKey _key = GlobalKey();
double? _height;
double? _width;
late double _topPadding;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_topPadding = MediaQuery.viewPaddingOf(context).top;
final width = MediaQuery.widthOf(context);
if (_width != width) {
_width = width;
_height = 0;
_updateHeight();
_height = null;
}
}
@override
Widget build(BuildContext context) {
//Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
if (_height == 0) {
if (_height == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_height =
(_key.currentContext!.findRenderObject() as RenderBox).size.height;
widget.afterCalc?.call(_height!);
setState(() {});
});
return SliverToBoxAdapter(
child: UnconstrainedBox(
alignment: Alignment.topLeft,
child: SizedBox(
key: _childKey,
width: _width,
child: widget.flexibleSpace,
child: OnlyLayoutWidget(
child: UnconstrainedBox(
alignment: Alignment.topLeft,
child: SizedBox(
key: _key,
width: _width,
child: widget.flexibleSpace,
),
),
),
);
}
final padding = MediaQuery.viewPaddingOf(context).top;
return SliverAppBar.medium(
leading: widget.leading,
automaticallyImplyLeading: widget.automaticallyImplyLeading,
@@ -170,8 +159,8 @@ class _DynamicSliverAppBarMediumState extends State<DynamicSliverAppBarMedium> {
onStretchTrigger: widget.onStretchTrigger,
shape: widget.shape,
toolbarHeight: kToolbarHeight,
collapsedHeight: kToolbarHeight + padding + 1,
expandedHeight: _height - padding,
collapsedHeight: kToolbarHeight + _topPadding + 1,
expandedHeight: _height! - _topPadding,
leadingWidth: widget.leadingWidth,
toolbarTextStyle: widget.toolbarTextStyle,
titleTextStyle: widget.titleTextStyle,

View File

@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show RenderProxyBox;
class OnlyLayoutWidget extends SingleChildRenderObjectWidget {
const OnlyLayoutWidget({
super.key,
super.child,
});
@override
RenderObject createRenderObject(BuildContext context) => Layout();
}
class Layout extends RenderProxyBox {
@override
void paint(PaintingContext context, Offset offset) {}
}

View File

@@ -1,88 +1,61 @@
import 'package:PiliPlus/common/widgets/only_layout_widget.dart';
import 'package:flutter/material.dart';
/// https://stackoverflow.com/a/76605401
class SelfSizedHorizontalList extends StatefulWidget {
final Widget Function(int index) childBuilder;
final int itemCount;
final double gapSize;
final EdgeInsetsGeometry? padding;
final ScrollController? controller;
const SelfSizedHorizontalList({
super.key,
required this.childBuilder,
required this.itemCount,
this.gapSize = 5,
this.padding,
required this.itemBuilder,
required this.separatorBuilder,
this.controller,
this.padding,
});
final int itemCount;
final EdgeInsets? padding;
final IndexedWidgetBuilder itemBuilder;
final IndexedWidgetBuilder separatorBuilder;
final ScrollController? controller;
@override
State<SelfSizedHorizontalList> createState() =>
_SelfSizedHorizontalListState();
}
class _SelfSizedHorizontalListState extends State<SelfSizedHorizontalList> {
final infoKey = GlobalKey();
double? prevHeight;
double? get height {
if (prevHeight != null) return prevHeight;
prevHeight = infoKey.globalPaintBounds?.height;
return prevHeight;
}
bool get isInit => height == null;
// @override
// void didUpdateWidget(SelfSizedHorizontalList oldWidget) {
// super.didUpdateWidget(oldWidget);
// if (BuildConfig.isDebug) {
// prevHeight = null;
// }
// }
final _key = GlobalKey();
double? _height;
@override
Widget build(BuildContext context) {
if (height == null) {
WidgetsBinding.instance.addPostFrameCallback((v) => setState(() {}));
}
if (widget.itemCount == 0) return const SizedBox.shrink();
if (isInit) {
return Align(
alignment: Alignment.centerLeft,
if (_height == null) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
_height = (_key.currentContext!.findRenderObject() as RenderBox)
.size
.height;
setState(() {});
},
);
return OnlyLayoutWidget(
key: _key,
child: Padding(
key: infoKey,
padding: widget.padding ?? EdgeInsets.zero,
child: widget.childBuilder(0),
padding: widget.padding ?? .zero,
child: widget.itemBuilder(context, 0),
),
);
}
return SizedBox(
height: height,
height: _height,
child: ListView.separated(
controller: widget.controller,
scrollDirection: .horizontal,
padding: widget.padding,
scrollDirection: Axis.horizontal,
itemCount: widget.itemCount,
itemBuilder: (c, i) => widget.childBuilder(i),
separatorBuilder: (c, i) => SizedBox(width: widget.gapSize),
controller: widget.controller,
itemBuilder: widget.itemBuilder,
separatorBuilder: widget.separatorBuilder,
),
);
}
}
extension GlobalKeyExtension on GlobalKey {
Rect? get globalPaintBounds {
final renderObject = currentContext?.findRenderObject();
final translation = renderObject?.getTransformTo(null).getTranslation();
if (translation != null && renderObject?.paintBounds != null) {
final offset = Offset(translation.x, translation.y);
return renderObject!.paintBounds.shift(offset);
} else {
return null;
}
}
}