更改卡片样式

main
落雨楓 2 years ago
parent 5ad50bbc58
commit 484810d390

@ -31,9 +31,7 @@ class IsekaiWikiApp extends StatelessWidget {
return Material( return Material(
child: GetCupertinoApp( child: GetCupertinoApp(
title: '异世界百科', title: '异世界百科',
theme: const CupertinoThemeData( theme: Styles.cupertinoLightTheme,
textTheme: Styles.defaultTextTheme,
scaffoldBackgroundColor: Styles.themePageBackgroundColor),
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[ localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
@ -51,7 +49,16 @@ class IsekaiWikiApp extends StatelessWidget {
} else { } else {
Styles.textScaleFactor = MediaQuery.of(context).textScaleFactor; Styles.textScaleFactor = MediaQuery.of(context).textScaleFactor;
Styles.isXs = MediaQuery.of(context).size.width <= 340; Styles.isXs = MediaQuery.of(context).size.width <= 340;
return child; return CupertinoTheme(
data: MediaQuery.of(context).platformBrightness != Brightness.dark
? Styles.cupertinoLightTheme
: Styles.cupertinoDarkTheme,
child: Theme(
data: MediaQuery.of(context).platformBrightness != Brightness.dark
? Styles.materialLightTheme
: Styles.materialDarkTheme,
child: child),
);
} }
}, },
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,

@ -52,8 +52,7 @@ class _HeroTag {
// Let the Hero tag be described in tree dumps. // Let the Hero tag be described in tree dumps.
@override @override
String toString() => String toString() => 'Default Hero tag for Cupertino navigation bars with navigator $navigator';
'Default Hero tag for Cupertino navigation bars with navigator $navigator';
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
@ -130,8 +129,7 @@ Widget _wrapWithBackground({
Widget result = child; Widget result = child;
if (updateSystemUiOverlay) { if (updateSystemUiOverlay) {
final bool isDark = backgroundColor.computeLuminance() < 0.179; final bool isDark = backgroundColor.computeLuminance() < 0.179;
final Brightness newBrightness = final Brightness newBrightness = brightness ?? (isDark ? Brightness.dark : Brightness.light);
brightness ?? (isDark ? Brightness.dark : Brightness.light);
final SystemUiOverlayStyle overlayStyle; final SystemUiOverlayStyle overlayStyle;
switch (newBrightness) { switch (newBrightness) {
case Brightness.dark: case Brightness.dark:
@ -233,8 +231,7 @@ bool _isTransitionable(BuildContext context) {
/// * [IsekaiSliverNavigationBar] for a navigation bar to be placed in a /// * [IsekaiSliverNavigationBar] for a navigation bar to be placed in a
/// scrolling list and that supports iOS-11-style large titles. /// scrolling list and that supports iOS-11-style large titles.
/// * <https://developer.apple.com/design/human-interface-guidelines/ios/bars/navigation-bars/> /// * <https://developer.apple.com/design/human-interface-guidelines/ios/bars/navigation-bars/>
class IsekaiNavigationBar extends StatefulWidget class IsekaiNavigationBar extends StatefulWidget implements ObstructingPreferredSizeWidget {
implements ObstructingPreferredSizeWidget {
/// Creates a navigation bar in the iOS style. /// Creates a navigation bar in the iOS style.
const IsekaiNavigationBar({ const IsekaiNavigationBar({
super.key, super.key,
@ -447,8 +444,7 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ??
CupertinoTheme.of(context).barBackgroundColor; CupertinoTheme.of(context).barBackgroundColor;
final _NavigationBarStaticComponents components = final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
_NavigationBarStaticComponents(
keys: keys, keys: keys,
route: ModalRoute.of(context), route: ModalRoute.of(context),
userLeading: widget.leading, userLeading: widget.leading,
@ -484,9 +480,7 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
// Get the context that might have a possibly changed CupertinoTheme. // Get the context that might have a possibly changed CupertinoTheme.
builder: (BuildContext context) { builder: (BuildContext context) {
return Hero( return Hero(
tag: widget.heroTag == _defaultHeroTag tag: widget.heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : widget.heroTag,
? _HeroTag(Navigator.of(context))
: widget.heroTag,
createRectTween: _linearTranslateWithLargestRectSizeTween, createRectTween: _linearTranslateWithLargestRectSizeTween,
placeholderBuilder: _navBarHeroLaunchPadBuilder, placeholderBuilder: _navBarHeroLaunchPadBuilder,
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder, flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
@ -494,10 +488,8 @@ class _IsekaiNavigationBarState extends State<IsekaiNavigationBar> {
child: _TransitionableNavigationBar( child: _TransitionableNavigationBar(
componentsKeys: keys, componentsKeys: keys,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
backButtonTextStyle: backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
CupertinoTheme.of(context).textTheme.navActionTextStyle, titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
titleTextStyle:
CupertinoTheme.of(context).textTheme.navTitleTextStyle,
largeTitleTextStyle: null, largeTitleTextStyle: null,
border: widget.border, border: widget.border,
hasUserMiddle: widget.middle != null, hasUserMiddle: widget.middle != null,
@ -696,8 +688,7 @@ class IsekaiSliverNavigationBar extends StatefulWidget {
final bool stretch; final bool stretch;
@override @override
State<IsekaiSliverNavigationBar> createState() => State<IsekaiSliverNavigationBar> createState() => _IsekaiSliverNavigationBarState();
_IsekaiSliverNavigationBarState();
} }
// A state class exists for the nav bar so that the keys of its sub-components // A state class exists for the nav bar so that the keys of its sub-components
@ -714,8 +705,7 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _NavigationBarStaticComponents components = final _NavigationBarStaticComponents components = _NavigationBarStaticComponents(
_NavigationBarStaticComponents(
keys: keys, keys: keys,
route: ModalRoute.of(context), route: ModalRoute.of(context),
userLeading: widget.leading, userLeading: widget.leading,
@ -736,8 +726,7 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
keys: keys, keys: keys,
components: components, components: components,
userMiddle: widget.middle, userMiddle: widget.middle,
backgroundColor: CupertinoDynamicColor.maybeResolve( backgroundColor: CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ??
widget.backgroundColor, context) ??
CupertinoTheme.of(context).barBackgroundColor, CupertinoTheme.of(context).barBackgroundColor,
brightness: widget.brightness, brightness: widget.brightness,
border: widget.border, border: widget.border,
@ -745,18 +734,17 @@ class _IsekaiSliverNavigationBarState extends State<IsekaiSliverNavigationBar> {
actionsForegroundColor: CupertinoTheme.of(context).primaryColor, actionsForegroundColor: CupertinoTheme.of(context).primaryColor,
transitionBetweenRoutes: widget.transitionBetweenRoutes, transitionBetweenRoutes: widget.transitionBetweenRoutes,
heroTag: widget.heroTag, heroTag: widget.heroTag,
persistentHeight: (_kNavBarPersistentHeight * scaleFactor) + persistentHeight:
MediaQuery.of(context).padding.top, (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top,
alwaysShowMiddle: widget.middle != null, alwaysShowMiddle: widget.middle != null,
stretchConfiguration: stretchConfiguration: widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
widget.stretch ? OverScrollHeaderStretchConfiguration() : null,
), ),
); );
} }
} }
class _LargeTitleNavigationBarSliverDelegate class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDelegate
extends SliverPersistentHeaderDelegate with DiagnosticableTreeMixin { with DiagnosticableTreeMixin {
_LargeTitleNavigationBarSliverDelegate({ _LargeTitleNavigationBarSliverDelegate({
required this.keys, required this.keys,
required this.components, required this.components,
@ -794,20 +782,17 @@ class _LargeTitleNavigationBarSliverDelegate
@override @override
double get maxExtent => double get maxExtent =>
persistentHeight + persistentHeight +
(_kNavBarLargeTitleHeightExtension * (_kNavBarLargeTitleHeightExtension * MediaQuery.of(Get.context!).textScaleFactor);
MediaQuery.of(Get.context!).textScaleFactor);
@override @override
OverScrollHeaderStretchConfiguration? stretchConfiguration; OverScrollHeaderStretchConfiguration? stretchConfiguration;
@override @override
Widget build( Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
BuildContext context, double shrinkOffset, bool overlapsContent) {
final bool showLargeTitle = final bool showLargeTitle =
shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold; shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold;
final _PersistentNavigationBar persistentNavigationBar = final _PersistentNavigationBar persistentNavigationBar = _PersistentNavigationBar(
_PersistentNavigationBar(
components: components, components: components,
padding: padding, padding: padding,
// If a user specified middle exists, always show it. Otherwise, show // If a user specified middle exists, always show it. Otherwise, show
@ -851,9 +836,7 @@ class _LargeTitleNavigationBarSliverDelegate
child: Semantics( child: Semantics(
header: true, header: true,
child: DefaultTextStyle( child: DefaultTextStyle(
style: CupertinoTheme.of(context) style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
.textTheme
.navLargeTitleTextStyle,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
child: components.largeTitle!, child: components.largeTitle!,
@ -881,9 +864,7 @@ class _LargeTitleNavigationBarSliverDelegate
} }
return Hero( return Hero(
tag: heroTag == _defaultHeroTag tag: heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : heroTag,
? _HeroTag(Navigator.of(context))
: heroTag,
createRectTween: _linearTranslateWithLargestRectSizeTween, createRectTween: _linearTranslateWithLargestRectSizeTween,
flightShuttleBuilder: _navBarHeroFlightShuttleBuilder, flightShuttleBuilder: _navBarHeroFlightShuttleBuilder,
placeholderBuilder: _navBarHeroLaunchPadBuilder, placeholderBuilder: _navBarHeroLaunchPadBuilder,
@ -893,13 +874,10 @@ class _LargeTitleNavigationBarSliverDelegate
// needs to wrap the top level RenderBox rather than a RenderSliver. // needs to wrap the top level RenderBox rather than a RenderSliver.
child: _TransitionableNavigationBar( child: _TransitionableNavigationBar(
componentsKeys: keys, componentsKeys: keys,
backgroundColor: backgroundColor: CupertinoDynamicColor.resolve(backgroundColor, context),
CupertinoDynamicColor.resolve(backgroundColor, context), backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle,
backButtonTextStyle:
CupertinoTheme.of(context).textTheme.navActionTextStyle,
titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle, titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
largeTitleTextStyle: largeTitleTextStyle: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
border: border, border: border,
hasUserMiddle: userMiddle != null, hasUserMiddle: userMiddle != null,
largeExpanded: showLargeTitle, largeExpanded: showLargeTitle,
@ -994,8 +972,7 @@ class _PersistentNavigationBar extends StatelessWidget {
double scaleFactor = MediaQuery.of(context).textScaleFactor; double scaleFactor = MediaQuery.of(context).textScaleFactor;
return SizedBox( return SizedBox(
height: (_kNavBarPersistentHeight * scaleFactor) + height: (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top,
MediaQuery.of(context).padding.top,
child: SafeArea( child: SafeArea(
bottom: false, bottom: false,
child: paddedToolbar, child: paddedToolbar,
@ -1354,11 +1331,10 @@ class IsekaiNavigationBarBackButton extends StatelessWidget {
); );
} }
TextStyle actionTextStyle = TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle;
CupertinoTheme.of(context).textTheme.navActionTextStyle;
if (color != null) { if (color != null) {
actionTextStyle = actionTextStyle.copyWith( actionTextStyle =
color: CupertinoDynamicColor.maybeResolve(color, context)); actionTextStyle.copyWith(color: CupertinoDynamicColor.maybeResolve(color, context));
} }
return CupertinoButton( return CupertinoButton(
@ -1371,8 +1347,7 @@ class IsekaiNavigationBarBackButton extends StatelessWidget {
child: DefaultTextStyle( child: DefaultTextStyle(
style: actionTextStyle, style: actionTextStyle,
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
@ -1457,8 +1432,7 @@ class _BackLabel extends StatelessWidget {
// `child` is never passed in into ValueListenableBuilder so it's always // `child` is never passed in into ValueListenableBuilder so it's always
// null here and unused. // null here and unused.
Widget _buildPreviousTitleWidget( Widget _buildPreviousTitleWidget(BuildContext context, String? previousTitle, Widget? child) {
BuildContext context, String? previousTitle, Widget? child) {
previousTitle ??= "返回"; previousTitle ??= "返回";
Text textWidget = Text( Text textWidget = Text(
@ -1482,8 +1456,7 @@ class _BackLabel extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (specifiedPreviousTitle != null) { if (specifiedPreviousTitle != null) {
return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null); return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null);
} else if (route is CupertinoRouteTransitionMixin<dynamic> && } else if (route is CupertinoRouteTransitionMixin<dynamic> && !route!.isFirst) {
!route!.isFirst) {
final CupertinoRouteTransitionMixin<dynamic> cupertinoRoute = final CupertinoRouteTransitionMixin<dynamic> cupertinoRoute =
route! as CupertinoRouteTransitionMixin<dynamic>; route! as CupertinoRouteTransitionMixin<dynamic>;
// There is no timing issue because the previousTitle Listenable changes // There is no timing issue because the previousTitle Listenable changes
@ -1534,8 +1507,8 @@ class _TransitionableNavigationBar extends StatelessWidget {
final Widget child; final Widget child;
RenderBox get renderBox { RenderBox get renderBox {
final RenderBox box = componentsKeys.navBarBoxKey.currentContext! final RenderBox box =
.findRenderObject()! as RenderBox; componentsKeys.navBarBoxKey.currentContext!.findRenderObject()! as RenderBox;
assert( assert(
box.attached, box.attached,
'_TransitionableNavigationBar.renderBox should be called when building ' '_TransitionableNavigationBar.renderBox should be called when building '
@ -1645,31 +1618,19 @@ class _NavigationBarTransition extends StatelessWidget {
}, },
), ),
// Draw all the components on top of the empty bar box. // Draw all the components on top of the empty bar box.
if (componentsTransition.bottomBackChevron != null) if (componentsTransition.bottomBackChevron != null) componentsTransition.bottomBackChevron!,
componentsTransition.bottomBackChevron!, if (componentsTransition.bottomBackLabel != null) componentsTransition.bottomBackLabel!,
if (componentsTransition.bottomBackLabel != null) if (componentsTransition.bottomLeading != null) componentsTransition.bottomLeading!,
componentsTransition.bottomBackLabel!, if (componentsTransition.bottomMiddle != null) componentsTransition.bottomMiddle!,
if (componentsTransition.bottomLeading != null) if (componentsTransition.bottomLargeTitle != null) componentsTransition.bottomLargeTitle!,
componentsTransition.bottomLeading!, if (componentsTransition.bottomTrailing != null) componentsTransition.bottomTrailing!,
if (componentsTransition.bottomMiddle != null)
componentsTransition.bottomMiddle!,
if (componentsTransition.bottomLargeTitle != null)
componentsTransition.bottomLargeTitle!,
if (componentsTransition.bottomTrailing != null)
componentsTransition.bottomTrailing!,
// Draw top components on top of the bottom components. // Draw top components on top of the bottom components.
if (componentsTransition.topLeading != null) if (componentsTransition.topLeading != null) componentsTransition.topLeading!,
componentsTransition.topLeading!, if (componentsTransition.topBackChevron != null) componentsTransition.topBackChevron!,
if (componentsTransition.topBackChevron != null) if (componentsTransition.topBackLabel != null) componentsTransition.topBackLabel!,
componentsTransition.topBackChevron!, if (componentsTransition.topMiddle != null) componentsTransition.topMiddle!,
if (componentsTransition.topBackLabel != null) if (componentsTransition.topLargeTitle != null) componentsTransition.topLargeTitle!,
componentsTransition.topBackLabel!, if (componentsTransition.topTrailing != null) componentsTransition.topTrailing!,
if (componentsTransition.topMiddle != null)
componentsTransition.topMiddle!,
if (componentsTransition.topLargeTitle != null)
componentsTransition.topLargeTitle!,
if (componentsTransition.topTrailing != null)
componentsTransition.topTrailing!,
]; ];
// The actual outer box is big enough to contain both the bottom and top // The actual outer box is big enough to contain both the bottom and top
@ -1677,8 +1638,7 @@ class _NavigationBarTransition extends StatelessWidget {
// can actually be outside the linearly lerp'ed Rect in the middle of // can actually be outside the linearly lerp'ed Rect in the middle of
// the animation, such as the topLargeTitle. // the animation, such as the topLargeTitle.
return SizedBox( return SizedBox(
height: math.max(heightTween.begin!, heightTween.end!) + height: math.max(heightTween.begin!, heightTween.end!) + MediaQuery.of(context).padding.top,
MediaQuery.of(context).padding.top,
width: double.infinity, width: double.infinity,
child: Stack( child: Stack(
children: children, children: children,
@ -1731,8 +1691,7 @@ class _NavigationBarComponentsTransition {
topLargeExpanded = topNavBar.largeExpanded, topLargeExpanded = topNavBar.largeExpanded,
transitionBox = transitionBox =
// paintBounds are based on offset zero so it's ok to expand the Rects. // paintBounds are based on offset zero so it's ok to expand the Rects.
bottomNavBar.renderBox.paintBounds bottomNavBar.renderBox.paintBounds.expandToInclude(topNavBar.renderBox.paintBounds),
.expandToInclude(topNavBar.renderBox.paintBounds),
forwardDirection = directionality == TextDirection.ltr ? 1.0 : -1.0; forwardDirection = directionality == TextDirection.ltr ? 1.0 : -1.0;
static final Animatable<double> fadeOut = Tween<double>( static final Animatable<double> fadeOut = Tween<double>(
@ -1779,13 +1738,11 @@ class _NavigationBarComponentsTransition {
GlobalKey key, { GlobalKey key, {
required RenderBox from, required RenderBox from,
}) { }) {
final RenderBox componentBox = final RenderBox componentBox = key.currentContext!.findRenderObject()! as RenderBox;
key.currentContext!.findRenderObject()! as RenderBox;
assert(componentBox.attached); assert(componentBox.attached);
return RelativeRect.fromRect( return RelativeRect.fromRect(
componentBox.localToGlobal(Offset.zero, ancestor: from) & componentBox.localToGlobal(Offset.zero, ancestor: from) & componentBox.size,
componentBox.size,
transitionBox, transitionBox,
); );
} }
@ -1810,10 +1767,8 @@ class _NavigationBarComponentsTransition {
required RenderBox toNavBarBox, required RenderBox toNavBarBox,
required Widget child, required Widget child,
}) { }) {
final RenderBox fromBox = final RenderBox fromBox = fromKey.currentContext!.findRenderObject()! as RenderBox;
fromKey.currentContext!.findRenderObject()! as RenderBox; final RenderBox toBox = toKey.currentContext!.findRenderObject()! as RenderBox;
final RenderBox toBox =
toKey.currentContext!.findRenderObject()! as RenderBox;
final bool isLTR = forwardDirection > 0; final bool isLTR = forwardDirection > 0;
@ -1829,8 +1784,7 @@ class _NavigationBarComponentsTransition {
); );
final Offset fromAnchorInFromBox = final Offset fromAnchorInFromBox =
fromBox.localToGlobal(fromAnchorLocal, ancestor: fromNavBarBox); fromBox.localToGlobal(fromAnchorLocal, ancestor: fromNavBarBox);
final Offset toAnchorInToBox = final Offset toAnchorInToBox = toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox);
toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox);
// We can't get ahold of the render box of the stack (i.e., `transitionBox`) // We can't get ahold of the render box of the stack (i.e., `transitionBox`)
// we place components on yet, but we know the stack needs to be top-leading // we place components on yet, but we know the stack needs to be top-leading
@ -1842,13 +1796,10 @@ class _NavigationBarComponentsTransition {
// coordinates. // coordinates.
final Offset translation = isLTR final Offset translation = isLTR
? toAnchorInToBox - fromAnchorInFromBox ? toAnchorInToBox - fromAnchorInFromBox
: Offset(toNavBarBox.size.width - toAnchorInToBox.dx, : Offset(toNavBarBox.size.width - toAnchorInToBox.dx, toAnchorInToBox.dy) -
toAnchorInToBox.dy) - Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx, fromAnchorInFromBox.dy);
Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx,
fromAnchorInFromBox.dy);
final RelativeRect fromBoxMargin = final RelativeRect fromBoxMargin = positionInTransitionBox(fromKey, from: fromNavBarBox);
positionInTransitionBox(fromKey, from: fromNavBarBox);
final Offset fromOriginInTransitionBox = Offset( final Offset fromOriginInTransitionBox = Offset(
isLTR ? fromBoxMargin.left : fromBoxMargin.right, isLTR ? fromBoxMargin.left : fromBoxMargin.right,
fromBoxMargin.top, fromBoxMargin.top,
@ -1880,16 +1831,14 @@ class _NavigationBarComponentsTransition {
} }
Widget? get bottomLeading { Widget? get bottomLeading {
final KeyedSubtree? bottomLeading = final KeyedSubtree? bottomLeading = bottomComponents.leadingKey.currentWidget as KeyedSubtree?;
bottomComponents.leadingKey.currentWidget as KeyedSubtree?;
if (bottomLeading == null) { if (bottomLeading == null) {
return null; return null;
} }
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: positionInTransitionBox(bottomComponents.leadingKey, rect: positionInTransitionBox(bottomComponents.leadingKey, from: bottomNavBarBox),
from: bottomNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeOutBy(0.4), opacity: fadeOutBy(0.4),
child: bottomLeading.child, child: bottomLeading.child,
@ -1906,8 +1855,7 @@ class _NavigationBarComponentsTransition {
} }
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: positionInTransitionBox(bottomComponents.backChevronKey, rect: positionInTransitionBox(bottomComponents.backChevronKey, from: bottomNavBarBox),
from: bottomNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeOutBy(0.6), opacity: fadeOutBy(0.6),
child: DefaultTextStyle( child: DefaultTextStyle(
@ -1926,9 +1874,8 @@ class _NavigationBarComponentsTransition {
return null; return null;
} }
final RelativeRect from = positionInTransitionBox( final RelativeRect from =
bottomComponents.backLabelKey, positionInTransitionBox(bottomComponents.backLabelKey, from: bottomNavBarBox);
from: bottomNavBarBox);
// Transition away by sliding horizontally to the leading edge off of the screen. // Transition away by sliding horizontally to the leading edge off of the screen.
final RelativeRectTween positionTween = RelativeRectTween( final RelativeRectTween positionTween = RelativeRectTween(
@ -1954,12 +1901,9 @@ class _NavigationBarComponentsTransition {
} }
Widget? get bottomMiddle { Widget? get bottomMiddle {
final KeyedSubtree? bottomMiddle = final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?;
bottomComponents.middleKey.currentWidget as KeyedSubtree?; final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? topBackLabel = final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
topComponents.backLabelKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? topLeading =
topComponents.leadingKey.currentWidget as KeyedSubtree?;
// The middle component is non-null when the nav bar is a large title // The middle component is non-null when the nav bar is a large title
// nav bar but would be invisible when expanded, therefore don't show it here. // nav bar but would be invisible when expanded, therefore don't show it here.
@ -1998,8 +1942,7 @@ class _NavigationBarComponentsTransition {
// fade. // fade.
if (bottomMiddle != null && topLeading != null) { if (bottomMiddle != null && topLeading != null) {
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: positionInTransitionBox(bottomComponents.middleKey, rect: positionInTransitionBox(bottomComponents.middleKey, from: bottomNavBarBox),
from: bottomNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7), opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7),
// Keep the font when transitioning into a non-back label leading. // Keep the font when transitioning into a non-back label leading.
@ -2017,10 +1960,8 @@ class _NavigationBarComponentsTransition {
Widget? get bottomLargeTitle { Widget? get bottomLargeTitle {
final KeyedSubtree? bottomLargeTitle = final KeyedSubtree? bottomLargeTitle =
bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?; bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? topBackLabel = final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
topComponents.backLabelKey.currentWidget as KeyedSubtree?; final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? topLeading =
topComponents.leadingKey.currentWidget as KeyedSubtree?;
if (bottomLargeTitle == null || !bottomLargeExpanded) { if (bottomLargeTitle == null || !bottomLargeExpanded) {
return null; return null;
@ -2056,9 +1997,8 @@ class _NavigationBarComponentsTransition {
if (bottomLargeTitle != null && topLeading != null) { if (bottomLargeTitle != null && topLeading != null) {
// Unlike bottom middle, the bottom large title moves when it can't // Unlike bottom middle, the bottom large title moves when it can't
// transition to the top back label position. // transition to the top back label position.
final RelativeRect from = positionInTransitionBox( final RelativeRect from =
bottomComponents.largeTitleKey, positionInTransitionBox(bottomComponents.largeTitleKey, from: bottomNavBarBox);
from: bottomNavBarBox);
final RelativeRectTween positionTween = RelativeRectTween( final RelativeRectTween positionTween = RelativeRectTween(
begin: from, begin: from,
@ -2097,8 +2037,7 @@ class _NavigationBarComponentsTransition {
} }
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: positionInTransitionBox(bottomComponents.trailingKey, rect: positionInTransitionBox(bottomComponents.trailingKey, from: bottomNavBarBox),
from: bottomNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeOutBy(0.6), opacity: fadeOutBy(0.6),
child: bottomTrailing.child, child: bottomTrailing.child,
@ -2107,16 +2046,14 @@ class _NavigationBarComponentsTransition {
} }
Widget? get topLeading { Widget? get topLeading {
final KeyedSubtree? topLeading = final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?;
topComponents.leadingKey.currentWidget as KeyedSubtree?;
if (topLeading == null) { if (topLeading == null) {
return null; return null;
} }
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: rect: positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox),
positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeInFrom(0.6), opacity: fadeInFrom(0.6),
child: topLeading.child, child: topLeading.child,
@ -2134,17 +2071,15 @@ class _NavigationBarComponentsTransition {
return null; return null;
} }
final RelativeRect to = positionInTransitionBox( final RelativeRect to =
topComponents.backChevronKey, positionInTransitionBox(topComponents.backChevronKey, from: topNavBarBox);
from: topNavBarBox);
RelativeRect from = to; RelativeRect from = to;
// If it's the first page with a back chevron, shift in slightly from the // If it's the first page with a back chevron, shift in slightly from the
// right. // right.
if (bottomBackChevron == null) { if (bottomBackChevron == null) {
final RenderBox topBackChevronBox = final RenderBox topBackChevronBox =
topComponents.backChevronKey.currentContext!.findRenderObject()! topComponents.backChevronKey.currentContext!.findRenderObject()! as RenderBox;
as RenderBox;
from = to.shift( from = to.shift(
Offset( Offset(
forwardDirection * topBackChevronBox.size.width * 2.0, forwardDirection * topBackChevronBox.size.width * 2.0,
@ -2171,24 +2106,20 @@ class _NavigationBarComponentsTransition {
} }
Widget? get topBackLabel { Widget? get topBackLabel {
final KeyedSubtree? bottomMiddle = final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?;
bottomComponents.middleKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? bottomLargeTitle = final KeyedSubtree? bottomLargeTitle =
bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?; bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?;
final KeyedSubtree? topBackLabel = final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?;
topComponents.backLabelKey.currentWidget as KeyedSubtree?;
if (topBackLabel == null) { if (topBackLabel == null) {
return null; return null;
} }
final RenderAnimatedOpacity? topBackLabelOpacity = topComponents final RenderAnimatedOpacity? topBackLabelOpacity = topComponents.backLabelKey.currentContext
.backLabelKey.currentContext
?.findAncestorRenderObjectOfType<RenderAnimatedOpacity>(); ?.findAncestorRenderObjectOfType<RenderAnimatedOpacity>();
Animation<double>? midClickOpacity; Animation<double>? midClickOpacity;
if (topBackLabelOpacity != null && if (topBackLabelOpacity != null && topBackLabelOpacity.opacity.value < 1.0) {
topBackLabelOpacity.opacity.value < 1.0) {
midClickOpacity = animation.drive(Tween<double>( midClickOpacity = animation.drive(Tween<double>(
begin: 0.0, begin: 0.0,
end: topBackLabelOpacity.opacity.value, end: topBackLabelOpacity.opacity.value,
@ -2200,9 +2131,7 @@ class _NavigationBarComponentsTransition {
// content text might be different. For instance, if the bottomLargeTitle // content text might be different. For instance, if the bottomLargeTitle
// text is too long, the topBackLabel will say 'Back' instead of the original // text is too long, the topBackLabel will say 'Back' instead of the original
// text. // text.
if (bottomLargeTitle != null && if (bottomLargeTitle != null && topBackLabel != null && bottomLargeExpanded) {
topBackLabel != null &&
bottomLargeExpanded) {
return slideFromLeadingEdge( return slideFromLeadingEdge(
fromKey: bottomComponents.largeTitleKey, fromKey: bottomComponents.largeTitleKey,
fromNavBarBox: bottomNavBarBox, fromNavBarBox: bottomNavBarBox,
@ -2248,8 +2177,7 @@ class _NavigationBarComponentsTransition {
} }
Widget? get topMiddle { Widget? get topMiddle {
final KeyedSubtree? topMiddle = final KeyedSubtree? topMiddle = topComponents.middleKey.currentWidget as KeyedSubtree?;
topComponents.middleKey.currentWidget as KeyedSubtree?;
if (topMiddle == null) { if (topMiddle == null) {
return null; return null;
@ -2261,10 +2189,9 @@ class _NavigationBarComponentsTransition {
return null; return null;
} }
final RelativeRect to = final RelativeRect to = positionInTransitionBox(topComponents.middleKey, from: topNavBarBox);
positionInTransitionBox(topComponents.middleKey, from: topNavBarBox); final RenderBox toBox =
final RenderBox toBox = topComponents.middleKey.currentContext! topComponents.middleKey.currentContext!.findRenderObject()! as RenderBox;
.findRenderObject()! as RenderBox;
final bool isLTR = forwardDirection > 0; final bool isLTR = forwardDirection > 0;
@ -2301,16 +2228,14 @@ class _NavigationBarComponentsTransition {
} }
Widget? get topTrailing { Widget? get topTrailing {
final KeyedSubtree? topTrailing = final KeyedSubtree? topTrailing = topComponents.trailingKey.currentWidget as KeyedSubtree?;
topComponents.trailingKey.currentWidget as KeyedSubtree?;
if (topTrailing == null) { if (topTrailing == null) {
return null; return null;
} }
return Positioned.fromRelativeRect( return Positioned.fromRelativeRect(
rect: positionInTransitionBox(topComponents.trailingKey, rect: positionInTransitionBox(topComponents.trailingKey, from: topNavBarBox),
from: topNavBarBox),
child: FadeTransition( child: FadeTransition(
opacity: fadeInFrom(0.4), opacity: fadeInFrom(0.4),
child: topTrailing.child, child: topTrailing.child,
@ -2319,15 +2244,14 @@ class _NavigationBarComponentsTransition {
} }
Widget? get topLargeTitle { Widget? get topLargeTitle {
final KeyedSubtree? topLargeTitle = final KeyedSubtree? topLargeTitle = topComponents.largeTitleKey.currentWidget as KeyedSubtree?;
topComponents.largeTitleKey.currentWidget as KeyedSubtree?;
if (topLargeTitle == null || !topLargeExpanded) { if (topLargeTitle == null || !topLargeExpanded) {
return null; return null;
} }
final RelativeRect to = positionInTransitionBox(topComponents.largeTitleKey, final RelativeRect to =
from: topNavBarBox); positionInTransitionBox(topComponents.largeTitleKey, from: topNavBarBox);
// Shift in from the trailing edge of the screen. // Shift in from the trailing edge of the screen.
final RelativeRectTween positionTween = RelativeRectTween( final RelativeRectTween positionTween = RelativeRectTween(
@ -2417,8 +2341,7 @@ Widget _navBarHeroFlightShuttleBuilder(
final _TransitionableNavigationBar fromNavBar = final _TransitionableNavigationBar fromNavBar =
fromHeroWidget.child as _TransitionableNavigationBar; fromHeroWidget.child as _TransitionableNavigationBar;
final _TransitionableNavigationBar toNavBar = final _TransitionableNavigationBar toNavBar = toHeroWidget.child as _TransitionableNavigationBar;
toHeroWidget.child as _TransitionableNavigationBar;
assert(fromNavBar.componentsKeys != null); assert(fromNavBar.componentsKeys != null);
assert(toNavBar.componentsKeys != null); assert(toNavBar.componentsKeys != null);

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -9,15 +11,44 @@ import 'package:like_button/like_button.dart';
import 'package:pull_down_button/pull_down_button.dart'; import 'package:pull_down_button/pull_down_button.dart';
import 'package:skeletons/skeletons.dart'; import 'package:skeletons/skeletons.dart';
import '../styles.dart';
typedef AddFavoriteCallback = Future<bool> Function(PageInfo pageInfo, bool localIsFavorite); typedef AddFavoriteCallback = Future<bool> Function(PageInfo pageInfo, bool localIsFavorite);
typedef PageInfoCallback = Future<void> Function(PageInfo pageInfo); typedef PageInfoCallback = Future<void> Function(PageInfo pageInfo);
class PageCardStyles { class PageCardStyles {
static const double cardInnerHeight = 150; static const cardContainerHeight = 100.0;
static const cardInnerPadding = EdgeInsets.only(top: 16, left: 20, right: 20, bottom: 12); static const cardContentHeight = 100.0;
static const cardHeaderPadding = EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 16);
static const cardHeaderCompactPadding = EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10);
static const cardContentPadding = EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14);
static const cardFooterPadding = EdgeInsets.only(top: 0, left: 20, right: 20, bottom: 14);
static const titleFontSize = 24.0;
static const subTitleFontSize = 12.0;
static const contentFontSize = 14.0;
static const TextStyle titleTextStyle = TextStyle(
color: CupertinoDynamicColor.withBrightness(color: Colors.black, darkColor: Colors.white),
fontSize: titleFontSize,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w700,
height: 1.2,
);
static const TextStyle subTitleTextStyle = TextStyle(
color: Color.fromRGBO(102, 102, 102, 1),
fontSize: subTitleFontSize,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w700,
height: 1.2,
);
static const TextStyle contentTextStyle = TextStyle(
color: Color.fromRGBO(102, 102, 102, 1),
fontSize: contentFontSize,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
height: 1.4);
static const double footerButtonSize = 30; static const double footerButtonSize = 30;
static const double footerButtonInnerSize = 26; static const double footerButtonInnerSize = 26;
} }
@ -106,24 +137,42 @@ class PageCard extends StatelessWidget {
var textScale = MediaQuery.of(context).textScaleFactor; var textScale = MediaQuery.of(context).textScaleFactor;
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 10), padding: pageInfo?.subtitle == null
child: Skeleton( ? PageCardStyles.cardHeaderPadding
isLoading: isLoading, : PageCardStyles.cardHeaderCompactPadding,
skeleton: SkeletonLine( child: Column(
style: SkeletonLineStyle( crossAxisAlignment: CrossAxisAlignment.start,
height: (Styles.pageCardTitle.fontSize! + 4) * textScale, randomLength: true), children: [
), if (pageInfo?.subtitle != null)
child: Text(pageInfo?.mainTitle ?? "页面信息丢失", style: Styles.pageCardTitle), Text(
pageInfo!.subtitle!,
style: PageCardStyles.subTitleTextStyle,
textScaleFactor: max(1, MediaQuery.of(context).textScaleFactor),
),
if (pageInfo?.subtitle != null) const SizedBox(height: 6),
Skeleton(
isLoading: isLoading,
skeleton: SkeletonLine(
style: SkeletonLineStyle(
height: (PageCardStyles.titleFontSize * 1.1) * textScale, randomLength: true),
),
child: Text(
pageInfo?.mainTitle ?? "页面信息丢失",
style: PageCardStyles.titleTextStyle,
textScaleFactor: 1,
),
),
],
), ),
); );
} }
Widget _buildCardBody(BuildContext context) { Widget _buildCardBody(BuildContext context) {
var textScale = MediaQuery.of(context).textScaleFactor; double textScale = max(1, MediaQuery.of(context).textScaleFactor);
return Expanded( return Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 8), padding: PageCardStyles.cardContentPadding,
child: IntrinsicHeight( child: IntrinsicHeight(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@ -132,18 +181,26 @@ class PageCard extends StatelessWidget {
flex: 1, flex: 1,
child: isLoading child: isLoading
? ClipRect( ? ClipRect(
clipBehavior: Clip.antiAlias,
child: SkeletonParagraph( child: SkeletonParagraph(
style: SkeletonParagraphStyle( style: SkeletonParagraphStyle(
lines: 3, lines: 3,
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), padding: EdgeInsets.symmetric(
vertical: PageCardStyles.contentFontSize * textScale * 0.2,
horizontal: 0,
),
lineStyle: SkeletonLineStyle( lineStyle: SkeletonLineStyle(
randomLength: true, randomLength: true,
height: Styles.pageCardDescription.fontSize! * textScale), height: PageCardStyles.contentFontSize * textScale),
), ),
), ),
) )
: Text(pageInfo?.description ?? "没有简介", : Text(
overflow: TextOverflow.fade, style: Styles.pageCardDescription), pageInfo?.description ?? "没有简介",
overflow: TextOverflow.fade,
style: PageCardStyles.contentTextStyle,
textScaleFactor: textScale,
),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Skeleton( Skeleton(
@ -162,77 +219,83 @@ class PageCard extends StatelessWidget {
Widget _buildCardFooter(BuildContext context) { Widget _buildCardFooter(BuildContext context) {
return ScaleTapIgnore( return ScaleTapIgnore(
child: Row( child: Padding(
crossAxisAlignment: CrossAxisAlignment.center, padding: PageCardStyles.cardFooterPadding,
children: [ child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
pageInfo?.mainCategory != null children: [
? Chip( //
backgroundColor: const Color.fromARGB(1, 238, 238, 238), pageInfo?.mainCategory != null
label: ? Chip(
Text(pageInfo!.mainCategory!, style: const TextStyle(color: Colors.black54)), backgroundColor: const Color.fromARGB(1, 238, 238, 238),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap) label: Text(pageInfo!.mainCategory!,
: const SizedBox(), style: const TextStyle(color: Colors.black54)),
pageInfo?.mainCategory != null ? const SizedBox(width: 10) : const SizedBox(), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap)
// : const SizedBox(),
Skeleton( pageInfo?.mainCategory != null ? const SizedBox(width: 10) : const SizedBox(),
isLoading: isLoading, //
skeleton: const SkeletonLine( Skeleton(
style: SkeletonLineStyle(width: 100), isLoading: isLoading,
), skeleton: const SkeletonLine(
child: Text( style: SkeletonLineStyle(width: 100),
),
child: Text(
pageInfo?.updatedTime != null ? Utils.getFriendDate(pageInfo!.updatedTime!) : "", pageInfo?.updatedTime != null ? Utils.getFriendDate(pageInfo!.updatedTime!) : "",
style: Styles.pageCardDescription), overflow: TextOverflow.fade,
), style: PageCardStyles.contentTextStyle,
const Spacer(), textScaleFactor: max(1, MediaQuery.of(context).textScaleFactor),
// ),
_actionButtonSkeleton(
isLoading,
LikeButton(
size: PageCardStyles.footerButtonInnerSize,
isLiked: isFavorite,
onTap: handleFavoriteClick,
likeBuilder: (bool isLiked) {
return Icon(
isLiked ? Icons.favorite : Icons.favorite_border,
color: isLiked ? Colors.red : Colors.grey,
size: PageCardStyles.footerButtonInnerSize,
);
},
), ),
), const Spacer(),
const SizedBox(width: 18), //
// _actionButtonSkeleton(
_actionButtonSkeleton( isLoading,
isLoading, LikeButton(
SizedBox( size: PageCardStyles.footerButtonInnerSize,
height: PageCardStyles.footerButtonSize, isLiked: isFavorite,
width: PageCardStyles.footerButtonSize, onTap: handleFavoriteClick,
child: PullDownButton( likeBuilder: (bool isLiked) {
routeTheme: PullDownMenuRouteTheme( return Icon(
endShadow: BoxShadow( isLiked ? Icons.favorite : Icons.favorite_border,
color: Colors.grey.withOpacity(0.6), color: isLiked ? Colors.red : Colors.grey,
spreadRadius: 1, size: PageCardStyles.footerButtonInnerSize,
blurRadius: 20, );
offset: const Offset(0, 2), },
),
),
const SizedBox(width: 18),
//
_actionButtonSkeleton(
isLoading,
SizedBox(
height: PageCardStyles.footerButtonSize,
width: PageCardStyles.footerButtonSize,
child: PullDownButton(
routeTheme: PullDownMenuRouteTheme(
endShadow: BoxShadow(
color: Colors.grey.withOpacity(0.6),
spreadRadius: 1,
blurRadius: 20,
offset: const Offset(0, 2),
),
), ),
), itemBuilder: _buildMenuItem,
itemBuilder: _buildMenuItem, position: PullDownMenuPosition.under,
position: PullDownMenuPosition.under, buttonBuilder: (context, showMenu) => IconButton(
buttonBuilder: (context, showMenu) => IconButton( onPressed: showMenu,
onPressed: showMenu, padding: const EdgeInsets.all(0.0),
padding: const EdgeInsets.all(0.0), splashRadius: PageCardStyles.footerButtonInnerSize - 4,
splashRadius: PageCardStyles.footerButtonInnerSize - 4, iconSize: PageCardStyles.footerButtonInnerSize,
iconSize: PageCardStyles.footerButtonInnerSize, icon: const Icon(
icon: const Icon( Icons.more_horiz,
Icons.more_horiz, color: Colors.grey,
color: Colors.grey, ),
), ),
), ),
), ),
), ),
), ],
], ),
), ),
); );
} }
@ -276,30 +339,33 @@ class PageCard extends StatelessWidget {
Widget _buildCard(BuildContext context) { Widget _buildCard(BuildContext context) {
var textScale = MediaQuery.of(context).textScaleFactor; var textScale = MediaQuery.of(context).textScaleFactor;
return Card( return Container(
elevation: 4.0, margin: Theme.of(context).cardTheme.margin,
// clipBehavior: Theme.of(context).cardTheme.clipBehavior ?? Clip.antiAlias,
shape: RoundedRectangleBorder( decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(Styles.isXs ? 0 : 14.0)), color: Theme.of(context).cardTheme.color,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Theme.of(context).cardTheme.shadowColor ?? Colors.transparent,
blurRadius: 16,
offset: const Offset(0, 2),
blurStyle: BlurStyle.outer),
],
), ),
// 齿 child: SizedBox(
clipBehavior: Clip.antiAlias, height: PageCardStyles.cardContainerHeight +
semanticContainer: false, (PageCardStyles.cardContentHeight * max(1, MediaQuery.of(context).textScaleFactor)),
child: Padding( child: Column(
padding: PageCardStyles.cardInnerPadding, crossAxisAlignment: CrossAxisAlignment.start,
child: SizedBox( children: [
height: PageCardStyles.cardInnerHeight * textScale, //
child: Column( _buildCardHeader(context),
crossAxisAlignment: CrossAxisAlignment.start, //
children: [ _buildCardBody(context),
// // Footer
_buildCardHeader(context), _buildCardFooter(context),
// ],
_buildCardBody(context),
// Footer
_buildCardFooter(context),
],
),
), ),
), ),
); );

@ -11,7 +11,7 @@ import 'package:pull_down_button/pull_down_button.dart';
import 'package:skeletons/skeletons.dart'; import 'package:skeletons/skeletons.dart';
import '../styles.dart'; import '../styles.dart';
/*
typedef AddFavoriteCallback = Future<bool> Function( typedef AddFavoriteCallback = Future<bool> Function(
PageInfo pageInfo, bool localIsFavorite, bool showToast); PageInfo pageInfo, bool localIsFavorite, bool showToast);
@ -144,9 +144,9 @@ class _PageCardState extends ReactiveState<PageCard> {
isLoading: c.isLoading.value, isLoading: c.isLoading.value,
skeleton: SkeletonLine( skeleton: SkeletonLine(
style: SkeletonLineStyle( style: SkeletonLineStyle(
height: (Styles.pageCardTitle.fontSize! + 4) * textScale, randomLength: true), height: (Styles.titleTextStyle.fontSize! + 4) * textScale, randomLength: true),
), ),
child: Text(c.pageInfo.value?.mainTitle ?? "页面信息丢失", style: Styles.pageCardTitle), child: Text(c.pageInfo.value?.mainTitle ?? "页面信息丢失", style: Styles.titleTextStyle),
), ),
); );
} }
@ -169,12 +169,12 @@ class _PageCardState extends ReactiveState<PageCard> {
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0),
lineStyle: SkeletonLineStyle( lineStyle: SkeletonLineStyle(
randomLength: true, randomLength: true,
height: Styles.pageCardDescription.fontSize! * textScale), height: Styles.contentTextStyle.fontSize! * textScale),
), ),
), ),
) )
: Text(c.pageInfo.value?.description ?? "没有简介", : Text(c.pageInfo.value?.description ?? "没有简介",
overflow: TextOverflow.fade, style: Styles.pageCardDescription), overflow: TextOverflow.fade, style: Styles.contentTextStyle),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Skeleton( Skeleton(
@ -215,7 +215,7 @@ class _PageCardState extends ReactiveState<PageCard> {
c.pageInfo.value?.updatedTime != null c.pageInfo.value?.updatedTime != null
? Utils.getFriendDate(c.pageInfo.value!.updatedTime!) ? Utils.getFriendDate(c.pageInfo.value!.updatedTime!)
: "", : "",
style: Styles.pageCardDescription), style: Styles.contentTextStyle),
), ),
const Spacer(), const Spacer(),
// //
@ -353,3 +353,4 @@ class _PageCardState extends ReactiveState<PageCard> {
); );
} }
} }
*/

@ -134,6 +134,7 @@ class RecentPageList extends StatelessWidget {
Widget _buildPageCard( Widget _buildPageCard(
int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController flc) { int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController flc) {
return PageCard( return PageCard(
key: ValueKey("rpl-card-$index"),
pageInfo: c.pageList[index], pageInfo: c.pageList[index],
isFavorite: flc.isFavorite(c.pageList[index]), isFavorite: flc.isFavorite(c.pageList[index]),
onSetFavorite: flc.setFavoriteImmediate, onSetFavorite: flc.setFavoriteImmediate,

@ -23,6 +23,8 @@ class SiteConfig {
String restfulApiUrl; String restfulApiUrl;
String pageUrlTemplate; String pageUrlTemplate;
bool enableFollowing;
SiteConfig({ SiteConfig({
this.moduleStyles = const [], this.moduleStyles = const [],
this.moduleScripts = const [], this.moduleScripts = const [],
@ -33,6 +35,7 @@ class SiteConfig {
this.resourceLoaderUrl = "", this.resourceLoaderUrl = "",
this.restfulApiUrl = "", this.restfulApiUrl = "",
this.pageUrlTemplate = "", this.pageUrlTemplate = "",
this.enableFollowing = false,
}); });
factory SiteConfig.fromJson(Map<String, dynamic> json) => _$SiteConfigFromJson(json); factory SiteConfig.fromJson(Map<String, dynamic> json) => _$SiteConfigFromJson(json);

@ -142,8 +142,8 @@ class HomeTab extends StatelessWidget {
backgroundColor: Styles.themeMainColor, backgroundColor: Styles.themeMainColor,
brightness: Brightness.dark, brightness: Brightness.dark,
largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)), largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)),
border: Border.all(style: BorderStyle.none),
trailing: _buildNotificationIconButton(), trailing: _buildNotificationIconButton(),
border: Border.all(style: BorderStyle.none),
), ),
SliverPersistentHeader( SliverPersistentHeader(
delegate: _SliverAppBarDelegate( delegate: _SliverAppBarDelegate(
@ -184,39 +184,40 @@ class HomeTab extends StatelessWidget {
), ),
), ),
), ),
SliverPersistentHeader( if (Global.siteConfig.enableFollowing) //
pinned: true, SliverPersistentHeader(
delegate: _SliverAppBarDelegate( pinned: true,
minHeight: 40.0, delegate: _SliverAppBarDelegate(
maxHeight: 40.0, minHeight: 40.0,
child: Container( maxHeight: 40.0,
decoration: BoxDecoration(color: Colors.white, boxShadow: [ child: Container(
BoxShadow( decoration: BoxDecoration(color: Colors.white, boxShadow: [
color: Colors.grey.withOpacity(0.2), BoxShadow(
spreadRadius: 2, color: Colors.grey.withOpacity(0.2),
blurRadius: 4, spreadRadius: 2,
offset: const Offset(0, 2), blurRadius: 4,
) offset: const Offset(0, 2),
]), )
child: Row( ]),
children: [ child: Row(
SizedBox( children: [
child: TabBar( SizedBox(
isScrollable: true, child: TabBar(
controller: c.tabController, isScrollable: true,
indicatorColor: Styles.themeMainColor, controller: c.tabController,
labelColor: Styles.themeMainColor, indicatorColor: Styles.themeMainColor,
unselectedLabelColor: Colors.black45, labelColor: Styles.themeMainColor,
tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')], unselectedLabelColor: Colors.black45,
onTap: (int selected) {}, tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')],
onTap: (int selected) {},
),
), ),
), const Expanded(child: Text('')),
const Expanded(child: Text('')), ],
], ),
), ),
), ),
), ),
),
CupertinoSliverRefreshControl( CupertinoSliverRefreshControl(
onRefresh: c.handleRefresh, onRefresh: c.handleRefresh,
), ),

@ -23,6 +23,30 @@ abstract class Styles {
), ),
); );
static CupertinoThemeData cupertinoLightTheme = const CupertinoThemeData(
textTheme: Styles.defaultTextTheme,
scaffoldBackgroundColor: Styles.themePageBackgroundColor,
);
static CupertinoThemeData cupertinoDarkTheme = const CupertinoThemeData(
brightness: Brightness.dark,
textTheme: Styles.defaultTextTheme,
scaffoldBackgroundColor: Styles.themePageBackgroundColor,
);
static ThemeData materialLightTheme = ThemeData.light().copyWith(
cardTheme: cardTheme,
);
static ThemeData materialDarkTheme = ThemeData.light().copyWith(
cardTheme: cardTheme.copyWith(color: const Color.fromRGBO(28, 28, 28, 1)),
);
static CardTheme cardTheme = CardTheme(
shadowColor: CupertinoColors.black.withOpacity(0.2),
margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 8),
);
static const TextStyle navLargeTitleTextStyle = TextStyle( static const TextStyle navLargeTitleTextStyle = TextStyle(
fontWeight: FontWeight.normal, fontSize: 32, color: CupertinoColors.label, inherit: false); fontWeight: FontWeight.normal, fontSize: 32, color: CupertinoColors.label, inherit: false);
@ -40,25 +64,12 @@ abstract class Styles {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
); );
static const TextStyle pageCardTitle = TextStyle(
fontSize: 20,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.bold,
);
static const TextStyle articleTitle = TextStyle( static const TextStyle articleTitle = TextStyle(
fontSize: 28, fontSize: 28,
fontStyle: FontStyle.normal, fontStyle: FontStyle.normal,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
); );
static const TextStyle pageCardDescription = TextStyle(
color: Colors.black54,
fontSize: 14,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static const TextStyle listTileLargeTitle = TextStyle(fontSize: 18); static const TextStyle listTileLargeTitle = TextStyle(fontSize: 18);
static const TextStyle listTileSubTitle = TextStyle(fontSize: 16); static const TextStyle listTileSubTitle = TextStyle(fontSize: 16);
@ -70,6 +81,7 @@ abstract class Styles {
static const Color themeNavTitleColor = Color.fromRGBO(255, 255, 255, 1); static const Color themeNavTitleColor = Color.fromRGBO(255, 255, 255, 1);
static const Color themeBottomColor = Color.fromRGBO(255, 255, 255, 1); static const Color themeBottomColor = Color.fromRGBO(255, 255, 255, 1);
static const Color themePageBackgroundColor = Color.fromRGBO(240, 240, 240, 1); static const Color themePageBackgroundColor = Color.fromRGBO(240, 240, 240, 1);
static const Color darkThemePageBackgroundColor = Color.fromRGBO(0, 0, 0, 1);
static const Color themeNavSegmentTextColor = Color.fromRGBO(12, 12, 12, 1); static const Color themeNavSegmentTextColor = Color.fromRGBO(12, 12, 12, 1);
static const Color panelBackgroundColor = Color.fromRGBO(255, 255, 255, 1); static const Color panelBackgroundColor = Color.fromRGBO(255, 255, 255, 1);
static const Color linkColor = CupertinoColors.link; static const Color linkColor = CupertinoColors.link;

Loading…
Cancel
Save