mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 03:06:59 +08:00
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class FavPgcItemSkeleton extends StatelessWidget {
|
||||
const FavPgcItemSkeleton({super.key});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/skeleton/skeleton.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class SpaceOpusSkeleton extends StatelessWidget {
|
||||
const SpaceOpusSkeleton({super.key});
|
||||
|
||||
@@ -17,10 +17,12 @@ library;
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
||||
import 'package:flutter/material.dart'
|
||||
hide DraggableScrollableSheet, LayoutBuilder;
|
||||
|
||||
/// Controls a [DraggableScrollableSheet].
|
||||
///
|
||||
|
||||
@@ -17,10 +17,12 @@ library;
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart' hide DraggableScrollableSheet;
|
||||
import 'package:flutter/material.dart'
|
||||
hide DraggableScrollableSheet, LayoutBuilder;
|
||||
|
||||
/// Controls a [DraggableScrollableSheet].
|
||||
///
|
||||
|
||||
@@ -1,789 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: uri_does_not_exist_in_doc_import
|
||||
|
||||
/// @docImport 'elevated_button_theme.dart';
|
||||
/// @docImport 'menu_anchor.dart';
|
||||
/// @docImport 'text_button_theme.dart';
|
||||
/// @docImport 'text_theme.dart';
|
||||
/// @docImport 'theme.dart';
|
||||
library;
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart' hide ButtonStyleButton, InkWell;
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
|
||||
///
|
||||
/// Concrete subclasses must override [defaultStyleOf] and [themeStyleOf].
|
||||
///
|
||||
/// See also:
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
/// * <https://m3.material.io/components/buttons/overview>, an overview of each of
|
||||
/// the Material Design button types and how they should be used in designs.
|
||||
abstract class ButtonStyleButton extends StatefulWidget {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
const ButtonStyleButton({
|
||||
super.key,
|
||||
required this.onPressed,
|
||||
required this.onLongPress,
|
||||
required this.onHover,
|
||||
required this.onFocusChange,
|
||||
required this.style,
|
||||
required this.focusNode,
|
||||
required this.autofocus,
|
||||
required this.clipBehavior,
|
||||
this.statesController,
|
||||
this.isSemanticButton = true,
|
||||
@Deprecated(
|
||||
'Remove this parameter as it is now ignored. '
|
||||
'Use ButtonStyle.iconAlignment instead. '
|
||||
'This feature was deprecated after v3.28.0-1.0.pre.',
|
||||
)
|
||||
this.iconAlignment,
|
||||
this.tooltip,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
/// Called when the button is tapped or otherwise activated.
|
||||
///
|
||||
/// If this callback and [onLongPress] are null, then the button will be disabled.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [enabled], which is true if the button is enabled.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// Called when the button is long-pressed.
|
||||
///
|
||||
/// If this callback and [onPressed] are null, then the button will be disabled.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [enabled], which is true if the button is enabled.
|
||||
final VoidCallback? onLongPress;
|
||||
|
||||
/// Called when a pointer enters or exits the button response area.
|
||||
///
|
||||
/// The value passed to the callback is true if a pointer has entered this
|
||||
/// part of the material and false if a pointer has exited this part of the
|
||||
/// material.
|
||||
final ValueChanged<bool>? onHover;
|
||||
|
||||
/// Handler called when the focus changes.
|
||||
///
|
||||
/// Called with true if this widget's node gains focus, and false if it loses
|
||||
/// focus.
|
||||
final ValueChanged<bool>? onFocusChange;
|
||||
|
||||
/// Customizes this button's appearance.
|
||||
///
|
||||
/// Non-null properties of this style override the corresponding
|
||||
/// properties in [themeStyleOf] and [defaultStyleOf]. [WidgetStateProperty]s
|
||||
/// that resolve to non-null values will similarly override the corresponding
|
||||
/// [WidgetStateProperty]s in [themeStyleOf] and [defaultStyleOf].
|
||||
///
|
||||
/// Null by default.
|
||||
final ButtonStyle? style;
|
||||
|
||||
/// {@macro flutter.material.Material.clipBehavior}
|
||||
///
|
||||
/// Defaults to [Clip.none] unless [ButtonStyle.backgroundBuilder] or
|
||||
/// [ButtonStyle.foregroundBuilder] is specified. In those
|
||||
/// cases the default is [Clip.antiAlias].
|
||||
final Clip? clipBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.Focus.focusNode}
|
||||
final FocusNode? focusNode;
|
||||
|
||||
/// {@macro flutter.widgets.Focus.autofocus}
|
||||
final bool autofocus;
|
||||
|
||||
/// {@macro flutter.material.inkwell.statesController}
|
||||
final WidgetStatesController? statesController;
|
||||
|
||||
/// Determine whether this subtree represents a button.
|
||||
///
|
||||
/// If this is null, the screen reader will not announce "button" when this
|
||||
/// is focused. This is useful for [MenuItemButton] and [SubmenuButton] when we
|
||||
/// traverse the menu system.
|
||||
///
|
||||
/// Defaults to true.
|
||||
final bool? isSemanticButton;
|
||||
|
||||
/// {@macro flutter.material.ButtonStyle.iconAlignment}
|
||||
@Deprecated(
|
||||
'Remove this parameter as it is now ignored. '
|
||||
'Use ButtonStyle.iconAlignment instead. '
|
||||
'This feature was deprecated after v3.28.0-1.0.pre.',
|
||||
)
|
||||
final IconAlignment? iconAlignment;
|
||||
|
||||
/// Text that describes the action that will occur when the button is pressed or
|
||||
/// hovered over.
|
||||
///
|
||||
/// This text is displayed when the user long-presses or hovers over the button
|
||||
/// in a tooltip. This string is also used for accessibility.
|
||||
///
|
||||
/// If null, the button will not display a tooltip.
|
||||
final String? tooltip;
|
||||
|
||||
/// Typically the button's label.
|
||||
///
|
||||
/// {@macro flutter.widgets.ProxyWidget.child}
|
||||
final Widget? child;
|
||||
|
||||
/// Returns a [ButtonStyle] that's based primarily on the [Theme]'s
|
||||
/// [ThemeData.textTheme] and [ThemeData.colorScheme], but has most values
|
||||
/// filled out (non-null).
|
||||
///
|
||||
/// The returned style can be overridden by the [style] parameter and by the
|
||||
/// style returned by [themeStyleOf] that some button-specific themes like
|
||||
/// [TextButtonTheme] or [ElevatedButtonTheme] override. For example the
|
||||
/// default style of the [TextButton] subclass can be overridden with its
|
||||
/// [TextButton.style] constructor parameter, or with a [TextButtonTheme].
|
||||
///
|
||||
/// Concrete button subclasses should return a [ButtonStyle] with as many
|
||||
/// non-null properties as possible, where all of the non-null
|
||||
/// [WidgetStateProperty] properties resolve to non-null values.
|
||||
///
|
||||
/// ## Properties that can be null
|
||||
///
|
||||
/// Some properties, like [ButtonStyle.fixedSize] would override other values
|
||||
/// in the same [ButtonStyle] if set, so they are allowed to be null. Here is
|
||||
/// a summary of properties that are allowed to be null when returned in the
|
||||
/// [ButtonStyle] returned by this function, an why:
|
||||
///
|
||||
/// - [ButtonStyle.fixedSize] because it would override other values in the
|
||||
/// same [ButtonStyle], like [ButtonStyle.maximumSize].
|
||||
/// - [ButtonStyle.side] because null is a valid value for a button that has
|
||||
/// no side. [OutlinedButton] returns a non-null default for this, however.
|
||||
/// - [ButtonStyle.backgroundBuilder] and [ButtonStyle.foregroundBuilder]
|
||||
/// because they would override the [ButtonStyle.foregroundColor] and
|
||||
/// [ButtonStyle.backgroundColor] of the same [ButtonStyle].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [themeStyleOf], returns the ButtonStyle of this button's component
|
||||
/// theme.
|
||||
@protected
|
||||
ButtonStyle defaultStyleOf(BuildContext context);
|
||||
|
||||
/// Returns the ButtonStyle that belongs to the button's component theme.
|
||||
///
|
||||
/// The returned style can be overridden by the [style] parameter.
|
||||
///
|
||||
/// Concrete button subclasses should return the ButtonStyle for the
|
||||
/// nearest subclass-specific inherited theme, and if no such theme
|
||||
/// exists, then the same value from the overall [Theme].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [defaultStyleOf], Returns the default [ButtonStyle] for this button.
|
||||
@protected
|
||||
ButtonStyle? themeStyleOf(BuildContext context);
|
||||
|
||||
/// Whether the button is enabled or disabled.
|
||||
///
|
||||
/// Buttons are disabled by default. To enable a button, set its [onPressed]
|
||||
/// or [onLongPress] properties to a non-null value.
|
||||
bool get enabled => onPressed != null || onLongPress != null;
|
||||
|
||||
@override
|
||||
State<ButtonStyleButton> createState() => _ButtonStyleState();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(
|
||||
FlagProperty('enabled', value: enabled, ifFalse: 'disabled'),
|
||||
)
|
||||
..add(
|
||||
DiagnosticsProperty<ButtonStyle>('style', style, defaultValue: null),
|
||||
)
|
||||
..add(
|
||||
DiagnosticsProperty<FocusNode>(
|
||||
'focusNode',
|
||||
focusNode,
|
||||
defaultValue: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns null if [value] is null, otherwise `WidgetStatePropertyAll<T>(value)`.
|
||||
///
|
||||
/// A convenience method for subclasses.
|
||||
static WidgetStateProperty<T>? allOrNull<T>(T? value) =>
|
||||
value == null ? null : WidgetStatePropertyAll<T>(value);
|
||||
|
||||
/// Returns null if [enabled] and [disabled] are null.
|
||||
/// Otherwise, returns a [WidgetStateProperty] that resolves to [disabled]
|
||||
/// when [WidgetState.disabled] is active, and [enabled] otherwise.
|
||||
///
|
||||
/// A convenience method for subclasses.
|
||||
static WidgetStateProperty<Color?>? defaultColor(
|
||||
Color? enabled,
|
||||
Color? disabled,
|
||||
) {
|
||||
if ((enabled ?? disabled) == null) {
|
||||
return null;
|
||||
}
|
||||
return WidgetStateProperty<Color?>.fromMap(<WidgetStatesConstraint, Color?>{
|
||||
WidgetState.disabled: disabled,
|
||||
WidgetState.any: enabled,
|
||||
});
|
||||
}
|
||||
|
||||
/// A convenience method used by subclasses in the framework, that returns an
|
||||
/// interpolated value based on the [fontSizeMultiplier] parameter:
|
||||
///
|
||||
/// * 0 - 1 [geometry1x]
|
||||
/// * 1 - 2 lerp([geometry1x], [geometry2x], [fontSizeMultiplier] - 1)
|
||||
/// * 2 - 3 lerp([geometry2x], [geometry3x], [fontSizeMultiplier] - 2)
|
||||
/// * otherwise [geometry3x]
|
||||
///
|
||||
/// This method is used by the framework for estimating the default paddings to
|
||||
/// use on a button with a text label, when the system text scaling setting
|
||||
/// changes. It's usually supplied with empirical [geometry1x], [geometry2x],
|
||||
/// [geometry3x] values adjusted for different system text scaling values, when
|
||||
/// the unscaled font size is set to 14.0 (the default [TextTheme.labelLarge]
|
||||
/// value).
|
||||
///
|
||||
/// The `fontSizeMultiplier` argument, for historical reasons, is the default
|
||||
/// font size specified in the [ButtonStyle], scaled by the ambient font
|
||||
/// scaler, then divided by 14.0 (the default font size used in buttons).
|
||||
static EdgeInsetsGeometry scaledPadding(
|
||||
EdgeInsetsGeometry geometry1x,
|
||||
EdgeInsetsGeometry geometry2x,
|
||||
EdgeInsetsGeometry geometry3x,
|
||||
double fontSizeMultiplier,
|
||||
) {
|
||||
return switch (fontSizeMultiplier) {
|
||||
<= 1 => geometry1x,
|
||||
< 2 => EdgeInsetsGeometry.lerp(
|
||||
geometry1x,
|
||||
geometry2x,
|
||||
fontSizeMultiplier - 1,
|
||||
)!,
|
||||
< 3 => EdgeInsetsGeometry.lerp(
|
||||
geometry2x,
|
||||
geometry3x,
|
||||
fontSizeMultiplier - 2,
|
||||
)!,
|
||||
_ => geometry3x,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// The base [State] class for buttons whose style is defined by a [ButtonStyle] object.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ButtonStyleButton], the [StatefulWidget] subclass for which this class is the [State].
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled ButtonStyleButton that doesn't elevate when pressed.
|
||||
/// * [OutlinedButton], similar to [TextButton], but with an outline.
|
||||
/// * [TextButton], a simple button without a shadow.
|
||||
class _ButtonStyleState extends State<ButtonStyleButton>
|
||||
with TickerProviderStateMixin {
|
||||
AnimationController? controller;
|
||||
double? elevation;
|
||||
Color? backgroundColor;
|
||||
WidgetStatesController? internalStatesController;
|
||||
|
||||
void handleStatesControllerChange() {
|
||||
// Force a rebuild to resolve WidgetStateProperty properties
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
WidgetStatesController get statesController =>
|
||||
widget.statesController ?? internalStatesController!;
|
||||
|
||||
void initStatesController() {
|
||||
if (widget.statesController == null) {
|
||||
internalStatesController = WidgetStatesController();
|
||||
}
|
||||
statesController
|
||||
..update(WidgetState.disabled, !widget.enabled)
|
||||
..addListener(handleStatesControllerChange);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initStatesController();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(ButtonStyleButton oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.statesController != oldWidget.statesController) {
|
||||
oldWidget.statesController?.removeListener(handleStatesControllerChange);
|
||||
if (widget.statesController != null) {
|
||||
internalStatesController?.dispose();
|
||||
internalStatesController = null;
|
||||
}
|
||||
initStatesController();
|
||||
}
|
||||
if (widget.enabled != oldWidget.enabled) {
|
||||
statesController.update(WidgetState.disabled, !widget.enabled);
|
||||
if (!widget.enabled) {
|
||||
// The button may have been disabled while a press gesture is currently underway.
|
||||
statesController.update(WidgetState.pressed, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
statesController.removeListener(handleStatesControllerChange);
|
||||
internalStatesController?.dispose();
|
||||
controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final IconThemeData iconTheme = IconTheme.of(context);
|
||||
final ButtonStyle? widgetStyle = widget.style;
|
||||
final ButtonStyle? themeStyle = widget.themeStyleOf(context);
|
||||
final ButtonStyle defaultStyle = widget.defaultStyleOf(context);
|
||||
|
||||
T? effectiveValue<T>(T? Function(ButtonStyle? style) getProperty) {
|
||||
final T? widgetValue = getProperty(widgetStyle);
|
||||
final T? themeValue = getProperty(themeStyle);
|
||||
final T? defaultValue = getProperty(defaultStyle);
|
||||
return widgetValue ?? themeValue ?? defaultValue;
|
||||
}
|
||||
|
||||
T? resolve<T>(
|
||||
WidgetStateProperty<T>? Function(ButtonStyle? style) getProperty,
|
||||
) {
|
||||
return effectiveValue((ButtonStyle? style) {
|
||||
return getProperty(style)?.resolve(statesController.value);
|
||||
});
|
||||
}
|
||||
|
||||
Color? effectiveIconColor() {
|
||||
return widgetStyle?.iconColor?.resolve(statesController.value) ??
|
||||
themeStyle?.iconColor?.resolve(statesController.value) ??
|
||||
widgetStyle?.foregroundColor?.resolve(statesController.value) ??
|
||||
themeStyle?.foregroundColor?.resolve(statesController.value) ??
|
||||
defaultStyle.iconColor?.resolve(statesController.value) ??
|
||||
// Fallback to foregroundColor if iconColor is null.
|
||||
defaultStyle.foregroundColor?.resolve(statesController.value);
|
||||
}
|
||||
|
||||
final double? resolvedElevation = resolve<double?>(
|
||||
(ButtonStyle? style) => style?.elevation,
|
||||
);
|
||||
final TextStyle? resolvedTextStyle = resolve<TextStyle?>(
|
||||
(ButtonStyle? style) => style?.textStyle,
|
||||
);
|
||||
Color? resolvedBackgroundColor = resolve<Color?>(
|
||||
(ButtonStyle? style) => style?.backgroundColor,
|
||||
);
|
||||
final Color? resolvedForegroundColor = resolve<Color?>(
|
||||
(ButtonStyle? style) => style?.foregroundColor,
|
||||
);
|
||||
final Color? resolvedShadowColor = resolve<Color?>(
|
||||
(ButtonStyle? style) => style?.shadowColor,
|
||||
);
|
||||
final Color? resolvedSurfaceTintColor = resolve<Color?>(
|
||||
(ButtonStyle? style) => style?.surfaceTintColor,
|
||||
);
|
||||
final EdgeInsetsGeometry? resolvedPadding = resolve<EdgeInsetsGeometry?>(
|
||||
(ButtonStyle? style) => style?.padding,
|
||||
);
|
||||
final Size? resolvedMinimumSize = resolve<Size?>(
|
||||
(ButtonStyle? style) => style?.minimumSize,
|
||||
);
|
||||
final Size? resolvedFixedSize = resolve<Size?>(
|
||||
(ButtonStyle? style) => style?.fixedSize,
|
||||
);
|
||||
final Size? resolvedMaximumSize = resolve<Size?>(
|
||||
(ButtonStyle? style) => style?.maximumSize,
|
||||
);
|
||||
final Color? resolvedIconColor = effectiveIconColor();
|
||||
final double? resolvedIconSize = resolve<double?>(
|
||||
(ButtonStyle? style) => style?.iconSize,
|
||||
);
|
||||
final BorderSide? resolvedSide = resolve<BorderSide?>(
|
||||
(ButtonStyle? style) => style?.side,
|
||||
);
|
||||
final OutlinedBorder? resolvedShape = resolve<OutlinedBorder?>(
|
||||
(ButtonStyle? style) => style?.shape,
|
||||
);
|
||||
|
||||
final WidgetStateMouseCursor mouseCursor = _MouseCursor(
|
||||
(Set<WidgetState> states) => effectiveValue(
|
||||
(ButtonStyle? style) => style?.mouseCursor?.resolve(states),
|
||||
),
|
||||
);
|
||||
|
||||
final WidgetStateProperty<Color?> overlayColor =
|
||||
WidgetStateProperty.resolveWith<Color?>(
|
||||
(Set<WidgetState> states) => effectiveValue(
|
||||
(ButtonStyle? style) => style?.overlayColor?.resolve(states),
|
||||
),
|
||||
);
|
||||
|
||||
final VisualDensity? resolvedVisualDensity = effectiveValue(
|
||||
(ButtonStyle? style) => style?.visualDensity,
|
||||
);
|
||||
final MaterialTapTargetSize? resolvedTapTargetSize = effectiveValue(
|
||||
(ButtonStyle? style) => style?.tapTargetSize,
|
||||
);
|
||||
final Duration? resolvedAnimationDuration = effectiveValue(
|
||||
(ButtonStyle? style) => style?.animationDuration,
|
||||
);
|
||||
final bool resolvedEnableFeedback =
|
||||
effectiveValue((ButtonStyle? style) => style?.enableFeedback) ?? true;
|
||||
final AlignmentGeometry? resolvedAlignment = effectiveValue(
|
||||
(ButtonStyle? style) => style?.alignment,
|
||||
);
|
||||
final Offset densityAdjustment = resolvedVisualDensity!.baseSizeAdjustment;
|
||||
final InteractiveInkFeatureFactory? resolvedSplashFactory = effectiveValue(
|
||||
(ButtonStyle? style) => style?.splashFactory,
|
||||
);
|
||||
final ButtonLayerBuilder? resolvedBackgroundBuilder = effectiveValue(
|
||||
(ButtonStyle? style) => style?.backgroundBuilder,
|
||||
);
|
||||
final ButtonLayerBuilder? resolvedForegroundBuilder = effectiveValue(
|
||||
(ButtonStyle? style) => style?.foregroundBuilder,
|
||||
);
|
||||
|
||||
final Clip effectiveClipBehavior =
|
||||
widget.clipBehavior ??
|
||||
((resolvedBackgroundBuilder ?? resolvedForegroundBuilder) != null
|
||||
? Clip.antiAlias
|
||||
: Clip.none);
|
||||
|
||||
BoxConstraints effectiveConstraints = resolvedVisualDensity
|
||||
.effectiveConstraints(
|
||||
BoxConstraints(
|
||||
minWidth: resolvedMinimumSize!.width,
|
||||
minHeight: resolvedMinimumSize.height,
|
||||
maxWidth: resolvedMaximumSize!.width,
|
||||
maxHeight: resolvedMaximumSize.height,
|
||||
),
|
||||
);
|
||||
if (resolvedFixedSize != null) {
|
||||
final Size size = effectiveConstraints.constrain(resolvedFixedSize);
|
||||
if (size.width.isFinite) {
|
||||
effectiveConstraints = effectiveConstraints.copyWith(
|
||||
minWidth: size.width,
|
||||
maxWidth: size.width,
|
||||
);
|
||||
}
|
||||
if (size.height.isFinite) {
|
||||
effectiveConstraints = effectiveConstraints.copyWith(
|
||||
minHeight: size.height,
|
||||
maxHeight: size.height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Per the Material Design team: don't allow the VisualDensity
|
||||
// adjustment to reduce the width of the left/right padding. If we
|
||||
// did, VisualDensity.compact, the default for desktop/web, would
|
||||
// reduce the horizontal padding to zero.
|
||||
final double dy = densityAdjustment.dy;
|
||||
final double dx = math.max(0, densityAdjustment.dx);
|
||||
final EdgeInsetsGeometry padding = resolvedPadding!
|
||||
.add(EdgeInsets.fromLTRB(dx, dy, dx, dy))
|
||||
.clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity);
|
||||
|
||||
// If an opaque button's background is becoming translucent while its
|
||||
// elevation is changing, change the elevation first. Material implicitly
|
||||
// animates its elevation but not its color. SKIA renders non-zero
|
||||
// elevations as a shadow colored fill behind the Material's background.
|
||||
if (resolvedAnimationDuration! > Duration.zero &&
|
||||
elevation != null &&
|
||||
backgroundColor != null &&
|
||||
elevation != resolvedElevation &&
|
||||
backgroundColor!.value != resolvedBackgroundColor!.value &&
|
||||
backgroundColor!.opacity == 1 &&
|
||||
resolvedBackgroundColor.opacity < 1 &&
|
||||
resolvedElevation == 0) {
|
||||
if (controller?.duration != resolvedAnimationDuration) {
|
||||
controller?.dispose();
|
||||
controller =
|
||||
AnimationController(
|
||||
duration: resolvedAnimationDuration,
|
||||
vsync: this,
|
||||
)..addStatusListener((AnimationStatus status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
setState(() {}); // Rebuild with the final background color.
|
||||
}
|
||||
});
|
||||
}
|
||||
resolvedBackgroundColor =
|
||||
backgroundColor; // Defer changing the background color.
|
||||
controller!.value = 0;
|
||||
controller!.forward();
|
||||
}
|
||||
elevation = resolvedElevation;
|
||||
backgroundColor = resolvedBackgroundColor;
|
||||
|
||||
Widget result = Padding(
|
||||
padding: padding,
|
||||
child: Align(
|
||||
alignment: resolvedAlignment!,
|
||||
widthFactor: 1.0,
|
||||
heightFactor: 1.0,
|
||||
child: resolvedForegroundBuilder != null
|
||||
? resolvedForegroundBuilder(
|
||||
context,
|
||||
statesController.value,
|
||||
widget.child,
|
||||
)
|
||||
: widget.child,
|
||||
),
|
||||
);
|
||||
if (resolvedBackgroundBuilder != null) {
|
||||
result = resolvedBackgroundBuilder(
|
||||
context,
|
||||
statesController.value,
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
result = AnimatedTheme(
|
||||
duration: resolvedAnimationDuration,
|
||||
data: theme.copyWith(
|
||||
iconTheme: iconTheme.merge(
|
||||
IconThemeData(color: resolvedIconColor, size: resolvedIconSize),
|
||||
),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: widget.onPressed,
|
||||
onLongPress: widget.onLongPress,
|
||||
onHover: widget.onHover,
|
||||
mouseCursor: mouseCursor,
|
||||
enableFeedback: resolvedEnableFeedback,
|
||||
focusNode: widget.focusNode,
|
||||
canRequestFocus: widget.enabled,
|
||||
onFocusChange: widget.onFocusChange,
|
||||
autofocus: widget.autofocus,
|
||||
splashFactory: resolvedSplashFactory,
|
||||
overlayColor: overlayColor,
|
||||
highlightColor: Colors.transparent,
|
||||
customBorder: resolvedShape!.copyWith(side: resolvedSide),
|
||||
statesController: statesController,
|
||||
child: result,
|
||||
),
|
||||
);
|
||||
|
||||
if (widget.tooltip != null) {
|
||||
result = Tooltip(message: widget.tooltip, child: result);
|
||||
}
|
||||
|
||||
final Size minSize;
|
||||
switch (resolvedTapTargetSize!) {
|
||||
case MaterialTapTargetSize.padded:
|
||||
minSize = Size(
|
||||
kMinInteractiveDimension + densityAdjustment.dx,
|
||||
kMinInteractiveDimension + densityAdjustment.dy,
|
||||
);
|
||||
assert(minSize.width >= 0.0);
|
||||
assert(minSize.height >= 0.0);
|
||||
case MaterialTapTargetSize.shrinkWrap:
|
||||
minSize = Size.zero;
|
||||
}
|
||||
|
||||
return Semantics(
|
||||
container: true,
|
||||
button: widget.isSemanticButton,
|
||||
enabled: widget.enabled,
|
||||
child: _InputPadding(
|
||||
minSize: minSize,
|
||||
child: ConstrainedBox(
|
||||
constraints: effectiveConstraints,
|
||||
child: Material(
|
||||
elevation: resolvedElevation!,
|
||||
textStyle: resolvedTextStyle?.copyWith(
|
||||
color: resolvedForegroundColor,
|
||||
),
|
||||
shape: resolvedShape.copyWith(side: resolvedSide),
|
||||
color: resolvedBackgroundColor,
|
||||
shadowColor: resolvedShadowColor,
|
||||
surfaceTintColor: resolvedSurfaceTintColor,
|
||||
type: resolvedBackgroundColor == null
|
||||
? MaterialType.transparency
|
||||
: MaterialType.button,
|
||||
animationDuration: resolvedAnimationDuration,
|
||||
clipBehavior: effectiveClipBehavior,
|
||||
borderOnForeground: false,
|
||||
child: result,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MouseCursor extends WidgetStateMouseCursor {
|
||||
const _MouseCursor(this.resolveCallback);
|
||||
|
||||
final WidgetPropertyResolver<MouseCursor?> resolveCallback;
|
||||
|
||||
@override
|
||||
MouseCursor resolve(Set<WidgetState> states) => resolveCallback(states)!;
|
||||
|
||||
@override
|
||||
String get debugDescription => 'ButtonStyleButton_MouseCursor';
|
||||
}
|
||||
|
||||
/// A widget to pad the area around a [ButtonStyleButton]'s inner [Material].
|
||||
///
|
||||
/// Redirect taps that occur in the padded area around the child to the center
|
||||
/// of the child. This increases the size of the button and the button's
|
||||
/// "tap target", but not its material or its ink splashes.
|
||||
class _InputPadding extends SingleChildRenderObjectWidget {
|
||||
const _InputPadding({super.child, required this.minSize});
|
||||
|
||||
final Size minSize;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _RenderInputPadding(minSize);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
covariant _RenderInputPadding renderObject,
|
||||
) {
|
||||
renderObject.minSize = minSize;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderInputPadding extends RenderShiftedBox {
|
||||
_RenderInputPadding(this._minSize, [RenderBox? child]) : super(child);
|
||||
|
||||
Size get minSize => _minSize;
|
||||
Size _minSize;
|
||||
set minSize(Size value) {
|
||||
if (_minSize == value) {
|
||||
return;
|
||||
}
|
||||
_minSize = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
if (child != null) {
|
||||
return math.max(child!.getMinIntrinsicWidth(height), minSize.width);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
if (child != null) {
|
||||
return math.max(child!.getMinIntrinsicHeight(width), minSize.height);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicWidth(double height) {
|
||||
if (child != null) {
|
||||
return math.max(child!.getMaxIntrinsicWidth(height), minSize.width);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicHeight(double width) {
|
||||
if (child != null) {
|
||||
return math.max(child!.getMaxIntrinsicHeight(width), minSize.height);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
Size _computeSize({
|
||||
required BoxConstraints constraints,
|
||||
required ChildLayouter layoutChild,
|
||||
}) {
|
||||
if (child != null) {
|
||||
final Size childSize = layoutChild(child!, constraints);
|
||||
final double width = math.max(childSize.width, minSize.width);
|
||||
final double height = math.max(childSize.height, minSize.height);
|
||||
return constraints.constrain(Size(width, height));
|
||||
}
|
||||
return Size.zero;
|
||||
}
|
||||
|
||||
@override
|
||||
Size computeDryLayout(BoxConstraints constraints) {
|
||||
return _computeSize(
|
||||
constraints: constraints,
|
||||
layoutChild: ChildLayoutHelper.dryLayoutChild,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDryBaseline(
|
||||
covariant BoxConstraints constraints,
|
||||
TextBaseline baseline,
|
||||
) {
|
||||
final RenderBox? child = this.child;
|
||||
if (child == null) {
|
||||
return null;
|
||||
}
|
||||
final double? result = child.getDryBaseline(constraints, baseline);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
final Size childSize = child.getDryLayout(constraints);
|
||||
return result +
|
||||
Alignment.center
|
||||
.alongOffset(getDryLayout(constraints) - childSize as Offset)
|
||||
.dy;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
size = _computeSize(
|
||||
constraints: constraints,
|
||||
layoutChild: ChildLayoutHelper.layoutChild,
|
||||
);
|
||||
if (child != null) {
|
||||
final childParentData = child!.parentData! as BoxParentData;
|
||||
childParentData.offset = Alignment.center.alongOffset(
|
||||
size - child!.size as Offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTest(BoxHitTestResult result, {required Offset position}) {
|
||||
if (super.hitTest(result, position: position)) {
|
||||
return true;
|
||||
}
|
||||
final Offset center = child!.size.center(Offset.zero);
|
||||
return result.addWithRawTransform(
|
||||
transform: MatrixUtils.forceToPoint(center),
|
||||
position: center,
|
||||
hitTest: (BoxHitTestResult result, Offset position) {
|
||||
assert(position == center);
|
||||
return child!.hitTest(result, position: center);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,625 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: uri_does_not_exist_in_doc_import
|
||||
|
||||
/// @docImport 'elevated_button.dart';
|
||||
/// @docImport 'filled_button.dart';
|
||||
/// @docImport 'material.dart';
|
||||
/// @docImport 'outlined_button.dart';
|
||||
library;
|
||||
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/button.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
hide ButtonStyleButton, TextButton, InkWell;
|
||||
|
||||
/// A Material Design "Text Button".
|
||||
///
|
||||
/// Use text buttons on toolbars, in dialogs, or inline with other
|
||||
/// content but offset from that content with padding so that the
|
||||
/// button's presence is obvious. Text buttons do not have visible
|
||||
/// borders and must therefore rely on their position relative to
|
||||
/// other content for context. In dialogs and cards, they should be
|
||||
/// grouped together in one of the bottom corners. Avoid using text
|
||||
/// buttons where they would blend in with other content, for example
|
||||
/// in the middle of lists.
|
||||
///
|
||||
/// A text button is a label [child] displayed on a (zero elevation)
|
||||
/// [Material] widget. The label's [Text] and [Icon] widgets are
|
||||
/// displayed in the [style]'s [ButtonStyle.foregroundColor]. The
|
||||
/// button reacts to touches by filling with the [style]'s
|
||||
/// [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// The text button's default style is defined by [defaultStyleOf].
|
||||
/// The style of this text button can be overridden with its [style]
|
||||
/// parameter. The style of all text buttons in a subtree can be
|
||||
/// overridden with the [TextButtonTheme] and the style of all of the
|
||||
/// text buttons in an app can be overridden with the [Theme]'s
|
||||
/// [ThemeData.textButtonTheme] property.
|
||||
///
|
||||
/// The static [styleFrom] method is a convenient way to create a
|
||||
/// text button [ButtonStyle] from simple values.
|
||||
///
|
||||
/// If the [onPressed] and [onLongPress] callbacks are null, then this
|
||||
/// button will be disabled, it will not react to touch.
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample shows various ways to configure TextButtons, from the
|
||||
/// simplest default appearance to versions that don't resemble
|
||||
/// Material Design at all.
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/text_button/text_button.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample demonstrates using the [statesController] parameter to create a button
|
||||
/// that adds support for [WidgetState.selected].
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/text_button/text_button.1.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * <https://material.io/design/components/buttons.html>
|
||||
/// * <https://m3.material.io/components/buttons>
|
||||
class TextButton extends ButtonStyleButton {
|
||||
/// Create a [TextButton].
|
||||
const TextButton({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
super.autofocus = false,
|
||||
super.clipBehavior,
|
||||
super.statesController,
|
||||
super.isSemanticButton,
|
||||
required Widget super.child,
|
||||
}) : _addPadding = false;
|
||||
|
||||
/// Create a text button from a pair of widgets that serve as the button's
|
||||
/// [icon] and [label].
|
||||
///
|
||||
/// The icon and label are arranged in a row and padded by 8 logical pixels
|
||||
/// at the ends, with an 8 pixel gap in between.
|
||||
///
|
||||
/// If [icon] is null, this constructor will create a [TextButton]
|
||||
/// that doesn't display an icon.
|
||||
///
|
||||
/// {@macro flutter.material.ButtonStyle.iconAlignment}
|
||||
///
|
||||
TextButton.icon({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
super.autofocus = false,
|
||||
super.clipBehavior = Clip.none,
|
||||
super.statesController,
|
||||
Widget? icon,
|
||||
required Widget label,
|
||||
IconAlignment? iconAlignment,
|
||||
}) : _addPadding = icon != null,
|
||||
super(
|
||||
child: icon != null
|
||||
? _TextButtonWithIconChild(
|
||||
label: label,
|
||||
icon: icon,
|
||||
buttonStyle: style,
|
||||
iconAlignment: iconAlignment,
|
||||
)
|
||||
: label,
|
||||
);
|
||||
|
||||
final bool _addPadding;
|
||||
|
||||
/// A static convenience method that constructs a text button
|
||||
/// [ButtonStyle] given simple values.
|
||||
///
|
||||
/// The [foregroundColor] and [disabledForegroundColor] colors are used
|
||||
/// to create a [WidgetStateProperty] [ButtonStyle.foregroundColor], and
|
||||
/// a derived [ButtonStyle.overlayColor] if [overlayColor] isn't specified.
|
||||
///
|
||||
/// The [backgroundColor] and [disabledBackgroundColor] colors are
|
||||
/// used to create a [WidgetStateProperty] [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// The [iconColor], [disabledIconColor] are used to construct
|
||||
/// [ButtonStyle.iconColor] and [iconSize] is used to construct
|
||||
/// [ButtonStyle.iconSize].
|
||||
///
|
||||
/// If [iconColor] is null, the button icon will use [foregroundColor]. If [foregroundColor] is also
|
||||
/// null, the button icon will use the default icon color.
|
||||
///
|
||||
/// If [overlayColor] is specified and its value is [Colors.transparent]
|
||||
/// then the pressed/focused/hovered highlights are effectively defeated.
|
||||
/// Otherwise a [WidgetStateProperty] with the same opacities as the
|
||||
/// default is created.
|
||||
///
|
||||
/// All of the other parameters are either used directly or used to
|
||||
/// create a [WidgetStateProperty] with a single value for all
|
||||
/// states.
|
||||
///
|
||||
/// All parameters default to null. By default this method returns
|
||||
/// a [ButtonStyle] that doesn't override anything.
|
||||
///
|
||||
/// For example, to override the default text and icon colors for a
|
||||
/// [TextButton], as well as its overlay color, with all of the
|
||||
/// standard opacity adjustments for the pressed, focused, and
|
||||
/// hovered states, one could write:
|
||||
///
|
||||
/// ```dart
|
||||
/// TextButton(
|
||||
/// style: TextButton.styleFrom(foregroundColor: Colors.green),
|
||||
/// child: const Text('Give Kate a mix tape'),
|
||||
/// onPressed: () {
|
||||
/// // ...
|
||||
/// },
|
||||
/// ),
|
||||
/// ```
|
||||
static ButtonStyle styleFrom({
|
||||
Color? foregroundColor,
|
||||
Color? backgroundColor,
|
||||
Color? disabledForegroundColor,
|
||||
Color? disabledBackgroundColor,
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
Color? iconColor,
|
||||
double? iconSize,
|
||||
IconAlignment? iconAlignment,
|
||||
Color? disabledIconColor,
|
||||
Color? overlayColor,
|
||||
double? elevation,
|
||||
TextStyle? textStyle,
|
||||
EdgeInsetsGeometry? padding,
|
||||
Size? minimumSize,
|
||||
Size? fixedSize,
|
||||
Size? maximumSize,
|
||||
BorderSide? side,
|
||||
OutlinedBorder? shape,
|
||||
MouseCursor? enabledMouseCursor,
|
||||
MouseCursor? disabledMouseCursor,
|
||||
VisualDensity? visualDensity,
|
||||
MaterialTapTargetSize? tapTargetSize,
|
||||
Duration? animationDuration,
|
||||
bool? enableFeedback,
|
||||
AlignmentGeometry? alignment,
|
||||
InteractiveInkFeatureFactory? splashFactory,
|
||||
ButtonLayerBuilder? backgroundBuilder,
|
||||
ButtonLayerBuilder? foregroundBuilder,
|
||||
}) {
|
||||
final WidgetStateProperty<Color?>? backgroundColorProp = switch ((
|
||||
backgroundColor,
|
||||
disabledBackgroundColor,
|
||||
)) {
|
||||
(_?, null) => WidgetStatePropertyAll<Color?>(backgroundColor),
|
||||
(_, _) => ButtonStyleButton.defaultColor(
|
||||
backgroundColor,
|
||||
disabledBackgroundColor,
|
||||
),
|
||||
};
|
||||
final WidgetStateProperty<Color?>? iconColorProp = switch ((
|
||||
iconColor,
|
||||
disabledIconColor,
|
||||
)) {
|
||||
(_?, null) => WidgetStatePropertyAll<Color?>(iconColor),
|
||||
(_, _) => ButtonStyleButton.defaultColor(iconColor, disabledIconColor),
|
||||
};
|
||||
final WidgetStateProperty<Color?>? overlayColorProp = switch ((
|
||||
foregroundColor,
|
||||
overlayColor,
|
||||
)) {
|
||||
(null, null) => null,
|
||||
(_, Color(a: 0.0)) => WidgetStatePropertyAll<Color?>(overlayColor),
|
||||
(_, final Color color) || (final Color color, _) =>
|
||||
WidgetStateProperty<Color?>.fromMap(<WidgetState, Color?>{
|
||||
WidgetState.pressed: color.withValues(alpha: 0.1),
|
||||
WidgetState.hovered: color.withValues(alpha: 0.08),
|
||||
WidgetState.focused: color.withValues(alpha: 0.1),
|
||||
}),
|
||||
};
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: ButtonStyleButton.allOrNull<TextStyle>(textStyle),
|
||||
foregroundColor: ButtonStyleButton.defaultColor(
|
||||
foregroundColor,
|
||||
disabledForegroundColor,
|
||||
),
|
||||
backgroundColor: backgroundColorProp,
|
||||
overlayColor: overlayColorProp,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
iconColor: iconColorProp,
|
||||
iconSize: ButtonStyleButton.allOrNull<double>(iconSize),
|
||||
iconAlignment: iconAlignment,
|
||||
elevation: ButtonStyleButton.allOrNull<double>(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: WidgetStateProperty<MouseCursor?>.fromMap(
|
||||
<WidgetStatesConstraint, MouseCursor?>{
|
||||
WidgetState.disabled: disabledMouseCursor,
|
||||
WidgetState.any: enabledMouseCursor,
|
||||
},
|
||||
),
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
enableFeedback: enableFeedback,
|
||||
alignment: alignment,
|
||||
splashFactory: splashFactory,
|
||||
backgroundBuilder: backgroundBuilder,
|
||||
foregroundBuilder: foregroundBuilder,
|
||||
);
|
||||
}
|
||||
|
||||
/// Defines the button's default appearance.
|
||||
///
|
||||
/// {@template flutter.material.text_button.default_style_of}
|
||||
/// The button [child]'s [Text] and [Icon] widgets are rendered with
|
||||
/// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
|
||||
/// the style's overlay color when the button is focused, hovered
|
||||
/// or pressed. The button's background color becomes its [Material]
|
||||
/// color and is transparent by default.
|
||||
///
|
||||
/// All of the [ButtonStyle]'s defaults appear below.
|
||||
///
|
||||
/// In this list "Theme.foo" is shorthand for
|
||||
/// `Theme.of(context).foo`. Color scheme values like
|
||||
/// "onSurface(0.38)" are shorthand for
|
||||
/// `onSurface.withValues(alpha: 0.38)`. [WidgetStateProperty] valued
|
||||
/// properties that are not followed by a sublist have the same
|
||||
/// value for all states, otherwise the values are as specified for
|
||||
/// each state and "others" means all other states.
|
||||
///
|
||||
/// The "default font size" below refers to the font size specified in the
|
||||
/// [defaultStyleOf] method (or 14.0 if unspecified), scaled by the
|
||||
/// `MediaQuery.textScalerOf(context).scale` method. And the names of the
|
||||
/// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been abbreviated
|
||||
/// for readability.
|
||||
///
|
||||
/// The color of the [ButtonStyle.textStyle] is not used, the
|
||||
/// [ButtonStyle.foregroundColor] color is used instead.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// ## Material 2 defaults
|
||||
///
|
||||
/// * `textStyle` - Theme.textTheme.button
|
||||
/// * `backgroundColor` - transparent
|
||||
/// * `foregroundColor`
|
||||
/// * disabled - Theme.colorScheme.onSurface(0.38)
|
||||
/// * others - Theme.colorScheme.primary
|
||||
/// * `overlayColor`
|
||||
/// * hovered - Theme.colorScheme.primary(0.08)
|
||||
/// * focused or pressed - Theme.colorScheme.primary(0.12)
|
||||
/// * `shadowColor` - Theme.shadowColor
|
||||
/// * `elevation` - 0
|
||||
/// * `padding`
|
||||
/// * `default font size <= 14` - (horizontal(12), vertical(8))
|
||||
/// * `14 < default font size <= 28` - lerp(all(8), horizontal(8))
|
||||
/// * `28 < default font size <= 36` - lerp(horizontal(8), horizontal(4))
|
||||
/// * `36 < default font size` - horizontal(4)
|
||||
/// * `minimumSize` - Size(64, 36)
|
||||
/// * `fixedSize` - null
|
||||
/// * `maximumSize` - Size.infinite
|
||||
/// * `side` - null
|
||||
/// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
|
||||
/// * `mouseCursor` - WidgetStateMouseCursor.adaptiveClickable
|
||||
/// * `visualDensity` - theme.visualDensity
|
||||
/// * `tapTargetSize` - theme.materialTapTargetSize
|
||||
/// * `animationDuration` - kThemeChangeDuration
|
||||
/// * `enableFeedback` - true
|
||||
/// * `alignment` - Alignment.center
|
||||
/// * `splashFactory` - InkRipple.splashFactory
|
||||
///
|
||||
/// The default padding values for the [TextButton.icon] factory are slightly different:
|
||||
///
|
||||
/// * `padding`
|
||||
/// * `default font size <= 14` - all(8)
|
||||
/// * `14 < default font size <= 28 `- lerp(all(8), horizontal(4))
|
||||
/// * `28 < default font size` - horizontal(4)
|
||||
///
|
||||
/// The default value for `side`, which defines the appearance of the button's
|
||||
/// outline, is null. That means that the outline is defined by the button
|
||||
/// shape's [OutlinedBorder.side]. Typically the default value of an
|
||||
/// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
|
||||
///
|
||||
/// ## Material 3 defaults
|
||||
///
|
||||
/// If [ThemeData.useMaterial3] is set to true the following defaults will
|
||||
/// be used:
|
||||
///
|
||||
/// {@template flutter.material.text_button.material3_defaults}
|
||||
/// * `textStyle` - Theme.textTheme.labelLarge
|
||||
/// * `backgroundColor` - transparent
|
||||
/// * `foregroundColor`
|
||||
/// * disabled - Theme.colorScheme.onSurface(0.38)
|
||||
/// * others - Theme.colorScheme.primary
|
||||
/// * `overlayColor`
|
||||
/// * hovered - Theme.colorScheme.primary(0.08)
|
||||
/// * focused or pressed - Theme.colorScheme.primary(0.1)
|
||||
/// * others - null
|
||||
/// * `shadowColor` - Colors.transparent,
|
||||
/// * `surfaceTintColor` - null
|
||||
/// * `elevation` - 0
|
||||
/// * `padding`
|
||||
/// * `default font size <= 14` - lerp(horizontal(12), horizontal(4))
|
||||
/// * `14 < default font size <= 28` - lerp(all(8), horizontal(8))
|
||||
/// * `28 < default font size <= 36` - lerp(horizontal(8), horizontal(4))
|
||||
/// * `36 < default font size` - horizontal(4)
|
||||
/// * `minimumSize` - Size(64, 40)
|
||||
/// * `fixedSize` - null
|
||||
/// * `maximumSize` - Size.infinite
|
||||
/// * `side` - null
|
||||
/// * `shape` - StadiumBorder()
|
||||
/// * `mouseCursor` - WidgetStateMouseCursor.adaptiveClickable
|
||||
/// * `visualDensity` - theme.visualDensity
|
||||
/// * `tapTargetSize` - theme.materialTapTargetSize
|
||||
/// * `animationDuration` - kThemeChangeDuration
|
||||
/// * `enableFeedback` - true
|
||||
/// * `alignment` - Alignment.center
|
||||
/// * `splashFactory` - Theme.splashFactory
|
||||
///
|
||||
/// For the [TextButton.icon] factory, the end (generally the right) value of
|
||||
/// `padding` is increased from 12 to 16.
|
||||
/// {@endtemplate}
|
||||
@override
|
||||
ButtonStyle defaultStyleOf(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
final ButtonStyle buttonStyle = theme.useMaterial3
|
||||
? _TextButtonDefaultsM3(context)
|
||||
: styleFrom(
|
||||
foregroundColor: colorScheme.primary,
|
||||
disabledForegroundColor: colorScheme.onSurface.withValues(
|
||||
alpha: 0.38,
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
disabledBackgroundColor: Colors.transparent,
|
||||
shadowColor: theme.shadowColor,
|
||||
elevation: 0,
|
||||
textStyle: theme.textTheme.labelLarge,
|
||||
padding: _scaledPadding(context),
|
||||
minimumSize: const Size(64, 36),
|
||||
maximumSize: Size.infinite,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
),
|
||||
enabledMouseCursor: kIsWeb
|
||||
? SystemMouseCursors.click
|
||||
: SystemMouseCursors.basic,
|
||||
disabledMouseCursor: SystemMouseCursors.basic,
|
||||
visualDensity: theme.visualDensity,
|
||||
tapTargetSize: theme.materialTapTargetSize,
|
||||
animationDuration: kThemeChangeDuration,
|
||||
enableFeedback: true,
|
||||
alignment: Alignment.center,
|
||||
splashFactory: InkRipple.splashFactory,
|
||||
);
|
||||
|
||||
// Only apply padding when TextButton has an Icon.
|
||||
if (_addPadding) {
|
||||
final double defaultFontSize =
|
||||
buttonStyle.textStyle?.resolve(const <WidgetState>{})?.fontSize ??
|
||||
14.0;
|
||||
final double effectiveTextScale =
|
||||
MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
|
||||
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
|
||||
theme.useMaterial3
|
||||
? const EdgeInsetsDirectional.fromSTEB(12, 8, 16, 8)
|
||||
: const EdgeInsets.all(8),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
effectiveTextScale,
|
||||
);
|
||||
return buttonStyle.copyWith(
|
||||
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
|
||||
);
|
||||
}
|
||||
|
||||
return buttonStyle;
|
||||
}
|
||||
|
||||
/// Returns the [TextButtonThemeData.style] of the closest
|
||||
/// [TextButtonTheme] ancestor.
|
||||
@override
|
||||
ButtonStyle? themeStyleOf(BuildContext context) {
|
||||
return TextButtonTheme.of(context).style;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final double defaultFontSize = theme.textTheme.labelLarge?.fontSize ?? 14.0;
|
||||
final double effectiveTextScale =
|
||||
MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0;
|
||||
return ButtonStyleButton.scaledPadding(
|
||||
theme.useMaterial3
|
||||
? const EdgeInsets.symmetric(horizontal: 12, vertical: 8)
|
||||
: const EdgeInsets.all(8),
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
effectiveTextScale,
|
||||
);
|
||||
}
|
||||
|
||||
class _TextButtonWithIconChild extends StatelessWidget {
|
||||
const _TextButtonWithIconChild({
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.buttonStyle,
|
||||
required this.iconAlignment,
|
||||
});
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
final ButtonStyle? buttonStyle;
|
||||
final IconAlignment? iconAlignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double defaultFontSize =
|
||||
buttonStyle?.textStyle?.resolve(const <WidgetState>{})?.fontSize ??
|
||||
14.0;
|
||||
final double scale =
|
||||
clampDouble(
|
||||
MediaQuery.textScalerOf(context).scale(defaultFontSize) / 14.0,
|
||||
1.0,
|
||||
2.0,
|
||||
) -
|
||||
1.0;
|
||||
final TextButtonThemeData textButtonTheme = TextButtonTheme.of(context);
|
||||
final IconAlignment effectiveIconAlignment =
|
||||
iconAlignment ??
|
||||
textButtonTheme.style?.iconAlignment ??
|
||||
buttonStyle?.iconAlignment ??
|
||||
IconAlignment.start;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: lerpDouble(8, 4, scale)!,
|
||||
children: effectiveIconAlignment == IconAlignment.start
|
||||
? <Widget>[icon, Flexible(child: label)]
|
||||
: <Widget>[Flexible(child: label), icon],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - TextButton
|
||||
|
||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||
// "END GENERATED" comments are generated from data in the Material
|
||||
// Design token database by the script:
|
||||
// dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
// dart format off
|
||||
class _TextButtonDefaultsM3 extends ButtonStyle {
|
||||
_TextButtonDefaultsM3(this.context)
|
||||
: super(
|
||||
animationDuration: kThemeChangeDuration,
|
||||
enableFeedback: true,
|
||||
alignment: Alignment.center,
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
WidgetStateProperty<TextStyle?> get textStyle =>
|
||||
WidgetStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color?>? get backgroundColor =>
|
||||
const WidgetStatePropertyAll<Color>(Colors.transparent);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color?>? get foregroundColor =>
|
||||
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return _colors.onSurface.withValues(alpha: 0.38);
|
||||
}
|
||||
return _colors.primary;
|
||||
});
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color?>? get overlayColor =>
|
||||
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return _colors.primary.withValues(alpha: 0.1);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return _colors.primary.withValues(alpha: 0.08);
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return _colors.primary.withValues(alpha: 0.1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color>? get shadowColor =>
|
||||
const WidgetStatePropertyAll<Color>(Colors.transparent);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color>? get surfaceTintColor =>
|
||||
const WidgetStatePropertyAll<Color>(Colors.transparent);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<double>? get elevation =>
|
||||
const WidgetStatePropertyAll<double>(0.0);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<EdgeInsetsGeometry>? get padding =>
|
||||
WidgetStatePropertyAll<EdgeInsetsGeometry>(_scaledPadding(context));
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Size>? get minimumSize =>
|
||||
const WidgetStatePropertyAll<Size>(Size(64.0, 40.0));
|
||||
|
||||
// No default fixedSize
|
||||
|
||||
@override
|
||||
WidgetStateProperty<double>? get iconSize =>
|
||||
const WidgetStatePropertyAll<double>(18.0);
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Color>? get iconColor {
|
||||
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return _colors.onSurface.withValues(alpha: 0.38);
|
||||
}
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return _colors.primary;
|
||||
}
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
return _colors.primary;
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return _colors.primary;
|
||||
}
|
||||
return _colors.primary;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
WidgetStateProperty<Size>? get maximumSize =>
|
||||
const WidgetStatePropertyAll<Size>(Size.infinite);
|
||||
|
||||
// No default side
|
||||
|
||||
@override
|
||||
WidgetStateProperty<OutlinedBorder>? get shape =>
|
||||
const WidgetStatePropertyAll<OutlinedBorder>(StadiumBorder());
|
||||
|
||||
@override
|
||||
WidgetStateProperty<MouseCursor?>? get mouseCursor => WidgetStateMouseCursor.adaptiveClickable;
|
||||
|
||||
@override
|
||||
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
|
||||
|
||||
@override
|
||||
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
|
||||
|
||||
@override
|
||||
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
|
||||
}
|
||||
// dart format on
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES - TextButton
|
||||
526
lib/common/widgets/flutter/layout_builder.dart
Normal file
526
lib/common/widgets/flutter/layout_builder.dart
Normal file
@@ -0,0 +1,526 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// An abstract superclass for widgets that defer their building until layout.
|
||||
///
|
||||
/// Similar to the [Builder] widget except that the implementation calls the [builder]
|
||||
/// function at layout time and provides the [LayoutInfoType] that is required to
|
||||
/// configure the child widget subtree.
|
||||
///
|
||||
/// This is useful when the child widget tree relies on information that are only
|
||||
/// available during layout, and doesn't depend on the child's intrinsic size.
|
||||
///
|
||||
/// The [LayoutInfoType] should typically be immutable. The equality of the
|
||||
/// [LayoutInfoType] type is used by the implementation to avoid unnecessary
|
||||
/// rebuilds: if the new [LayoutInfoType] computed during layout is the same as
|
||||
/// (defined by `LayoutInfoType.==`) the previous [LayoutInfoType], the
|
||||
/// implementation will try to avoid calling the [builder] again unless
|
||||
/// [updateShouldRebuild] returns true. The corresponding [RenderObject] produced
|
||||
/// by this widget retains the most up-to-date [LayoutInfoType] for this purpose,
|
||||
/// which may keep a [LayoutInfoType] object in memory until the widget is removed
|
||||
/// from the tree.
|
||||
///
|
||||
/// Subclasses must return a [RenderObject] that mixes in [RenderAbstractLayoutBuilderMixin].
|
||||
abstract class AbstractLayoutBuilder<LayoutInfoType>
|
||||
extends RenderObjectWidget {
|
||||
/// Creates a widget that defers its building until layout.
|
||||
const AbstractLayoutBuilder({super.key});
|
||||
|
||||
/// Called at layout time to construct the widget tree.
|
||||
///
|
||||
/// The builder must not return null.
|
||||
Widget Function(BuildContext context, LayoutInfoType layoutInfo) get builder;
|
||||
|
||||
@override
|
||||
RenderObjectElement createElement() =>
|
||||
_LayoutBuilderElement<LayoutInfoType>(this);
|
||||
|
||||
/// Whether [builder] needs to be called again even if the layout constraints
|
||||
/// are the same.
|
||||
///
|
||||
/// When this widget's configuration is updated, the [builder] callback most
|
||||
/// likely needs to be called to build this widget's child. However,
|
||||
/// subclasses may provide ways in which the widget can be updated without
|
||||
/// needing to rebuild the child. Such subclasses can use this method to tell
|
||||
/// the framework when the child widget should be rebuilt.
|
||||
///
|
||||
/// When this method is called by the framework, the newly configured widget
|
||||
/// is asked if it requires a rebuild, and it is passed the old widget as a
|
||||
/// parameter.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [State.setState] and [State.didUpdateWidget], which talk about widget
|
||||
/// configuration changes and how they're triggered.
|
||||
/// * [Element.update], the method that actually updates the widget's
|
||||
/// configuration.
|
||||
@protected
|
||||
bool updateShouldRebuild(
|
||||
covariant AbstractLayoutBuilder<LayoutInfoType> oldWidget,
|
||||
) => true;
|
||||
|
||||
@override
|
||||
RenderAbstractLayoutBuilderMixin<LayoutInfoType, RenderObject>
|
||||
createRenderObject(
|
||||
BuildContext context,
|
||||
);
|
||||
|
||||
// updateRenderObject is redundant with the logic in the LayoutBuilderElement below.
|
||||
}
|
||||
|
||||
/// A specialized [AbstractLayoutBuilder] whose widget subtree depends on the
|
||||
/// incoming [ConstraintType] that will be imposed on the widget.
|
||||
///
|
||||
/// {@template flutter.widgets.ConstrainedLayoutBuilder}
|
||||
/// The [builder] function is called in the following situations:
|
||||
///
|
||||
/// * The first time the widget is laid out.
|
||||
/// * When the parent widget passes different layout constraints.
|
||||
/// * When the parent widget updates this widget and [updateShouldRebuild] returns `true`.
|
||||
/// * When the dependencies that the [builder] function subscribes to change.
|
||||
///
|
||||
/// The [builder] function is _not_ called during layout if the parent passes
|
||||
/// the same constraints repeatedly.
|
||||
///
|
||||
/// In the event that an ancestor skips the layout of this subtree so the
|
||||
/// constraints become outdated, the `builder` rebuilds with the last known
|
||||
/// constraints.
|
||||
/// {@endtemplate}
|
||||
abstract class ConstrainedLayoutBuilder<ConstraintType extends Constraints>
|
||||
extends AbstractLayoutBuilder<ConstraintType> {
|
||||
/// Creates a widget that defers its building until layout.
|
||||
const ConstrainedLayoutBuilder({super.key, required this.builder});
|
||||
|
||||
@override
|
||||
final Widget Function(BuildContext context, ConstraintType constraints)
|
||||
builder;
|
||||
}
|
||||
|
||||
class _LayoutBuilderElement<LayoutInfoType> extends RenderObjectElement {
|
||||
_LayoutBuilderElement(AbstractLayoutBuilder<LayoutInfoType> super.widget);
|
||||
|
||||
@override
|
||||
RenderAbstractLayoutBuilderMixin<LayoutInfoType, RenderObject>
|
||||
get renderObject =>
|
||||
super.renderObject
|
||||
as RenderAbstractLayoutBuilderMixin<LayoutInfoType, RenderObject>;
|
||||
|
||||
Element? _child;
|
||||
|
||||
// @override
|
||||
// BuildScope get buildScope => _buildScope;
|
||||
|
||||
// late final BuildScope _buildScope = BuildScope(
|
||||
// scheduleRebuild: _scheduleRebuild,
|
||||
// );
|
||||
|
||||
// To schedule a rebuild, markNeedsLayout needs to be called on this Element's
|
||||
// render object (as the rebuilding is done in its performLayout call). However,
|
||||
// the render tree should typically be kept clean during the postFrameCallbacks
|
||||
// and the idle phase, so the layout data can be safely read.
|
||||
// bool _deferredCallbackScheduled = false;
|
||||
// void _scheduleRebuild() {
|
||||
// if (_deferredCallbackScheduled) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// final bool deferMarkNeedsLayout =
|
||||
// switch (SchedulerBinding.instance.schedulerPhase) {
|
||||
// SchedulerPhase.idle || SchedulerPhase.postFrameCallbacks => true,
|
||||
// SchedulerPhase.transientCallbacks ||
|
||||
// SchedulerPhase.midFrameMicrotasks ||
|
||||
// SchedulerPhase.persistentCallbacks => false,
|
||||
// };
|
||||
// if (!deferMarkNeedsLayout) {
|
||||
// renderObject.scheduleLayoutCallback();
|
||||
// return;
|
||||
// }
|
||||
// _deferredCallbackScheduled = true;
|
||||
// SchedulerBinding.instance.scheduleFrameCallback(_frameCallback);
|
||||
// }
|
||||
|
||||
// void _frameCallback(Duration timestamp) {
|
||||
// _deferredCallbackScheduled = false;
|
||||
// // This method is only called when the render tree is stable, if the Element
|
||||
// // is deactivated it will never be reincorporated back to the tree.
|
||||
// if (mounted) {
|
||||
// renderObject.scheduleLayoutCallback();
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
void visitChildren(ElementVisitor visitor) {
|
||||
if (_child != null) {
|
||||
visitor(_child!);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void forgetChild(Element child) {
|
||||
assert(child == _child);
|
||||
_child = null;
|
||||
super.forgetChild(child);
|
||||
}
|
||||
|
||||
@override
|
||||
void mount(Element? parent, Object? newSlot) {
|
||||
super.mount(parent, newSlot); // Creates the renderObject.
|
||||
renderObject._updateCallback(_rebuildWithConstraints);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(AbstractLayoutBuilder<LayoutInfoType> newWidget) {
|
||||
assert(widget != newWidget);
|
||||
final oldWidget = widget as AbstractLayoutBuilder<LayoutInfoType>;
|
||||
super.update(newWidget);
|
||||
assert(widget == newWidget);
|
||||
|
||||
renderObject._updateCallback(_rebuildWithConstraints);
|
||||
if (newWidget.updateShouldRebuild(oldWidget)) {
|
||||
_needsBuild = true;
|
||||
renderObject.scheduleLayoutCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void markNeedsBuild() {
|
||||
// Calling super.markNeedsBuild is not needed. This Element does not need
|
||||
// to performRebuild since this call already does what performRebuild does,
|
||||
// So the element is clean as soon as this method returns and does not have
|
||||
// to be added to the dirty list or marked as dirty.
|
||||
renderObject.scheduleLayoutCallback();
|
||||
_needsBuild = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void performRebuild() {
|
||||
// This gets called if markNeedsBuild() is called on us.
|
||||
// That might happen if, e.g., our builder uses Inherited widgets.
|
||||
|
||||
// Force the callback to be called, even if the layout constraints are the
|
||||
// same. This is because that callback may depend on the updated widget
|
||||
// configuration, or an inherited widget.
|
||||
renderObject.scheduleLayoutCallback();
|
||||
_needsBuild = true;
|
||||
super
|
||||
.performRebuild(); // Calls widget.updateRenderObject (a no-op in this case).
|
||||
}
|
||||
|
||||
@override
|
||||
void unmount() {
|
||||
renderObject._callback = null;
|
||||
super.unmount();
|
||||
}
|
||||
|
||||
// The LayoutInfoType that was used to invoke the layout callback with last time,
|
||||
// during layout. The `_previousLayoutInfo` value is compared to the new one
|
||||
// to determine whether [LayoutBuilderBase.builder] needs to be called.
|
||||
LayoutInfoType? _previousLayoutInfo;
|
||||
bool _needsBuild = true;
|
||||
|
||||
void _rebuildWithConstraints(Constraints _) {
|
||||
final LayoutInfoType layoutInfo = renderObject.layoutInfo;
|
||||
@pragma('vm:notify-debugger-on-exception')
|
||||
void updateChildCallback() {
|
||||
Widget built;
|
||||
try {
|
||||
assert(layoutInfo == renderObject.layoutInfo);
|
||||
built = (widget as AbstractLayoutBuilder<LayoutInfoType>).builder(
|
||||
this,
|
||||
layoutInfo,
|
||||
);
|
||||
debugWidgetBuilderValue(widget, built);
|
||||
} catch (e, stack) {
|
||||
built = ErrorWidget.builder(
|
||||
_reportException(
|
||||
ErrorDescription('building $widget'),
|
||||
e,
|
||||
stack,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
try {
|
||||
_child = updateChild(_child, built, null);
|
||||
assert(_child != null);
|
||||
} catch (e, stack) {
|
||||
built = ErrorWidget.builder(
|
||||
_reportException(
|
||||
ErrorDescription('building $widget'),
|
||||
e,
|
||||
stack,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
if (kDebugMode) DiagnosticsDebugCreator(DebugCreator(this)),
|
||||
],
|
||||
),
|
||||
);
|
||||
_child = updateChild(null, built, slot);
|
||||
} finally {
|
||||
_needsBuild = false;
|
||||
_previousLayoutInfo = layoutInfo;
|
||||
}
|
||||
}
|
||||
|
||||
final VoidCallback? callback =
|
||||
_needsBuild || (layoutInfo != _previousLayoutInfo)
|
||||
? updateChildCallback
|
||||
: null;
|
||||
owner!.buildScope(this, callback);
|
||||
}
|
||||
|
||||
@override
|
||||
void insertRenderObjectChild(RenderObject child, Object? slot) {
|
||||
final RenderObjectWithChildMixin<RenderObject> renderObject =
|
||||
this.renderObject;
|
||||
assert(slot == null);
|
||||
assert(renderObject.debugValidateChild(child));
|
||||
renderObject.child = child;
|
||||
assert(renderObject == this.renderObject);
|
||||
}
|
||||
|
||||
@override
|
||||
void moveRenderObjectChild(
|
||||
RenderObject child,
|
||||
Object? oldSlot,
|
||||
Object? newSlot,
|
||||
) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeRenderObjectChild(RenderObject child, Object? slot) {
|
||||
final RenderAbstractLayoutBuilderMixin<LayoutInfoType, RenderObject>
|
||||
renderObject = this.renderObject;
|
||||
assert(renderObject.child == child);
|
||||
renderObject.child = null;
|
||||
assert(renderObject == this.renderObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic mixin for [RenderObject]s created by an [AbstractLayoutBuilder] with
|
||||
/// the the same `LayoutInfoType`.
|
||||
///
|
||||
/// Provides a [layoutCallback] implementation which, if needed, invokes
|
||||
/// [AbstractLayoutBuilder]'s builder callback.
|
||||
///
|
||||
/// Implementers can override the [layoutInfo] implementation with a value
|
||||
/// that is safe to access in [layoutCallback], which is called in
|
||||
/// [performLayout]. The default [layoutInfo] returns the incoming
|
||||
/// [Constraints].
|
||||
///
|
||||
/// This mixin replaces [RenderConstrainedLayoutBuilder].
|
||||
mixin RenderAbstractLayoutBuilderMixin<
|
||||
LayoutInfoType,
|
||||
ChildType extends RenderObject
|
||||
>
|
||||
on
|
||||
RenderObjectWithChildMixin<ChildType>,
|
||||
RenderObjectWithLayoutCallbackMixin {
|
||||
LayoutCallback<Constraints>? _callback;
|
||||
|
||||
/// Change the layout callback.
|
||||
void _updateCallback(LayoutCallback<Constraints> value) {
|
||||
if (value == _callback) {
|
||||
return;
|
||||
}
|
||||
_callback = value;
|
||||
scheduleLayoutCallback();
|
||||
}
|
||||
|
||||
/// Invokes the builder callback supplied via [AbstractLayoutBuilder] and
|
||||
/// rebuilds the [AbstractLayoutBuilder]'s widget tree, if needed.
|
||||
///
|
||||
/// No further work will be done if [layoutInfo] has not changed since the last
|
||||
/// time this method was called, and [AbstractLayoutBuilder.updateShouldRebuild]
|
||||
/// returned `false` when the widget was rebuilt.
|
||||
///
|
||||
/// This method should typically be called as soon as possible in the class's
|
||||
/// [performLayout] implementation, before any layout work is done.
|
||||
@visibleForOverriding
|
||||
@override
|
||||
void layoutCallback() => _callback!(constraints);
|
||||
|
||||
/// The information to invoke the [AbstractLayoutBuilder.builder] callback with.
|
||||
///
|
||||
/// This is typically the information that are only made available in
|
||||
/// [performLayout], which is inaccessible for regular [Builder] widget,
|
||||
/// such as the incoming [Constraints], which are the default value.
|
||||
@protected
|
||||
LayoutInfoType get layoutInfo => constraints as LayoutInfoType;
|
||||
}
|
||||
|
||||
/// Generic mixin for [RenderObject]s created by an [AbstractLayoutBuilder] with
|
||||
/// the the same `LayoutInfoType`.
|
||||
///
|
||||
/// Use [RenderAbstractLayoutBuilderMixin] instead, which replaces this mixin.
|
||||
typedef RenderConstrainedLayoutBuilder<
|
||||
LayoutInfoType,
|
||||
ChildType extends RenderObject
|
||||
> = RenderAbstractLayoutBuilderMixin<LayoutInfoType, ChildType>;
|
||||
|
||||
/// Builds a widget tree that can depend on the parent widget's size.
|
||||
///
|
||||
/// Similar to the [Builder] widget except that the framework calls the [builder]
|
||||
/// function at layout time and provides the parent widget's constraints. This
|
||||
/// is useful when the parent constrains the child's size and doesn't depend on
|
||||
/// the child's intrinsic size. The [LayoutBuilder]'s final size will match its
|
||||
/// child's size.
|
||||
///
|
||||
/// {@macro flutter.widgets.ConstrainedLayoutBuilder}
|
||||
///
|
||||
/// {@youtube 560 315 https://www.youtube.com/watch?v=IYDVcriKjsw}
|
||||
///
|
||||
/// If the child should be smaller than the parent, consider wrapping the child
|
||||
/// in an [Align] widget. If the child might want to be bigger, consider
|
||||
/// wrapping it in a [SingleChildScrollView] or [OverflowBox].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This example uses a [LayoutBuilder] to build a different widget depending on the available width. Resize the
|
||||
/// DartPad window to see [LayoutBuilder] in action!
|
||||
///
|
||||
/// ** See code in examples/api/lib/widgets/layout_builder/layout_builder.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverLayoutBuilder], the sliver counterpart of this widget.
|
||||
/// * [Builder], which calls a `builder` function at build time.
|
||||
/// * [StatefulBuilder], which passes its `builder` function a `setState` callback.
|
||||
/// * [CustomSingleChildLayout], which positions its child during layout.
|
||||
/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
|
||||
class LayoutBuilder extends ConstrainedLayoutBuilder<BoxConstraints> {
|
||||
/// Creates a widget that defers its building until layout.
|
||||
const LayoutBuilder({super.key, required super.builder});
|
||||
|
||||
@override
|
||||
RenderAbstractLayoutBuilderMixin<BoxConstraints, RenderBox>
|
||||
createRenderObject(
|
||||
BuildContext context,
|
||||
) => _RenderLayoutBuilder();
|
||||
}
|
||||
|
||||
class _RenderLayoutBuilder extends RenderBox
|
||||
with
|
||||
RenderObjectWithChildMixin<RenderBox>,
|
||||
RenderObjectWithLayoutCallbackMixin,
|
||||
RenderAbstractLayoutBuilderMixin<BoxConstraints, RenderBox> {
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
assert(_debugThrowIfNotCheckingIntrinsics());
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicWidth(double height) {
|
||||
assert(_debugThrowIfNotCheckingIntrinsics());
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
assert(_debugThrowIfNotCheckingIntrinsics());
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicHeight(double width) {
|
||||
assert(_debugThrowIfNotCheckingIntrinsics());
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@override
|
||||
Size computeDryLayout(BoxConstraints constraints) {
|
||||
assert(
|
||||
debugCannotComputeDryLayout(
|
||||
reason:
|
||||
'Calculating the dry layout would require running the layout callback '
|
||||
'speculatively, which might mutate the live render object tree.',
|
||||
),
|
||||
);
|
||||
return Size.zero;
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDryBaseline(
|
||||
BoxConstraints constraints,
|
||||
TextBaseline baseline,
|
||||
) {
|
||||
assert(
|
||||
debugCannotComputeDryLayout(
|
||||
reason:
|
||||
'Calculating the dry baseline would require running the layout callback '
|
||||
'speculatively, which might mutate the live render object tree.',
|
||||
),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
final BoxConstraints constraints = this.constraints;
|
||||
runLayoutCallback();
|
||||
if (child != null) {
|
||||
child!.layout(constraints, parentUsesSize: true);
|
||||
size = constraints.constrain(child!.size);
|
||||
} else {
|
||||
size = constraints.biggest;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDistanceToActualBaseline(TextBaseline baseline) {
|
||||
return child?.getDistanceToActualBaseline(baseline) ??
|
||||
super.computeDistanceToActualBaseline(baseline);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||
return child?.hitTest(result, position: position) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (child != null) {
|
||||
context.paintChild(child!, offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool _debugThrowIfNotCheckingIntrinsics() {
|
||||
assert(() {
|
||||
if (!RenderObject.debugCheckingIntrinsics) {
|
||||
throw FlutterError(
|
||||
'LayoutBuilder does not support returning intrinsic dimensions.\n'
|
||||
'Calculating the intrinsic dimensions would require running the layout '
|
||||
'callback speculatively, which might mutate the live render object tree.',
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FlutterErrorDetails _reportException(
|
||||
DiagnosticsNode context,
|
||||
Object exception,
|
||||
StackTrace stack, {
|
||||
InformationCollector? informationCollector,
|
||||
}) {
|
||||
final details = FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'widgets library',
|
||||
context: context,
|
||||
informationCollector: informationCollector,
|
||||
);
|
||||
FlutterError.reportError(details);
|
||||
return details;
|
||||
}
|
||||
83
lib/common/widgets/flutter/sliver_layout_builder.dart
Normal file
83
lib/common/widgets/flutter/sliver_layout_builder.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart'
|
||||
hide
|
||||
ConstrainedLayoutBuilder,
|
||||
LayoutBuilder,
|
||||
RenderConstrainedLayoutBuilder;
|
||||
|
||||
/// Builds a sliver widget tree that can depend on its own [SliverConstraints].
|
||||
///
|
||||
/// Similar to the [LayoutBuilder] widget except its builder should return a sliver
|
||||
/// widget, and [SliverLayoutBuilder] is itself a sliver. The framework calls the
|
||||
/// [builder] function at layout time and provides the current [SliverConstraints].
|
||||
/// The [SliverLayoutBuilder]'s final [SliverGeometry] will match the [SliverGeometry]
|
||||
/// of its child.
|
||||
///
|
||||
/// {@macro flutter.widgets.ConstrainedLayoutBuilder}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [LayoutBuilder], the non-sliver version of this widget.
|
||||
class SliverLayoutBuilder extends ConstrainedLayoutBuilder<SliverConstraints> {
|
||||
/// Creates a sliver widget that defers its building until layout.
|
||||
const SliverLayoutBuilder({super.key, required super.builder});
|
||||
|
||||
@override
|
||||
RenderConstrainedLayoutBuilder<SliverConstraints, RenderSliver>
|
||||
createRenderObject(
|
||||
BuildContext context,
|
||||
) => _RenderSliverLayoutBuilder();
|
||||
}
|
||||
|
||||
class _RenderSliverLayoutBuilder extends RenderSliver
|
||||
with
|
||||
RenderObjectWithChildMixin<RenderSliver>,
|
||||
RenderObjectWithLayoutCallbackMixin,
|
||||
RenderConstrainedLayoutBuilder<SliverConstraints, RenderSliver> {
|
||||
@override
|
||||
double childMainAxisPosition(RenderObject child) {
|
||||
assert(child == this.child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
runLayoutCallback();
|
||||
child?.layout(constraints, parentUsesSize: true);
|
||||
geometry = child?.geometry ?? SliverGeometry.zero;
|
||||
}
|
||||
|
||||
@override
|
||||
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
||||
assert(child == this.child);
|
||||
// child's offset is always (0, 0), transform.translate(0, 0) does not mutate the transform.
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
// This renderObject does not introduce additional offset to child's position.
|
||||
if (child?.geometry?.visible ?? false) {
|
||||
context.paintChild(child!, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTestChildren(
|
||||
SliverHitTestResult result, {
|
||||
required double mainAxisPosition,
|
||||
required double crossAxisPosition,
|
||||
}) {
|
||||
return child != null &&
|
||||
child!.geometry!.hitTestExtent > 0 &&
|
||||
child!.hitTest(
|
||||
result,
|
||||
mainAxisPosition: mainAxisPosition,
|
||||
crossAxisPosition: crossAxisPosition,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ import 'package:flutter/rendering.dart'
|
||||
ContainerParentDataMixin,
|
||||
InformationCollector,
|
||||
DiagnosticsDebugCreator;
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
||||
/// ref [LayoutBuilder]
|
||||
|
||||
@@ -274,42 +273,42 @@ class ImageGridRenderObjectElement extends RenderObjectElement {
|
||||
// repeatedly to remove children.
|
||||
final Set<Element> _forgottenChildren = HashSet<Element>();
|
||||
|
||||
@override
|
||||
BuildScope get buildScope => _buildScope;
|
||||
// @override
|
||||
// BuildScope get buildScope => _buildScope;
|
||||
|
||||
late final BuildScope _buildScope = BuildScope(
|
||||
scheduleRebuild: _scheduleRebuild,
|
||||
);
|
||||
// late final BuildScope _buildScope = BuildScope(
|
||||
// scheduleRebuild: _scheduleRebuild,
|
||||
// );
|
||||
|
||||
bool _deferredCallbackScheduled = false;
|
||||
void _scheduleRebuild() {
|
||||
if (_deferredCallbackScheduled) {
|
||||
return;
|
||||
}
|
||||
// bool _deferredCallbackScheduled = false;
|
||||
// void _scheduleRebuild() {
|
||||
// if (_deferredCallbackScheduled) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
final bool deferMarkNeedsLayout =
|
||||
switch (SchedulerBinding.instance.schedulerPhase) {
|
||||
SchedulerPhase.idle || SchedulerPhase.postFrameCallbacks => true,
|
||||
SchedulerPhase.transientCallbacks ||
|
||||
SchedulerPhase.midFrameMicrotasks ||
|
||||
SchedulerPhase.persistentCallbacks => false,
|
||||
};
|
||||
if (!deferMarkNeedsLayout) {
|
||||
renderObject.scheduleLayoutCallback();
|
||||
return;
|
||||
}
|
||||
_deferredCallbackScheduled = true;
|
||||
SchedulerBinding.instance.scheduleFrameCallback(_frameCallback);
|
||||
}
|
||||
// final bool deferMarkNeedsLayout =
|
||||
// switch (SchedulerBinding.instance.schedulerPhase) {
|
||||
// SchedulerPhase.idle || SchedulerPhase.postFrameCallbacks => true,
|
||||
// SchedulerPhase.transientCallbacks ||
|
||||
// SchedulerPhase.midFrameMicrotasks ||
|
||||
// SchedulerPhase.persistentCallbacks => false,
|
||||
// };
|
||||
// if (!deferMarkNeedsLayout) {
|
||||
// renderObject.scheduleLayoutCallback();
|
||||
// return;
|
||||
// }
|
||||
// _deferredCallbackScheduled = true;
|
||||
// SchedulerBinding.instance.scheduleFrameCallback(_frameCallback);
|
||||
// }
|
||||
|
||||
void _frameCallback(Duration timestamp) {
|
||||
_deferredCallbackScheduled = false;
|
||||
// This method is only called when the render tree is stable, if the Element
|
||||
// is deactivated it will never be reincorporated back to the tree.
|
||||
if (mounted) {
|
||||
renderObject.scheduleLayoutCallback();
|
||||
}
|
||||
}
|
||||
// void _frameCallback(Duration timestamp) {
|
||||
// _deferredCallbackScheduled = false;
|
||||
// // This method is only called when the render tree is stable, if the Element
|
||||
// // is deactivated it will never be reincorporated back to the tree.
|
||||
// if (mounted) {
|
||||
// renderObject.scheduleLayoutCallback();
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
void insertRenderObjectChild(RenderObject child, IndexedSlot<Element?> slot) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import 'dart:io' show File, Platform;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/colored_box_transition.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_viewer/image.dart';
|
||||
@@ -35,7 +36,7 @@ import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart' hide Image, PageView;
|
||||
import 'package:flutter/material.dart' hide Image, PageView, LayoutBuilder;
|
||||
import 'package:flutter/services.dart' show HapticFeedback;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
|
||||
@@ -15,7 +16,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
// 视频卡片 - 水平布局
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -15,7 +16,7 @@ import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ class _ArticlePageState extends CommonDynPageState<ArticlePage> {
|
||||
// if (kDebugMode) debugPrint('moduleBlocked');
|
||||
final moduleBlocked = controller.opusData!.modules.moduleBlocked!;
|
||||
content = SliverToBoxAdapter(
|
||||
child: moduleBlockedItem(context, theme, moduleBlocked, maxWidth),
|
||||
child: moduleBlockedItem(context, theme, moduleBlocked),
|
||||
);
|
||||
} else if (controller.articleData?.content != null) {
|
||||
if (controller.articleData?.type == 3) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/cached_network_svg_image.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
@@ -20,7 +21,7 @@ import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:get/get_navigation/src/extension_navigation.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
@@ -711,7 +712,6 @@ Widget moduleBlockedItem(
|
||||
BuildContext context,
|
||||
ThemeData theme,
|
||||
ModuleBlocked moduleBlocked,
|
||||
double maxWidth,
|
||||
) {
|
||||
late final isDarkMode = theme.brightness.isDark;
|
||||
|
||||
@@ -784,35 +784,41 @@ Widget moduleBlockedItem(
|
||||
}
|
||||
|
||||
if (moduleBlocked.blockedType == 1) {
|
||||
maxWidth = maxWidth <= 255 ? maxWidth : math.min(400, maxWidth * 0.8);
|
||||
return UnconstrainedBox(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
width: maxWidth,
|
||||
height: maxWidth,
|
||||
decoration: bgImg(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (moduleBlocked.icon != null) icon(math.max(40, maxWidth / 7)),
|
||||
if (moduleBlocked.hintMessage?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
moduleBlocked.hintMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
],
|
||||
if (moduleBlocked.button != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
btn(
|
||||
context,
|
||||
visualDensity: const VisualDensity(vertical: -2.5),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
return Align(
|
||||
alignment: .centerLeft,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
var maxWidth = constraints.maxWidth;
|
||||
maxWidth = maxWidth <= 255 ? maxWidth : math.min(400, maxWidth * 0.8);
|
||||
return Container(
|
||||
width: maxWidth,
|
||||
height: maxWidth,
|
||||
decoration: bgImg(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (moduleBlocked.icon != null)
|
||||
icon(math.max(40, maxWidth / 7)),
|
||||
if (moduleBlocked.hintMessage?.isNotEmpty == true) ...[
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
moduleBlocked.hintMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
],
|
||||
if (moduleBlocked.button != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
btn(
|
||||
context,
|
||||
visualDensity: const VisualDensity(vertical: -2.5),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/article/article_list/article.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ArticleListItem extends StatelessWidget {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'dart:math' show max;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:flutter/gestures.dart' show HorizontalDragGestureRecognizer;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
abstract class CommonSlidePage extends StatefulWidget {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -20,7 +21,7 @@ import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/path_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -20,7 +21,7 @@ import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart'
|
||||
hide SliverGridDelegateWithMaxCrossAxisExtent;
|
||||
hide SliverGridDelegateWithMaxCrossAxisExtent, LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/text_button.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_repost/view.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:flutter/material.dart' hide TextButton;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class ActionPanel extends StatelessWidget {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
@@ -9,7 +8,7 @@ import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
Widget addWidget(
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
@@ -24,7 +23,7 @@ import 'package:PiliPlus/utils/request_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
||||
@@ -7,10 +7,9 @@ Widget blockedItem(
|
||||
BuildContext context, {
|
||||
required ThemeData theme,
|
||||
required ModuleBlocked blocked,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 1),
|
||||
child: moduleBlockedItem(context, theme, blocked, maxWidth - 26),
|
||||
child: moduleBlockedItem(context, theme, blocked),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,16 +15,11 @@ Widget content(
|
||||
required DynamicItemModel item,
|
||||
required bool isSave,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
if (floor == 1) {
|
||||
maxWidth -= 24;
|
||||
}
|
||||
TextSpan? richNodes = richNode(
|
||||
context,
|
||||
theme: theme,
|
||||
item: item,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
final moduleDynamic = item.modules.moduleDynamic;
|
||||
final pics = moduleDynamic?.major?.opus?.pics;
|
||||
|
||||
@@ -12,7 +12,6 @@ List<Widget> dynContent(
|
||||
required DynamicItemModel item,
|
||||
required bool isSave,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
final moduleDynamic = item.modules.moduleDynamic;
|
||||
return [
|
||||
@@ -24,7 +23,6 @@ List<Widget> dynContent(
|
||||
isDetail: isDetail,
|
||||
item: item,
|
||||
floor: floor,
|
||||
maxWidth: maxWidth,
|
||||
),
|
||||
module(
|
||||
context,
|
||||
@@ -33,7 +31,6 @@ List<Widget> dynContent(
|
||||
isDetail: isDetail,
|
||||
item: item,
|
||||
floor: floor,
|
||||
maxWidth: maxWidth,
|
||||
),
|
||||
if (moduleDynamic?.additional case final additional?)
|
||||
addWidget(
|
||||
@@ -44,6 +41,6 @@ List<Widget> dynContent(
|
||||
floor: floor,
|
||||
),
|
||||
if (moduleDynamic?.major?.blocked case final blocked?)
|
||||
blockedItem(context, theme: theme, blocked: blocked, maxWidth: maxWidth),
|
||||
blockedItem(context, theme: theme, blocked: blocked),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:PiliPlus/common/widgets/avatars.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
@@ -10,11 +9,10 @@ import 'package:PiliPlus/pages/dynamics/widgets/interaction.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DynamicPanel extends StatelessWidget {
|
||||
final DynamicItemModel item;
|
||||
final double maxWidth;
|
||||
final bool isDetail;
|
||||
final ValueChanged<Object>? onRemove;
|
||||
final bool isSave;
|
||||
@@ -30,7 +28,6 @@ class DynamicPanel extends StatelessWidget {
|
||||
const DynamicPanel({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.maxWidth,
|
||||
this.isDetail = false,
|
||||
this.onRemove,
|
||||
this.isSave = false,
|
||||
@@ -99,7 +96,6 @@ class DynamicPanel extends StatelessWidget {
|
||||
isDetail: isDetail,
|
||||
item: item,
|
||||
floor: 1,
|
||||
maxWidth: maxWidth,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
if (!isDetail) ...[
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/dynamics/widgets/dyn_content.dart';
|
||||
@@ -6,7 +5,7 @@ import 'package:PiliPlus/pages/dynamics/widgets/module_panel.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
Widget forwardPanel(
|
||||
@@ -16,7 +15,6 @@ Widget forwardPanel(
|
||||
required DynamicItemModel orig,
|
||||
required bool isSave,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
final moduleDynamic = orig.modules.moduleDynamic;
|
||||
final major = moduleDynamic?.major;
|
||||
@@ -44,7 +42,6 @@ Widget forwardPanel(
|
||||
isDetail: isDetail,
|
||||
item: orig,
|
||||
floor: floor + 1,
|
||||
maxWidth: maxWidth - 30,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ Widget livePanel(
|
||||
required ThemeData theme,
|
||||
required DynamicItemModel item,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
Function(List<String>, int)? callback,
|
||||
}) {
|
||||
DynamicLive2Model? live = item.modules.moduleDynamic!.major!.live;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
Widget livePanelSub(
|
||||
BuildContext context, {
|
||||
@@ -12,7 +13,6 @@ Widget livePanelSub(
|
||||
required ThemeData theme,
|
||||
required DynamicItemModel item,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
LivePlayInfo? live = item
|
||||
.modules
|
||||
@@ -27,7 +27,6 @@ Widget livePanelSub(
|
||||
}
|
||||
EdgeInsets padding;
|
||||
if (floor == 1) {
|
||||
maxWidth -= 24;
|
||||
padding = const EdgeInsets.symmetric(horizontal: 12);
|
||||
} else {
|
||||
padding = EdgeInsets.zero;
|
||||
@@ -40,11 +39,13 @@ Widget livePanelSub(
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: maxWidth,
|
||||
height: maxWidth / StyleString.aspectRatio,
|
||||
src: live.cover,
|
||||
quality: 40,
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) => NetworkImgLayer(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth / StyleString.aspectRatio,
|
||||
src: live.cover,
|
||||
quality: 40,
|
||||
),
|
||||
),
|
||||
PBadge(
|
||||
text: live.watchedShow?.textLarge,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
Widget liveRcmdPanel(
|
||||
BuildContext context, {
|
||||
@@ -12,7 +13,6 @@ Widget liveRcmdPanel(
|
||||
required ThemeData theme,
|
||||
required DynamicItemModel item,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
Function(List<String>, int)? callback,
|
||||
}) {
|
||||
DynamicLiveModel? liveRcmd = item.modules.moduleDynamic?.major?.liveRcmd;
|
||||
@@ -21,7 +21,6 @@ Widget liveRcmdPanel(
|
||||
}
|
||||
EdgeInsets padding;
|
||||
if (floor == 1) {
|
||||
maxWidth -= 24;
|
||||
padding = const EdgeInsets.symmetric(horizontal: 12);
|
||||
} else {
|
||||
padding = EdgeInsets.zero;
|
||||
@@ -34,11 +33,13 @@ Widget liveRcmdPanel(
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: maxWidth,
|
||||
height: maxWidth / StyleString.aspectRatio,
|
||||
src: liveRcmd.cover,
|
||||
quality: 40,
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) => NetworkImgLayer(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth / StyleString.aspectRatio,
|
||||
src: liveRcmd.cover,
|
||||
quality: 40,
|
||||
),
|
||||
),
|
||||
PBadge(
|
||||
text: liveRcmd.watchedShow?.textLarge,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/grpc/bilibili/app/listener/v1.pbenum.dart'
|
||||
show PlaylistSource;
|
||||
@@ -15,7 +14,7 @@ import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
Widget noneWidget(ThemeData theme, String? tips) => Row(
|
||||
@@ -40,7 +39,6 @@ Widget module(
|
||||
required DynamicItemModel item,
|
||||
required bool isSave,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
final moduleDynamic = item.modules.moduleDynamic;
|
||||
final major = moduleDynamic?.major;
|
||||
@@ -77,7 +75,6 @@ Widget module(
|
||||
floor: floor,
|
||||
isSave: isSave,
|
||||
isDetail: isDetail,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
// 转发
|
||||
case 'DYNAMIC_TYPE_FORWARD':
|
||||
@@ -88,7 +85,6 @@ Widget module(
|
||||
orig: item.orig!,
|
||||
isDetail: isDetail,
|
||||
floor: floor + 1,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
// 直播
|
||||
case 'DYNAMIC_TYPE_LIVE_RCMD':
|
||||
@@ -98,7 +94,6 @@ Widget module(
|
||||
isDetail: isDetail,
|
||||
item: item,
|
||||
floor: floor,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
// 直播
|
||||
case 'DYNAMIC_TYPE_LIVE':
|
||||
@@ -108,7 +103,6 @@ Widget module(
|
||||
item: item,
|
||||
floor: floor,
|
||||
isDetail: isDetail,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
// 活动
|
||||
case 'DYNAMIC_TYPE_COMMON_SQUARE':
|
||||
@@ -315,7 +309,6 @@ Widget module(
|
||||
isDetail: isDetail,
|
||||
item: item,
|
||||
floor: floor,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
|
||||
default:
|
||||
|
||||
@@ -22,7 +22,6 @@ TextSpan? richNode(
|
||||
BuildContext context, {
|
||||
required ThemeData theme,
|
||||
required DynamicItemModel item,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
try {
|
||||
late final style = TextStyle(color: theme.colorScheme.primary);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
@@ -10,7 +9,7 @@ import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class UpPanel extends StatefulWidget {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// 视频or合集
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/utils/extension/num_ext.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
Widget videoSeasonWidget(
|
||||
BuildContext context, {
|
||||
@@ -15,7 +16,6 @@ Widget videoSeasonWidget(
|
||||
required DynamicItemModel item,
|
||||
required bool isSave,
|
||||
required bool isDetail,
|
||||
required double maxWidth,
|
||||
}) {
|
||||
// type archive ugcSeason
|
||||
// archive 视频/显示发布人
|
||||
@@ -36,7 +36,6 @@ Widget videoSeasonWidget(
|
||||
|
||||
EdgeInsets padding;
|
||||
if (floor == 1) {
|
||||
maxWidth -= 24;
|
||||
padding = const EdgeInsets.symmetric(horizontal: 12);
|
||||
} else {
|
||||
padding = EdgeInsets.zero;
|
||||
@@ -51,11 +50,13 @@ Widget videoSeasonWidget(
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: maxWidth,
|
||||
height: maxWidth / StyleString.aspectRatio,
|
||||
src: cover,
|
||||
quality: 40,
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) => NetworkImgLayer(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth / StyleString.aspectRatio,
|
||||
src: cover,
|
||||
quality: 40,
|
||||
),
|
||||
),
|
||||
if (video.badge?.text case final badge?)
|
||||
PBadge(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:math';
|
||||
import 'package:PiliPlus/common/widgets/avatars.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/dynamics.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
@@ -15,7 +16,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class VotePanel extends StatefulWidget {
|
||||
|
||||
@@ -251,7 +251,6 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
child: DynamicPanel(
|
||||
item: controller.dynItem,
|
||||
isDetail: true,
|
||||
maxWidth: maxWidth - this.padding.horizontal - 2 * padding,
|
||||
isDetailPortraitW: isPortrait,
|
||||
onSetPubSetting: controller.onSetPubSetting,
|
||||
onEdit: _onEdit,
|
||||
@@ -284,10 +283,6 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
||||
child: DynamicPanel(
|
||||
item: controller.dynItem,
|
||||
isDetail: true,
|
||||
maxWidth:
|
||||
(maxWidth - this.padding.horizontal) *
|
||||
(flex / (flex + flex1)) -
|
||||
padding,
|
||||
isDetailPortraitW: isPortrait,
|
||||
onSetPubSetting: controller.onSetPubSetting,
|
||||
onEdit: _onEdit,
|
||||
|
||||
@@ -104,7 +104,6 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
|
||||
onRemove: (idStr) =>
|
||||
controller.onRemove(index, idStr),
|
||||
onBlock: () => controller.onBlock(index),
|
||||
maxWidth: maxWidth,
|
||||
onUnfold: () => controller.onUnfold(item, index),
|
||||
);
|
||||
},
|
||||
@@ -122,7 +121,6 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
|
||||
onRemove: (idStr) =>
|
||||
controller.onRemove(index, idStr),
|
||||
onBlock: () => controller.onBlock(index),
|
||||
maxWidth: maxWidth,
|
||||
onUnfold: () => controller.onUnfold(item, index),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -350,10 +350,7 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
|
||||
|
||||
final item = response[index];
|
||||
if (item.dynamicCardItem != null) {
|
||||
return DynamicPanel(
|
||||
item: item.dynamicCardItem!,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
return DynamicPanel(item: item.dynamicCardItem!);
|
||||
}
|
||||
|
||||
return Text(item.topicType ?? 'err');
|
||||
@@ -368,10 +365,7 @@ class _DynTopicPageState extends State<DynTopicPage> with DynMixin {
|
||||
}
|
||||
final item = response[index];
|
||||
if (item.dynamicCardItem != null) {
|
||||
return DynamicPanel(
|
||||
item: item.dynamicCardItem!,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
return DynamicPanel(item: item.dynamicCardItem!);
|
||||
} else {
|
||||
return Text(item.topicType ?? 'err');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_article/item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FavArticleItem extends StatelessWidget {
|
||||
@@ -45,17 +46,13 @@ class FavArticleItem extends StatelessWidget {
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(
|
||||
BuildContext context,
|
||||
BoxConstraints boxConstraints,
|
||||
) {
|
||||
return NetworkImgLayer(
|
||||
src: item.cover!.url,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
);
|
||||
},
|
||||
builder: (context, boxConstraints) {
|
||||
return NetworkImgLayer(
|
||||
src: item.cover!.url,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_note/list.dart';
|
||||
import 'package:PiliPlus/pages/fav/note/controller.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class FavNoteItem extends StatelessWidget {
|
||||
const FavNoteItem({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
@@ -8,7 +9,7 @@ import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select/base.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class FavPgcItem extends StatelessWidget {
|
||||
const FavPgcItem({
|
||||
@@ -60,45 +61,41 @@ class FavPgcItem extends StatelessWidget {
|
||||
AspectRatio(
|
||||
aspectRatio: 3 / 4,
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(
|
||||
BuildContext context,
|
||||
BoxConstraints boxConstraints,
|
||||
) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item.cover,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
),
|
||||
builder: (context, boxConstraints) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item.cover,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
),
|
||||
),
|
||||
PBadge(
|
||||
right: 4,
|
||||
top: 4,
|
||||
text: item.badge,
|
||||
size: PBadgeSize.small,
|
||||
fontSize: 10,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 2,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: selectMask(
|
||||
theme,
|
||||
item.checked,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
),
|
||||
PBadge(
|
||||
right: 4,
|
||||
top: 4,
|
||||
text: item.badge,
|
||||
size: PBadgeSize.small,
|
||||
fontSize: 10,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 2,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: selectMask(
|
||||
theme,
|
||||
item.checked,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
import 'package:PiliPlus/utils/fav_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class FavVideoItem extends StatelessWidget {
|
||||
final String heroTag;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -16,7 +17,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
// 收藏视频卡片 - 水平布局
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -13,7 +14,7 @@ import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -13,7 +14,7 @@ import 'package:PiliPlus/pages/later/controller.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
// 视频卡片 - 水平布局
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class LiveCardVApp extends StatelessWidget {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
@@ -10,8 +11,8 @@ import 'package:PiliPlus/pages/search/widgets/search_text.dart';
|
||||
import 'package:PiliPlus/utils/extension/size_ext.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter/services.dart' show FilteringTextInputFormatter;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class LiveDmBlockPage extends StatefulWidget {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_follow/item.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class LiveCardVFollow extends StatelessWidget {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/live/live_search/room_item.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class LiveCardVSearch extends StatelessWidget {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -6,7 +7,7 @@ import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_article/item.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class MemberArticleItem extends StatelessWidget {
|
||||
const MemberArticleItem({super.key, required this.item});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -9,7 +10,7 @@ import 'package:PiliPlus/models_new/space/space_audio/item.dart';
|
||||
import 'package:PiliPlus/pages/audio/view.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class MemberAudioItem extends StatelessWidget {
|
||||
const MemberAudioItem({super.key, required this.item});
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_cheese/item.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class MemberCheeseItem extends StatelessWidget {
|
||||
const MemberCheeseItem({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -12,7 +13,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class MemberCoinLikeItem extends StatelessWidget {
|
||||
final CoinLikeArcItem item;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_archive/item.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberComicItem extends StatelessWidget {
|
||||
|
||||
@@ -93,7 +93,6 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
item: response[index],
|
||||
onRemove: _memberDynamicController.onRemove,
|
||||
onSetTop: _memberDynamicController.onSetTop,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
},
|
||||
childCount: response.length,
|
||||
@@ -108,7 +107,6 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
item: response[index],
|
||||
onRemove: _memberDynamicController.onRemove,
|
||||
onSetTop: _memberDynamicController.onSetTop,
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
},
|
||||
itemCount: response.length,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_fav/list.dart';
|
||||
@@ -8,7 +9,7 @@ import 'package:PiliPlus/utils/fav_utils.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavItem extends StatelessWidget {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/http/search.dart';
|
||||
@@ -10,7 +11,7 @@ import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class VideoCardVMemberHome extends StatelessWidget {
|
||||
|
||||
@@ -33,8 +33,6 @@ class _MemberOpusState extends State<MemberOpus>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late final MemberOpusController _controller;
|
||||
|
||||
late double _maxWidth;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -127,7 +125,6 @@ class _MemberOpusState extends State<MemberOpus>
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
afterCalc: (value) => _maxWidth = value,
|
||||
);
|
||||
|
||||
Widget _buildBody(LoadingState<List<SpaceOpusItemModel>?> loadingState) {
|
||||
@@ -148,10 +145,7 @@ class _MemberOpusState extends State<MemberOpus>
|
||||
if (index == response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return SpaceOpusItem(
|
||||
item: response[index],
|
||||
maxWidth: _maxWidth,
|
||||
);
|
||||
return SpaceOpusItem(item: response[index]);
|
||||
},
|
||||
childCount: response.length,
|
||||
),
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import 'package:PiliPlus/common/widgets/flutter/dyn/ink_well.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_opus/item.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:flutter/material.dart' hide InkWell;
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class SpaceOpusItem extends StatelessWidget {
|
||||
const SpaceOpusItem({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.maxWidth,
|
||||
});
|
||||
|
||||
final SpaceOpusItemModel item;
|
||||
final double maxWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -34,12 +32,14 @@ class SpaceOpusItem extends StatelessWidget {
|
||||
if (hasPic)
|
||||
Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
width: maxWidth,
|
||||
height: maxWidth * item.cover!.ratio,
|
||||
src: item.cover!.url,
|
||||
type: ImageType.emote,
|
||||
quality: 60,
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) => NetworkImgLayer(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth * item.cover!.ratio,
|
||||
src: item.cover!.url,
|
||||
type: ImageType.emote,
|
||||
quality: 60,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_archive/item.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class PgcCardVMemberPgc extends StatelessWidget {
|
||||
|
||||
@@ -95,10 +95,7 @@ class _MemberSearchChildPageState extends State<MemberSearchChildPage>
|
||||
if (index == response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return DynamicPanel(
|
||||
item: response[index],
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
return DynamicPanel(item: response[index]);
|
||||
},
|
||||
childCount: response.length,
|
||||
),
|
||||
@@ -108,10 +105,7 @@ class _MemberSearchChildPageState extends State<MemberSearchChildPage>
|
||||
if (index == response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return DynamicPanel(
|
||||
item: response[index],
|
||||
maxWidth: maxWidth,
|
||||
);
|
||||
return DynamicPanel(item: response[index]);
|
||||
},
|
||||
itemCount: response.length,
|
||||
),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_season_series/season.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class SeasonSeriesCard extends StatelessWidget {
|
||||
const SeasonSeriesCard({
|
||||
|
||||
@@ -65,13 +65,10 @@ class _MemberShopState extends State<MemberShop>
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late double _maxWidth;
|
||||
|
||||
late final gridDelegate = SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
mainAxisSpacing: StyleString.safeSpace,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
afterCalc: (value) => _maxWidth = value,
|
||||
);
|
||||
|
||||
Widget _buildBody(LoadingState<List<SpaceShopItem>?> loadingState) {
|
||||
@@ -92,10 +89,7 @@ class _MemberShopState extends State<MemberShop>
|
||||
gridDelegate: gridDelegate,
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(_, index) {
|
||||
return MemberShopItem(
|
||||
item: response[index],
|
||||
maxWidth: _maxWidth,
|
||||
);
|
||||
return MemberShopItem(item: response[index]);
|
||||
},
|
||||
childCount: response.length,
|
||||
),
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models_new/space/space_shop/item.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberShopItem extends StatelessWidget {
|
||||
const MemberShopItem({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.maxWidth,
|
||||
});
|
||||
|
||||
final SpaceShopItem item;
|
||||
final double maxWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -34,11 +33,13 @@ class MemberShopItem extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: .emote,
|
||||
src: item.cover?.url,
|
||||
width: maxWidth,
|
||||
height: maxWidth,
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) => NetworkImgLayer(
|
||||
type: .emote,
|
||||
src: item.cover?.url,
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxWidth,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/video_progress_indicator.dart';
|
||||
@@ -12,7 +13,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
|
||||
// 视频卡片 - 水平布局
|
||||
@@ -77,137 +78,118 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints boxConstraints) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(
|
||||
BuildContext context,
|
||||
BoxConstraints boxConstraints,
|
||||
) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight =
|
||||
boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: videoItem.cover,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
final double maxWidth = boxConstraints.maxWidth;
|
||||
final double maxHeight = boxConstraints.maxHeight;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: videoItem.cover,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
if (fromViewAid == videoItem.param)
|
||||
const Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: StyleString.mdRadius,
|
||||
color: Colors.black54,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'上次观看',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
letterSpacing: 5,
|
||||
),
|
||||
),
|
||||
if (fromViewAid == videoItem.param)
|
||||
const Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: StyleString.mdRadius,
|
||||
color: Colors.black54,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'上次观看',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
letterSpacing: 5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (videoItem.badges?.isNotEmpty == true)
|
||||
PBadge(
|
||||
text: videoItem.badges!
|
||||
.map((item) => item.text)
|
||||
.join('|'),
|
||||
right: 6.0,
|
||||
top: 6.0,
|
||||
type: videoItem.badges!.first.text == '充电专属'
|
||||
? PBadgeType.error
|
||||
: PBadgeType.primary,
|
||||
),
|
||||
if (videoItem.history != null) ...[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
try {
|
||||
return Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: VideoProgressIndicator(
|
||||
color: theme.colorScheme.primary,
|
||||
backgroundColor: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
progress:
|
||||
videoItem.history!.progress! /
|
||||
videoItem.history!.duration!,
|
||||
),
|
||||
if (videoItem.badges?.isNotEmpty == true)
|
||||
PBadge(
|
||||
text: videoItem.badges!
|
||||
.map((item) => item.text)
|
||||
.join('|'),
|
||||
right: 6.0,
|
||||
top: 6.0,
|
||||
type:
|
||||
videoItem.badges!.first.text ==
|
||||
'充电专属'
|
||||
? PBadgeType.error
|
||||
: PBadgeType.primary,
|
||||
);
|
||||
} catch (_) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
try {
|
||||
return PBadge(
|
||||
text:
|
||||
videoItem.history!.progress ==
|
||||
videoItem.history!.duration
|
||||
? '已看完'
|
||||
: '${DurationUtils.formatDuration(videoItem.history!.progress)}/${DurationUtils.formatDuration(videoItem.history!.duration)}',
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
);
|
||||
} catch (_) {
|
||||
return PBadge(
|
||||
text: DurationUtils.formatDuration(
|
||||
videoItem.duration,
|
||||
),
|
||||
if (videoItem.history != null) ...[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
try {
|
||||
return Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: VideoProgressIndicator(
|
||||
color:
|
||||
theme.colorScheme.primary,
|
||||
backgroundColor: theme
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
progress:
|
||||
videoItem
|
||||
.history!
|
||||
.progress! /
|
||||
videoItem
|
||||
.history!
|
||||
.duration!,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
try {
|
||||
return PBadge(
|
||||
text:
|
||||
videoItem.history!.progress ==
|
||||
videoItem
|
||||
.history!
|
||||
.duration
|
||||
? '已看完'
|
||||
: '${DurationUtils.formatDuration(videoItem.history!.progress)}/${DurationUtils.formatDuration(videoItem.history!.duration)}',
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
);
|
||||
} catch (_) {
|
||||
return PBadge(
|
||||
text:
|
||||
DurationUtils.formatDuration(
|
||||
videoItem.duration,
|
||||
),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
] else if (videoItem.duration > 0)
|
||||
PBadge(
|
||||
text: DurationUtils.formatDuration(
|
||||
videoItem.duration,
|
||||
),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
content(context, theme),
|
||||
],
|
||||
);
|
||||
},
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
] else if (videoItem.duration > 0)
|
||||
PBadge(
|
||||
text: DurationUtils.formatDuration(
|
||||
videoItem.duration,
|
||||
),
|
||||
right: 6.0,
|
||||
bottom: 6.0,
|
||||
type: PBadgeType.gray,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
content(context, theme),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/marquee.dart';
|
||||
@@ -11,7 +12,7 @@ import 'package:PiliPlus/models_new/music/bgm_recommend_list.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class MusicVideoCardH extends StatelessWidget {
|
||||
final BgmRecommend videoItem;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class PgcCardV extends StatelessWidget {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_timeline/episode.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class PgcCardVTimeline extends StatelessWidget {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_index_result/list.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class PgcCardVPgcIndex extends StatelessWidget {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -6,7 +7,7 @@ import 'package:PiliPlus/models/common/stat_type.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_rank/pgc_rank_item_model.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class PgcRankItem extends StatelessWidget {
|
||||
const PgcRankItem({super.key, required this.item});
|
||||
|
||||
@@ -386,7 +386,6 @@ class _SavePanelState extends State<SavePanel> {
|
||||
item: dyn,
|
||||
isDetail: true,
|
||||
isSave: true,
|
||||
maxWidth: maxWidth - 24,
|
||||
),
|
||||
),
|
||||
if (cover?.isNotEmpty == true &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/disabled_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/search/search_rcmd/data.dart';
|
||||
@@ -13,7 +14,7 @@ import 'package:PiliPlus/utils/extension/size_ext.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart'hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SearchPage extends StatefulWidget {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/search/result.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class PgcCardVSearch extends StatelessWidget {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/search/result.dart';
|
||||
import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SearchArticleItem extends StatelessWidget {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/search/result.dart';
|
||||
import 'package:PiliPlus/utils/num_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
class LiveItem extends StatelessWidget {
|
||||
final SearchLiveItemModel liveItem;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models_new/sub/sub/list.dart';
|
||||
import 'package:PiliPlus/pages/subscription_detail/view.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
@@ -11,7 +12,7 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
|
||||
// 收藏视频卡片 - 水平布局
|
||||
class SubVideoCardH extends StatelessWidget {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
@@ -8,7 +9,7 @@ import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/utils/duration_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ReplySearchItem extends StatelessWidget {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:math' as math;
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_viewer/hero.dart';
|
||||
@@ -21,7 +22,7 @@ import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide LayoutBuilder;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/skeleton/dynamic_card.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/sliver_layout_builder.dart';
|
||||
import 'package:PiliPlus/utils/global_data.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide SliverLayoutBuilder;
|
||||
import 'package:flutter/rendering.dart' show SliverConstraints;
|
||||
import 'package:waterfall_flow/waterfall_flow.dart'
|
||||
show SliverWaterfallFlowDelegate;
|
||||
|
||||
mixin DynMixin {
|
||||
late double maxWidth;
|
||||
|
||||
late final dynGridDelegate =
|
||||
SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||
crossAxisSpacing: 4,
|
||||
afterCalc: (value) => maxWidth = value,
|
||||
);
|
||||
|
||||
Widget buildPage(Widget child) {
|
||||
@@ -26,7 +24,6 @@ mixin DynMixin {
|
||||
final maxWidth = constraints.crossAxisExtent;
|
||||
final cardWidth = Grid.smallCardWidth * 2;
|
||||
final flag = cardWidth < maxWidth;
|
||||
this.maxWidth = flag ? cardWidth : maxWidth;
|
||||
return SliverPadding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: flag ? (maxWidth - cardWidth) / 2 : 0,
|
||||
@@ -76,7 +73,6 @@ class SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
|
||||
super.collectGarbage,
|
||||
super.viewportBuilder,
|
||||
super.closeToTrailing,
|
||||
this.afterCalc,
|
||||
}) : assert(maxCrossAxisExtent >= 0);
|
||||
|
||||
/// The maximum extent of tiles in the cross axis.
|
||||
@@ -95,8 +91,6 @@ class SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
|
||||
int? crossAxisCount;
|
||||
double? crossAxisExtent;
|
||||
|
||||
final ValueChanged<double>? afterCalc;
|
||||
|
||||
@override
|
||||
int getCrossAxisCount(SliverConstraints constraints) {
|
||||
final crossAxisExtent = constraints.crossAxisExtent;
|
||||
@@ -106,10 +100,6 @@ class SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
|
||||
this.crossAxisExtent = crossAxisExtent;
|
||||
crossAxisCount = (crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing))
|
||||
.ceil();
|
||||
afterCalc?.call(
|
||||
(crossAxisExtent - ((crossAxisCount! - 1) * crossAxisSpacing)) /
|
||||
crossAxisCount!,
|
||||
);
|
||||
return crossAxisCount!;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user