mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 03:06:59 +08:00
show member collection top
Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart' show RenderProxyBox;
|
||||
import 'package:flutter/rendering.dart' show RenderProxyBox, BoxHitTestResult;
|
||||
|
||||
class CustomHeightWidget extends SingleChildRenderObjectWidget {
|
||||
const CustomHeightWidget({
|
||||
super.key,
|
||||
required this.height,
|
||||
this.height,
|
||||
this.offset = .zero,
|
||||
required super.child,
|
||||
required Widget super.child,
|
||||
});
|
||||
|
||||
final double height;
|
||||
final double? height;
|
||||
|
||||
final Offset offset;
|
||||
|
||||
@@ -34,14 +34,14 @@ class CustomHeightWidget extends SingleChildRenderObjectWidget {
|
||||
|
||||
class RenderCustomHeightWidget extends RenderProxyBox {
|
||||
RenderCustomHeightWidget({
|
||||
required double height,
|
||||
double? height,
|
||||
required Offset offset,
|
||||
}) : _height = height,
|
||||
_offset = offset;
|
||||
|
||||
double _height;
|
||||
double get height => _height;
|
||||
set height(double value) {
|
||||
double? _height;
|
||||
double? get height => _height;
|
||||
set height(double? value) {
|
||||
if (_height == value) return;
|
||||
_height = value;
|
||||
markNeedsLayout();
|
||||
@@ -57,12 +57,40 @@ class RenderCustomHeightWidget extends RenderProxyBox {
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
child!.layout(constraints);
|
||||
size = constraints.constrainDimensions(constraints.maxWidth, height);
|
||||
if (height != null) {
|
||||
child!.layout(constraints.copyWith(maxHeight: .infinity));
|
||||
size = constraints.constrainDimensions(constraints.maxWidth, height!);
|
||||
} else {
|
||||
child!.layout(
|
||||
constraints.copyWith(maxHeight: .infinity),
|
||||
parentUsesSize: true,
|
||||
);
|
||||
size = constraints.constrainDimensions(
|
||||
constraints.maxWidth,
|
||||
child!.size.height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
context.paintChild(child!, offset + _offset);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTest(BoxHitTestResult result, {required Offset position}) {
|
||||
return result.addWithPaintOffset(
|
||||
offset: _offset,
|
||||
position: position,
|
||||
hitTest: (BoxHitTestResult result, Offset transformed) {
|
||||
assert(transformed == position - _offset);
|
||||
return child!.hitTest(result, position: transformed);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void applyPaintTransform(covariant RenderObject child, Matrix4 transform) {
|
||||
transform.translateByDouble(_offset.dx, _offset.dy, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/custom_height_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/rendering/sliver_persistent_header.dart';
|
||||
import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/sliver_persistent_header.dart';
|
||||
import 'package:PiliPlus/common/widgets/only_layout_widget.dart'
|
||||
@@ -24,6 +25,7 @@ import 'package:PiliPlus/common/widgets/only_layout_widget.dart'
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
hide SliverPersistentHeader, SliverPersistentHeaderDelegate;
|
||||
import 'package:flutter/rendering.dart' show RenderOpacity, OpacityLayer;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// ref [SliverAppBar]
|
||||
@@ -133,12 +135,10 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
title: effectiveTitle,
|
||||
actions: actions,
|
||||
automaticallyImplyActions: automaticallyImplyActions,
|
||||
flexibleSpace: maxExtent == .infinity
|
||||
? flexibleSpace
|
||||
: IgnorePointer(
|
||||
ignoring: isScrolledUnder,
|
||||
child: FlexibleSpaceBar(background: flexibleSpace),
|
||||
),
|
||||
flexibleSpace: IgnorePointer(
|
||||
ignoring: isScrolledUnder,
|
||||
child: DynamicFlexibleSpaceBar(background: flexibleSpace),
|
||||
),
|
||||
bottom: bottom,
|
||||
elevation: isScrolledUnder ? elevation : 0.0,
|
||||
scrolledUnderElevation: scrolledUnderElevation,
|
||||
@@ -350,3 +350,148 @@ class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ref [FlexibleSpaceBar]
|
||||
class DynamicFlexibleSpaceBar extends StatefulWidget {
|
||||
const DynamicFlexibleSpaceBar({
|
||||
super.key,
|
||||
required this.background,
|
||||
this.collapseMode = CollapseMode.parallax,
|
||||
});
|
||||
|
||||
final Widget background;
|
||||
|
||||
final CollapseMode collapseMode;
|
||||
|
||||
@override
|
||||
State<DynamicFlexibleSpaceBar> createState() =>
|
||||
_DynamicFlexibleSpaceBarState();
|
||||
}
|
||||
|
||||
class _DynamicFlexibleSpaceBarState extends State<DynamicFlexibleSpaceBar> {
|
||||
double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) {
|
||||
switch (widget.collapseMode) {
|
||||
case CollapseMode.pin:
|
||||
return -(settings.maxExtent - settings.currentExtent);
|
||||
case CollapseMode.none:
|
||||
return 0.0;
|
||||
case CollapseMode.parallax:
|
||||
final double deltaExtent = settings.maxExtent - settings.minExtent;
|
||||
return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final FlexibleSpaceBarSettings settings = context
|
||||
.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>()!;
|
||||
|
||||
double? height;
|
||||
final double opacity;
|
||||
final double topPadding;
|
||||
if (settings.maxExtent == .infinity) {
|
||||
opacity = 1.0;
|
||||
topPadding = 0.0;
|
||||
} else {
|
||||
final double deltaExtent = settings.maxExtent - settings.minExtent;
|
||||
|
||||
// 0.0 -> Expanded
|
||||
// 1.0 -> Collapsed to toolbar
|
||||
final double t = clampDouble(
|
||||
1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
final double fadeStart = math.max(
|
||||
0.0,
|
||||
1.0 - kToolbarHeight / deltaExtent,
|
||||
);
|
||||
const fadeEnd = 1.0;
|
||||
assert(fadeStart <= fadeEnd);
|
||||
// If the min and max extent are the same, the app bar cannot collapse
|
||||
// and the content should be visible, so opacity = 1.
|
||||
opacity = settings.maxExtent == settings.minExtent
|
||||
? 1.0
|
||||
: 1.0 - Interval(fadeStart, fadeEnd).transform(t);
|
||||
|
||||
topPadding = _getCollapsePadding(t, settings);
|
||||
}
|
||||
|
||||
return ClipRect(
|
||||
child: CustomHeightWidget(
|
||||
height: height,
|
||||
offset: Offset(0.0, topPadding),
|
||||
child: _FlexibleSpaceHeaderOpacity(
|
||||
// IOS is relying on this semantics node to correctly traverse
|
||||
// through the app bar when it is collapsed.
|
||||
alwaysIncludeSemantics: true,
|
||||
opacity: opacity,
|
||||
child: widget.background,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// [_FlexibleSpaceHeaderOpacity]
|
||||
class _FlexibleSpaceHeaderOpacity extends SingleChildRenderObjectWidget {
|
||||
const _FlexibleSpaceHeaderOpacity({
|
||||
required this.opacity,
|
||||
required super.child,
|
||||
required this.alwaysIncludeSemantics,
|
||||
});
|
||||
|
||||
final double opacity;
|
||||
final bool alwaysIncludeSemantics;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _RenderFlexibleSpaceHeaderOpacity(
|
||||
opacity: opacity,
|
||||
alwaysIncludeSemantics: alwaysIncludeSemantics,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
covariant _RenderFlexibleSpaceHeaderOpacity renderObject,
|
||||
) {
|
||||
renderObject
|
||||
..alwaysIncludeSemantics = alwaysIncludeSemantics
|
||||
..opacity = opacity;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderFlexibleSpaceHeaderOpacity extends RenderOpacity {
|
||||
_RenderFlexibleSpaceHeaderOpacity({
|
||||
super.opacity,
|
||||
super.alwaysIncludeSemantics,
|
||||
});
|
||||
|
||||
@override
|
||||
bool get isRepaintBoundary => false;
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child == null) {
|
||||
return;
|
||||
}
|
||||
if ((opacity * 255).roundToDouble() <= 0) {
|
||||
layer = null;
|
||||
return;
|
||||
}
|
||||
assert(needsCompositing);
|
||||
layer = context.pushOpacity(
|
||||
offset,
|
||||
(opacity * 255).round(),
|
||||
super.paint,
|
||||
oldLayer: layer as OpacityLayer?,
|
||||
);
|
||||
assert(() {
|
||||
layer!.debugCreator = debugCreator;
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class GalleryViewer extends StatefulWidget {
|
||||
required this.quality,
|
||||
required this.sources,
|
||||
this.initIndex = 0,
|
||||
this.onPageChanged,
|
||||
});
|
||||
|
||||
final double minScale;
|
||||
@@ -61,6 +62,7 @@ class GalleryViewer extends StatefulWidget {
|
||||
final int quality;
|
||||
final List<SourceModel> sources;
|
||||
final int initIndex;
|
||||
final ValueChanged<int>? onPageChanged;
|
||||
|
||||
@override
|
||||
State<GalleryViewer> createState() => _GalleryViewerState();
|
||||
@@ -346,6 +348,7 @@ class _GalleryViewerState extends State<GalleryViewer>
|
||||
_player?.pause();
|
||||
_playIfNeeded(widget.sources[index]);
|
||||
_currIndex.value = index;
|
||||
widget.onPageChanged?.call(index);
|
||||
}
|
||||
|
||||
late final ValueChanged<int>? _onChangePage = widget.sources.length == 1
|
||||
|
||||
@@ -15,11 +15,13 @@ class PendantAvatar extends StatelessWidget {
|
||||
final String? garbPendantImage;
|
||||
final int? roomId;
|
||||
final VoidCallback? onTap;
|
||||
final bool isMemberAvatar;
|
||||
|
||||
const PendantAvatar({
|
||||
super.key,
|
||||
required this.avatar,
|
||||
this.size = 80,
|
||||
required this.size,
|
||||
this.isMemberAvatar = false,
|
||||
double? badgeSize,
|
||||
bool isVip = false,
|
||||
int? officialType,
|
||||
@@ -42,13 +44,12 @@ class PendantAvatar extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isMemberAvatar = size == 80;
|
||||
Widget? pendant;
|
||||
if (showDynDecorate && !garbPendantImage.isNullOrEmpty) {
|
||||
final pendantSize = size * 1.75;
|
||||
pendant = Positioned(
|
||||
// -(size * 1.75 - size) / 2
|
||||
top: -0.375 * size + (size == 80 ? 2 : 0),
|
||||
top: -0.375 * size + (isMemberAvatar ? 2 : 0),
|
||||
child: IgnorePointer(
|
||||
child: NetworkImgLayer(
|
||||
type: .emote,
|
||||
|
||||
Reference in New Issue
Block a user