From 484810d3902b92074a5ce6f4739db388d803d72d Mon Sep 17 00:00:00 2001 From: Lex Lim Date: Wed, 18 Jan 2023 17:12:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=8D=A1=E7=89=87=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app.dart | 15 +- lib/components/isekai_nav_bar.dart | 253 +++++++++--------------- lib/components/page_card.dart | 276 +++++++++++++++++---------- lib/components/page_card.getx.dart | 13 +- lib/components/recent_page_list.dart | 1 + lib/models/site_config.dart | 3 + lib/pages/home.dart | 61 +++--- lib/styles.dart | 38 ++-- 8 files changed, 337 insertions(+), 323 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index a5b03e6..07a2b4a 100755 --- a/lib/app.dart +++ b/lib/app.dart @@ -31,9 +31,7 @@ class IsekaiWikiApp extends StatelessWidget { return Material( child: GetCupertinoApp( title: '异世界百科', - theme: const CupertinoThemeData( - textTheme: Styles.defaultTextTheme, - scaffoldBackgroundColor: Styles.themePageBackgroundColor), + theme: Styles.cupertinoLightTheme, localizationsDelegates: const >[ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, @@ -51,7 +49,16 @@ class IsekaiWikiApp extends StatelessWidget { } else { Styles.textScaleFactor = MediaQuery.of(context).textScaleFactor; 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, diff --git a/lib/components/isekai_nav_bar.dart b/lib/components/isekai_nav_bar.dart index 63f8533..f8e6ce8 100755 --- a/lib/components/isekai_nav_bar.dart +++ b/lib/components/isekai_nav_bar.dart @@ -52,8 +52,7 @@ class _HeroTag { // Let the Hero tag be described in tree dumps. @override - String toString() => - 'Default Hero tag for Cupertino navigation bars with navigator $navigator'; + String toString() => 'Default Hero tag for Cupertino navigation bars with navigator $navigator'; @override bool operator ==(Object other) { @@ -130,8 +129,7 @@ Widget _wrapWithBackground({ Widget result = child; if (updateSystemUiOverlay) { final bool isDark = backgroundColor.computeLuminance() < 0.179; - final Brightness newBrightness = - brightness ?? (isDark ? Brightness.dark : Brightness.light); + final Brightness newBrightness = brightness ?? (isDark ? Brightness.dark : Brightness.light); final SystemUiOverlayStyle overlayStyle; switch (newBrightness) { case Brightness.dark: @@ -233,8 +231,7 @@ bool _isTransitionable(BuildContext context) { /// * [IsekaiSliverNavigationBar] for a navigation bar to be placed in a /// scrolling list and that supports iOS-11-style large titles. /// * -class IsekaiNavigationBar extends StatefulWidget - implements ObstructingPreferredSizeWidget { +class IsekaiNavigationBar extends StatefulWidget implements ObstructingPreferredSizeWidget { /// Creates a navigation bar in the iOS style. const IsekaiNavigationBar({ super.key, @@ -447,8 +444,7 @@ class _IsekaiNavigationBarState extends State { CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? CupertinoTheme.of(context).barBackgroundColor; - final _NavigationBarStaticComponents components = - _NavigationBarStaticComponents( + final _NavigationBarStaticComponents components = _NavigationBarStaticComponents( keys: keys, route: ModalRoute.of(context), userLeading: widget.leading, @@ -484,9 +480,7 @@ class _IsekaiNavigationBarState extends State { // Get the context that might have a possibly changed CupertinoTheme. builder: (BuildContext context) { return Hero( - tag: widget.heroTag == _defaultHeroTag - ? _HeroTag(Navigator.of(context)) - : widget.heroTag, + tag: widget.heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : widget.heroTag, createRectTween: _linearTranslateWithLargestRectSizeTween, placeholderBuilder: _navBarHeroLaunchPadBuilder, flightShuttleBuilder: _navBarHeroFlightShuttleBuilder, @@ -494,10 +488,8 @@ class _IsekaiNavigationBarState extends State { child: _TransitionableNavigationBar( componentsKeys: keys, backgroundColor: backgroundColor, - backButtonTextStyle: - CupertinoTheme.of(context).textTheme.navActionTextStyle, - titleTextStyle: - CupertinoTheme.of(context).textTheme.navTitleTextStyle, + backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle, + titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle, largeTitleTextStyle: null, border: widget.border, hasUserMiddle: widget.middle != null, @@ -696,8 +688,7 @@ class IsekaiSliverNavigationBar extends StatefulWidget { final bool stretch; @override - State createState() => - _IsekaiSliverNavigationBarState(); + State createState() => _IsekaiSliverNavigationBarState(); } // A state class exists for the nav bar so that the keys of its sub-components @@ -714,8 +705,7 @@ class _IsekaiSliverNavigationBarState extends State { @override Widget build(BuildContext context) { - final _NavigationBarStaticComponents components = - _NavigationBarStaticComponents( + final _NavigationBarStaticComponents components = _NavigationBarStaticComponents( keys: keys, route: ModalRoute.of(context), userLeading: widget.leading, @@ -736,8 +726,7 @@ class _IsekaiSliverNavigationBarState extends State { keys: keys, components: components, userMiddle: widget.middle, - backgroundColor: CupertinoDynamicColor.maybeResolve( - widget.backgroundColor, context) ?? + backgroundColor: CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? CupertinoTheme.of(context).barBackgroundColor, brightness: widget.brightness, border: widget.border, @@ -745,18 +734,17 @@ class _IsekaiSliverNavigationBarState extends State { actionsForegroundColor: CupertinoTheme.of(context).primaryColor, transitionBetweenRoutes: widget.transitionBetweenRoutes, heroTag: widget.heroTag, - persistentHeight: (_kNavBarPersistentHeight * scaleFactor) + - MediaQuery.of(context).padding.top, + persistentHeight: + (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top, alwaysShowMiddle: widget.middle != null, - stretchConfiguration: - widget.stretch ? OverScrollHeaderStretchConfiguration() : null, + stretchConfiguration: widget.stretch ? OverScrollHeaderStretchConfiguration() : null, ), ); } } -class _LargeTitleNavigationBarSliverDelegate - extends SliverPersistentHeaderDelegate with DiagnosticableTreeMixin { +class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDelegate + with DiagnosticableTreeMixin { _LargeTitleNavigationBarSliverDelegate({ required this.keys, required this.components, @@ -794,20 +782,17 @@ class _LargeTitleNavigationBarSliverDelegate @override double get maxExtent => persistentHeight + - (_kNavBarLargeTitleHeightExtension * - MediaQuery.of(Get.context!).textScaleFactor); + (_kNavBarLargeTitleHeightExtension * MediaQuery.of(Get.context!).textScaleFactor); @override OverScrollHeaderStretchConfiguration? stretchConfiguration; @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { final bool showLargeTitle = shrinkOffset < maxExtent - minExtent - _kNavBarShowLargeTitleThreshold; - final _PersistentNavigationBar persistentNavigationBar = - _PersistentNavigationBar( + final _PersistentNavigationBar persistentNavigationBar = _PersistentNavigationBar( components: components, padding: padding, // If a user specified middle exists, always show it. Otherwise, show @@ -851,9 +836,7 @@ class _LargeTitleNavigationBarSliverDelegate child: Semantics( header: true, child: DefaultTextStyle( - style: CupertinoTheme.of(context) - .textTheme - .navLargeTitleTextStyle, + style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle, maxLines: 1, overflow: TextOverflow.ellipsis, child: components.largeTitle!, @@ -881,9 +864,7 @@ class _LargeTitleNavigationBarSliverDelegate } return Hero( - tag: heroTag == _defaultHeroTag - ? _HeroTag(Navigator.of(context)) - : heroTag, + tag: heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : heroTag, createRectTween: _linearTranslateWithLargestRectSizeTween, flightShuttleBuilder: _navBarHeroFlightShuttleBuilder, placeholderBuilder: _navBarHeroLaunchPadBuilder, @@ -893,13 +874,10 @@ class _LargeTitleNavigationBarSliverDelegate // needs to wrap the top level RenderBox rather than a RenderSliver. child: _TransitionableNavigationBar( componentsKeys: keys, - backgroundColor: - CupertinoDynamicColor.resolve(backgroundColor, context), - backButtonTextStyle: - CupertinoTheme.of(context).textTheme.navActionTextStyle, + backgroundColor: CupertinoDynamicColor.resolve(backgroundColor, context), + backButtonTextStyle: CupertinoTheme.of(context).textTheme.navActionTextStyle, titleTextStyle: CupertinoTheme.of(context).textTheme.navTitleTextStyle, - largeTitleTextStyle: - CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle, + largeTitleTextStyle: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle, border: border, hasUserMiddle: userMiddle != null, largeExpanded: showLargeTitle, @@ -994,8 +972,7 @@ class _PersistentNavigationBar extends StatelessWidget { double scaleFactor = MediaQuery.of(context).textScaleFactor; return SizedBox( - height: (_kNavBarPersistentHeight * scaleFactor) + - MediaQuery.of(context).padding.top, + height: (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.top, child: SafeArea( bottom: false, child: paddedToolbar, @@ -1354,11 +1331,10 @@ class IsekaiNavigationBarBackButton extends StatelessWidget { ); } - TextStyle actionTextStyle = - CupertinoTheme.of(context).textTheme.navActionTextStyle; + TextStyle actionTextStyle = CupertinoTheme.of(context).textTheme.navActionTextStyle; if (color != null) { - actionTextStyle = actionTextStyle.copyWith( - color: CupertinoDynamicColor.maybeResolve(color, context)); + actionTextStyle = + actionTextStyle.copyWith(color: CupertinoDynamicColor.maybeResolve(color, context)); } return CupertinoButton( @@ -1371,8 +1347,7 @@ class IsekaiNavigationBarBackButton extends StatelessWidget { child: DefaultTextStyle( style: actionTextStyle, child: ConstrainedBox( - constraints: - const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth), + constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -1457,8 +1432,7 @@ class _BackLabel extends StatelessWidget { // `child` is never passed in into ValueListenableBuilder so it's always // null here and unused. - Widget _buildPreviousTitleWidget( - BuildContext context, String? previousTitle, Widget? child) { + Widget _buildPreviousTitleWidget(BuildContext context, String? previousTitle, Widget? child) { previousTitle ??= "返回"; Text textWidget = Text( @@ -1482,8 +1456,7 @@ class _BackLabel extends StatelessWidget { Widget build(BuildContext context) { if (specifiedPreviousTitle != null) { return _buildPreviousTitleWidget(context, specifiedPreviousTitle, null); - } else if (route is CupertinoRouteTransitionMixin && - !route!.isFirst) { + } else if (route is CupertinoRouteTransitionMixin && !route!.isFirst) { final CupertinoRouteTransitionMixin cupertinoRoute = route! as CupertinoRouteTransitionMixin; // There is no timing issue because the previousTitle Listenable changes @@ -1534,8 +1507,8 @@ class _TransitionableNavigationBar extends StatelessWidget { final Widget child; RenderBox get renderBox { - final RenderBox box = componentsKeys.navBarBoxKey.currentContext! - .findRenderObject()! as RenderBox; + final RenderBox box = + componentsKeys.navBarBoxKey.currentContext!.findRenderObject()! as RenderBox; assert( box.attached, '_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. - if (componentsTransition.bottomBackChevron != null) - componentsTransition.bottomBackChevron!, - if (componentsTransition.bottomBackLabel != null) - componentsTransition.bottomBackLabel!, - if (componentsTransition.bottomLeading != null) - componentsTransition.bottomLeading!, - if (componentsTransition.bottomMiddle != null) - componentsTransition.bottomMiddle!, - if (componentsTransition.bottomLargeTitle != null) - componentsTransition.bottomLargeTitle!, - if (componentsTransition.bottomTrailing != null) - componentsTransition.bottomTrailing!, + if (componentsTransition.bottomBackChevron != null) componentsTransition.bottomBackChevron!, + if (componentsTransition.bottomBackLabel != null) componentsTransition.bottomBackLabel!, + if (componentsTransition.bottomLeading != null) componentsTransition.bottomLeading!, + 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. - if (componentsTransition.topLeading != null) - componentsTransition.topLeading!, - if (componentsTransition.topBackChevron != null) - componentsTransition.topBackChevron!, - if (componentsTransition.topBackLabel != null) - componentsTransition.topBackLabel!, - if (componentsTransition.topMiddle != null) - componentsTransition.topMiddle!, - if (componentsTransition.topLargeTitle != null) - componentsTransition.topLargeTitle!, - if (componentsTransition.topTrailing != null) - componentsTransition.topTrailing!, + if (componentsTransition.topLeading != null) componentsTransition.topLeading!, + if (componentsTransition.topBackChevron != null) componentsTransition.topBackChevron!, + if (componentsTransition.topBackLabel != null) componentsTransition.topBackLabel!, + 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 @@ -1677,8 +1638,7 @@ class _NavigationBarTransition extends StatelessWidget { // can actually be outside the linearly lerp'ed Rect in the middle of // the animation, such as the topLargeTitle. return SizedBox( - height: math.max(heightTween.begin!, heightTween.end!) + - MediaQuery.of(context).padding.top, + height: math.max(heightTween.begin!, heightTween.end!) + MediaQuery.of(context).padding.top, width: double.infinity, child: Stack( children: children, @@ -1731,8 +1691,7 @@ class _NavigationBarComponentsTransition { topLargeExpanded = topNavBar.largeExpanded, transitionBox = // paintBounds are based on offset zero so it's ok to expand the Rects. - bottomNavBar.renderBox.paintBounds - .expandToInclude(topNavBar.renderBox.paintBounds), + bottomNavBar.renderBox.paintBounds.expandToInclude(topNavBar.renderBox.paintBounds), forwardDirection = directionality == TextDirection.ltr ? 1.0 : -1.0; static final Animatable fadeOut = Tween( @@ -1779,13 +1738,11 @@ class _NavigationBarComponentsTransition { GlobalKey key, { required RenderBox from, }) { - final RenderBox componentBox = - key.currentContext!.findRenderObject()! as RenderBox; + final RenderBox componentBox = key.currentContext!.findRenderObject()! as RenderBox; assert(componentBox.attached); return RelativeRect.fromRect( - componentBox.localToGlobal(Offset.zero, ancestor: from) & - componentBox.size, + componentBox.localToGlobal(Offset.zero, ancestor: from) & componentBox.size, transitionBox, ); } @@ -1810,10 +1767,8 @@ class _NavigationBarComponentsTransition { required RenderBox toNavBarBox, required Widget child, }) { - final RenderBox fromBox = - fromKey.currentContext!.findRenderObject()! as RenderBox; - final RenderBox toBox = - toKey.currentContext!.findRenderObject()! as RenderBox; + final RenderBox fromBox = fromKey.currentContext!.findRenderObject()! as RenderBox; + final RenderBox toBox = toKey.currentContext!.findRenderObject()! as RenderBox; final bool isLTR = forwardDirection > 0; @@ -1829,8 +1784,7 @@ class _NavigationBarComponentsTransition { ); final Offset fromAnchorInFromBox = fromBox.localToGlobal(fromAnchorLocal, ancestor: fromNavBarBox); - final Offset toAnchorInToBox = - toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox); + final Offset toAnchorInToBox = toBox.localToGlobal(toAnchorLocal, ancestor: toNavBarBox); // 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 @@ -1842,13 +1796,10 @@ class _NavigationBarComponentsTransition { // coordinates. final Offset translation = isLTR ? toAnchorInToBox - fromAnchorInFromBox - : Offset(toNavBarBox.size.width - toAnchorInToBox.dx, - toAnchorInToBox.dy) - - Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx, - fromAnchorInFromBox.dy); + : Offset(toNavBarBox.size.width - toAnchorInToBox.dx, toAnchorInToBox.dy) - + Offset(fromNavBarBox.size.width - fromAnchorInFromBox.dx, fromAnchorInFromBox.dy); - final RelativeRect fromBoxMargin = - positionInTransitionBox(fromKey, from: fromNavBarBox); + final RelativeRect fromBoxMargin = positionInTransitionBox(fromKey, from: fromNavBarBox); final Offset fromOriginInTransitionBox = Offset( isLTR ? fromBoxMargin.left : fromBoxMargin.right, fromBoxMargin.top, @@ -1880,16 +1831,14 @@ class _NavigationBarComponentsTransition { } Widget? get bottomLeading { - final KeyedSubtree? bottomLeading = - bottomComponents.leadingKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? bottomLeading = bottomComponents.leadingKey.currentWidget as KeyedSubtree?; if (bottomLeading == null) { return null; } return Positioned.fromRelativeRect( - rect: positionInTransitionBox(bottomComponents.leadingKey, - from: bottomNavBarBox), + rect: positionInTransitionBox(bottomComponents.leadingKey, from: bottomNavBarBox), child: FadeTransition( opacity: fadeOutBy(0.4), child: bottomLeading.child, @@ -1906,8 +1855,7 @@ class _NavigationBarComponentsTransition { } return Positioned.fromRelativeRect( - rect: positionInTransitionBox(bottomComponents.backChevronKey, - from: bottomNavBarBox), + rect: positionInTransitionBox(bottomComponents.backChevronKey, from: bottomNavBarBox), child: FadeTransition( opacity: fadeOutBy(0.6), child: DefaultTextStyle( @@ -1926,9 +1874,8 @@ class _NavigationBarComponentsTransition { return null; } - final RelativeRect from = positionInTransitionBox( - bottomComponents.backLabelKey, - from: bottomNavBarBox); + final RelativeRect from = + positionInTransitionBox(bottomComponents.backLabelKey, from: bottomNavBarBox); // Transition away by sliding horizontally to the leading edge off of the screen. final RelativeRectTween positionTween = RelativeRectTween( @@ -1954,12 +1901,9 @@ class _NavigationBarComponentsTransition { } Widget? get bottomMiddle { - final KeyedSubtree? bottomMiddle = - bottomComponents.middleKey.currentWidget as KeyedSubtree?; - final KeyedSubtree? topBackLabel = - topComponents.backLabelKey.currentWidget as KeyedSubtree?; - final KeyedSubtree? topLeading = - topComponents.leadingKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topBackLabel = 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 // nav bar but would be invisible when expanded, therefore don't show it here. @@ -1998,8 +1942,7 @@ class _NavigationBarComponentsTransition { // fade. if (bottomMiddle != null && topLeading != null) { return Positioned.fromRelativeRect( - rect: positionInTransitionBox(bottomComponents.middleKey, - from: bottomNavBarBox), + rect: positionInTransitionBox(bottomComponents.middleKey, from: bottomNavBarBox), child: FadeTransition( opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7), // Keep the font when transitioning into a non-back label leading. @@ -2017,10 +1960,8 @@ class _NavigationBarComponentsTransition { Widget? get bottomLargeTitle { final KeyedSubtree? bottomLargeTitle = bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?; - final KeyedSubtree? topBackLabel = - topComponents.backLabelKey.currentWidget as KeyedSubtree?; - final KeyedSubtree? topLeading = - topComponents.leadingKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?; if (bottomLargeTitle == null || !bottomLargeExpanded) { return null; @@ -2056,9 +1997,8 @@ class _NavigationBarComponentsTransition { if (bottomLargeTitle != null && topLeading != null) { // Unlike bottom middle, the bottom large title moves when it can't // transition to the top back label position. - final RelativeRect from = positionInTransitionBox( - bottomComponents.largeTitleKey, - from: bottomNavBarBox); + final RelativeRect from = + positionInTransitionBox(bottomComponents.largeTitleKey, from: bottomNavBarBox); final RelativeRectTween positionTween = RelativeRectTween( begin: from, @@ -2097,8 +2037,7 @@ class _NavigationBarComponentsTransition { } return Positioned.fromRelativeRect( - rect: positionInTransitionBox(bottomComponents.trailingKey, - from: bottomNavBarBox), + rect: positionInTransitionBox(bottomComponents.trailingKey, from: bottomNavBarBox), child: FadeTransition( opacity: fadeOutBy(0.6), child: bottomTrailing.child, @@ -2107,16 +2046,14 @@ class _NavigationBarComponentsTransition { } Widget? get topLeading { - final KeyedSubtree? topLeading = - topComponents.leadingKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree?; if (topLeading == null) { return null; } return Positioned.fromRelativeRect( - rect: - positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox), + rect: positionInTransitionBox(topComponents.leadingKey, from: topNavBarBox), child: FadeTransition( opacity: fadeInFrom(0.6), child: topLeading.child, @@ -2134,17 +2071,15 @@ class _NavigationBarComponentsTransition { return null; } - final RelativeRect to = positionInTransitionBox( - topComponents.backChevronKey, - from: topNavBarBox); + final RelativeRect to = + positionInTransitionBox(topComponents.backChevronKey, from: topNavBarBox); RelativeRect from = to; // If it's the first page with a back chevron, shift in slightly from the // right. if (bottomBackChevron == null) { final RenderBox topBackChevronBox = - topComponents.backChevronKey.currentContext!.findRenderObject()! - as RenderBox; + topComponents.backChevronKey.currentContext!.findRenderObject()! as RenderBox; from = to.shift( Offset( forwardDirection * topBackChevronBox.size.width * 2.0, @@ -2171,24 +2106,20 @@ class _NavigationBarComponentsTransition { } Widget? get topBackLabel { - final KeyedSubtree? bottomMiddle = - bottomComponents.middleKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? bottomMiddle = bottomComponents.middleKey.currentWidget as KeyedSubtree?; final KeyedSubtree? bottomLargeTitle = bottomComponents.largeTitleKey.currentWidget as KeyedSubtree?; - final KeyedSubtree? topBackLabel = - topComponents.backLabelKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topBackLabel = topComponents.backLabelKey.currentWidget as KeyedSubtree?; if (topBackLabel == null) { return null; } - final RenderAnimatedOpacity? topBackLabelOpacity = topComponents - .backLabelKey.currentContext + final RenderAnimatedOpacity? topBackLabelOpacity = topComponents.backLabelKey.currentContext ?.findAncestorRenderObjectOfType(); Animation? midClickOpacity; - if (topBackLabelOpacity != null && - topBackLabelOpacity.opacity.value < 1.0) { + if (topBackLabelOpacity != null && topBackLabelOpacity.opacity.value < 1.0) { midClickOpacity = animation.drive(Tween( begin: 0.0, end: topBackLabelOpacity.opacity.value, @@ -2200,9 +2131,7 @@ class _NavigationBarComponentsTransition { // content text might be different. For instance, if the bottomLargeTitle // text is too long, the topBackLabel will say 'Back' instead of the original // text. - if (bottomLargeTitle != null && - topBackLabel != null && - bottomLargeExpanded) { + if (bottomLargeTitle != null && topBackLabel != null && bottomLargeExpanded) { return slideFromLeadingEdge( fromKey: bottomComponents.largeTitleKey, fromNavBarBox: bottomNavBarBox, @@ -2248,8 +2177,7 @@ class _NavigationBarComponentsTransition { } Widget? get topMiddle { - final KeyedSubtree? topMiddle = - topComponents.middleKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topMiddle = topComponents.middleKey.currentWidget as KeyedSubtree?; if (topMiddle == null) { return null; @@ -2261,10 +2189,9 @@ class _NavigationBarComponentsTransition { return null; } - final RelativeRect to = - positionInTransitionBox(topComponents.middleKey, from: topNavBarBox); - final RenderBox toBox = topComponents.middleKey.currentContext! - .findRenderObject()! as RenderBox; + final RelativeRect to = positionInTransitionBox(topComponents.middleKey, from: topNavBarBox); + final RenderBox toBox = + topComponents.middleKey.currentContext!.findRenderObject()! as RenderBox; final bool isLTR = forwardDirection > 0; @@ -2301,16 +2228,14 @@ class _NavigationBarComponentsTransition { } Widget? get topTrailing { - final KeyedSubtree? topTrailing = - topComponents.trailingKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topTrailing = topComponents.trailingKey.currentWidget as KeyedSubtree?; if (topTrailing == null) { return null; } return Positioned.fromRelativeRect( - rect: positionInTransitionBox(topComponents.trailingKey, - from: topNavBarBox), + rect: positionInTransitionBox(topComponents.trailingKey, from: topNavBarBox), child: FadeTransition( opacity: fadeInFrom(0.4), child: topTrailing.child, @@ -2319,15 +2244,14 @@ class _NavigationBarComponentsTransition { } Widget? get topLargeTitle { - final KeyedSubtree? topLargeTitle = - topComponents.largeTitleKey.currentWidget as KeyedSubtree?; + final KeyedSubtree? topLargeTitle = topComponents.largeTitleKey.currentWidget as KeyedSubtree?; if (topLargeTitle == null || !topLargeExpanded) { return null; } - final RelativeRect to = positionInTransitionBox(topComponents.largeTitleKey, - from: topNavBarBox); + final RelativeRect to = + positionInTransitionBox(topComponents.largeTitleKey, from: topNavBarBox); // Shift in from the trailing edge of the screen. final RelativeRectTween positionTween = RelativeRectTween( @@ -2417,8 +2341,7 @@ Widget _navBarHeroFlightShuttleBuilder( final _TransitionableNavigationBar fromNavBar = fromHeroWidget.child as _TransitionableNavigationBar; - final _TransitionableNavigationBar toNavBar = - toHeroWidget.child as _TransitionableNavigationBar; + final _TransitionableNavigationBar toNavBar = toHeroWidget.child as _TransitionableNavigationBar; assert(fromNavBar.componentsKeys != null); assert(toNavBar.componentsKeys != null); diff --git a/lib/components/page_card.dart b/lib/components/page_card.dart index 04e2d7e..ff1bc74 100755 --- a/lib/components/page_card.dart +++ b/lib/components/page_card.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.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:skeletons/skeletons.dart'; -import '../styles.dart'; - typedef AddFavoriteCallback = Future Function(PageInfo pageInfo, bool localIsFavorite); typedef PageInfoCallback = Future Function(PageInfo pageInfo); class PageCardStyles { - static const double cardInnerHeight = 150; - static const cardInnerPadding = EdgeInsets.only(top: 16, left: 20, right: 20, bottom: 12); + static const cardContainerHeight = 100.0; + 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 footerButtonInnerSize = 26; } @@ -106,24 +137,42 @@ class PageCard extends StatelessWidget { var textScale = MediaQuery.of(context).textScaleFactor; return Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Skeleton( - isLoading: isLoading, - skeleton: SkeletonLine( - style: SkeletonLineStyle( - height: (Styles.pageCardTitle.fontSize! + 4) * textScale, randomLength: true), - ), - child: Text(pageInfo?.mainTitle ?? "页面信息丢失", style: Styles.pageCardTitle), + padding: pageInfo?.subtitle == null + ? PageCardStyles.cardHeaderPadding + : PageCardStyles.cardHeaderCompactPadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (pageInfo?.subtitle != null) + 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) { - var textScale = MediaQuery.of(context).textScaleFactor; + double textScale = max(1, MediaQuery.of(context).textScaleFactor); return Expanded( child: Padding( - padding: const EdgeInsets.only(bottom: 8), + padding: PageCardStyles.cardContentPadding, child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -132,18 +181,26 @@ class PageCard extends StatelessWidget { flex: 1, child: isLoading ? ClipRect( + clipBehavior: Clip.antiAlias, child: SkeletonParagraph( style: SkeletonParagraphStyle( lines: 3, - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), + padding: EdgeInsets.symmetric( + vertical: PageCardStyles.contentFontSize * textScale * 0.2, + horizontal: 0, + ), lineStyle: SkeletonLineStyle( randomLength: true, - height: Styles.pageCardDescription.fontSize! * textScale), + height: PageCardStyles.contentFontSize * textScale), ), ), ) - : Text(pageInfo?.description ?? "没有简介", - overflow: TextOverflow.fade, style: Styles.pageCardDescription), + : Text( + pageInfo?.description ?? "没有简介", + overflow: TextOverflow.fade, + style: PageCardStyles.contentTextStyle, + textScaleFactor: textScale, + ), ), const SizedBox(width: 10), Skeleton( @@ -162,77 +219,83 @@ class PageCard extends StatelessWidget { Widget _buildCardFooter(BuildContext context) { return ScaleTapIgnore( - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // 分类信息 - pageInfo?.mainCategory != null - ? Chip( - backgroundColor: const Color.fromARGB(1, 238, 238, 238), - label: - Text(pageInfo!.mainCategory!, style: const TextStyle(color: Colors.black54)), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap) - : const SizedBox(), - pageInfo?.mainCategory != null ? const SizedBox(width: 10) : const SizedBox(), - // 发布日期 - Skeleton( - isLoading: isLoading, - skeleton: const SkeletonLine( - style: SkeletonLineStyle(width: 100), - ), - child: Text( + child: Padding( + padding: PageCardStyles.cardFooterPadding, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 分类信息 + pageInfo?.mainCategory != null + ? Chip( + backgroundColor: const Color.fromARGB(1, 238, 238, 238), + label: Text(pageInfo!.mainCategory!, + style: const TextStyle(color: Colors.black54)), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap) + : const SizedBox(), + pageInfo?.mainCategory != null ? const SizedBox(width: 10) : const SizedBox(), + // 发布日期 + Skeleton( + isLoading: isLoading, + skeleton: const SkeletonLine( + style: SkeletonLineStyle(width: 100), + ), + child: Text( pageInfo?.updatedTime != null ? Utils.getFriendDate(pageInfo!.updatedTime!) : "", - style: Styles.pageCardDescription), - ), - const Spacer(), - // 收藏键 - _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, - ); - }, + overflow: TextOverflow.fade, + style: PageCardStyles.contentTextStyle, + textScaleFactor: max(1, MediaQuery.of(context).textScaleFactor), + ), ), - ), - 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), + const Spacer(), + // 收藏键 + _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 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, - position: PullDownMenuPosition.under, - buttonBuilder: (context, showMenu) => IconButton( - onPressed: showMenu, - padding: const EdgeInsets.all(0.0), - splashRadius: PageCardStyles.footerButtonInnerSize - 4, - iconSize: PageCardStyles.footerButtonInnerSize, - icon: const Icon( - Icons.more_horiz, - color: Colors.grey, + itemBuilder: _buildMenuItem, + position: PullDownMenuPosition.under, + buttonBuilder: (context, showMenu) => IconButton( + onPressed: showMenu, + padding: const EdgeInsets.all(0.0), + splashRadius: PageCardStyles.footerButtonInnerSize - 4, + iconSize: PageCardStyles.footerButtonInnerSize, + icon: const Icon( + Icons.more_horiz, + color: Colors.grey, + ), ), ), ), ), - ), - ], + ], + ), ), ); } @@ -276,30 +339,33 @@ class PageCard extends StatelessWidget { Widget _buildCard(BuildContext context) { var textScale = MediaQuery.of(context).textScaleFactor; - return Card( - elevation: 4.0, - // 圆角 - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(Styles.isXs ? 0 : 14.0)), + return Container( + margin: Theme.of(context).cardTheme.margin, + clipBehavior: Theme.of(context).cardTheme.clipBehavior ?? Clip.antiAlias, + decoration: BoxDecoration( + 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), + ], ), - // 抗锯齿 - clipBehavior: Clip.antiAlias, - semanticContainer: false, - child: Padding( - padding: PageCardStyles.cardInnerPadding, - child: SizedBox( - height: PageCardStyles.cardInnerHeight * textScale, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 标题 - _buildCardHeader(context), - // 简介、图片 - _buildCardBody(context), - // Footer - _buildCardFooter(context), - ], - ), + child: SizedBox( + height: PageCardStyles.cardContainerHeight + + (PageCardStyles.cardContentHeight * max(1, MediaQuery.of(context).textScaleFactor)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 标题 + _buildCardHeader(context), + // 简介、图片 + _buildCardBody(context), + // Footer + _buildCardFooter(context), + ], ), ), ); diff --git a/lib/components/page_card.getx.dart b/lib/components/page_card.getx.dart index 706fdd4..e43cdef 100644 --- a/lib/components/page_card.getx.dart +++ b/lib/components/page_card.getx.dart @@ -11,7 +11,7 @@ import 'package:pull_down_button/pull_down_button.dart'; import 'package:skeletons/skeletons.dart'; import '../styles.dart'; - +/* typedef AddFavoriteCallback = Future Function( PageInfo pageInfo, bool localIsFavorite, bool showToast); @@ -144,9 +144,9 @@ class _PageCardState extends ReactiveState { isLoading: c.isLoading.value, skeleton: SkeletonLine( 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 { padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0), lineStyle: SkeletonLineStyle( randomLength: true, - height: Styles.pageCardDescription.fontSize! * textScale), + height: Styles.contentTextStyle.fontSize! * textScale), ), ), ) : Text(c.pageInfo.value?.description ?? "没有简介", - overflow: TextOverflow.fade, style: Styles.pageCardDescription), + overflow: TextOverflow.fade, style: Styles.contentTextStyle), ), const SizedBox(width: 10), Skeleton( @@ -215,7 +215,7 @@ class _PageCardState extends ReactiveState { c.pageInfo.value?.updatedTime != null ? Utils.getFriendDate(c.pageInfo.value!.updatedTime!) : "", - style: Styles.pageCardDescription), + style: Styles.contentTextStyle), ), const Spacer(), // 收藏键 @@ -353,3 +353,4 @@ class _PageCardState extends ReactiveState { ); } } +*/ \ No newline at end of file diff --git a/lib/components/recent_page_list.dart b/lib/components/recent_page_list.dart index 13ba8e7..a18cf98 100755 --- a/lib/components/recent_page_list.dart +++ b/lib/components/recent_page_list.dart @@ -134,6 +134,7 @@ class RecentPageList extends StatelessWidget { Widget _buildPageCard( int index, PageInfo pageInfo, RecentPageListController c, FavoriteListController flc) { return PageCard( + key: ValueKey("rpl-card-$index"), pageInfo: c.pageList[index], isFavorite: flc.isFavorite(c.pageList[index]), onSetFavorite: flc.setFavoriteImmediate, diff --git a/lib/models/site_config.dart b/lib/models/site_config.dart index 9b52a32..892a74c 100644 --- a/lib/models/site_config.dart +++ b/lib/models/site_config.dart @@ -23,6 +23,8 @@ class SiteConfig { String restfulApiUrl; String pageUrlTemplate; + bool enableFollowing; + SiteConfig({ this.moduleStyles = const [], this.moduleScripts = const [], @@ -33,6 +35,7 @@ class SiteConfig { this.resourceLoaderUrl = "", this.restfulApiUrl = "", this.pageUrlTemplate = "", + this.enableFollowing = false, }); factory SiteConfig.fromJson(Map json) => _$SiteConfigFromJson(json); diff --git a/lib/pages/home.dart b/lib/pages/home.dart index a933bc2..36f6b15 100755 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -142,8 +142,8 @@ class HomeTab extends StatelessWidget { backgroundColor: Styles.themeMainColor, brightness: Brightness.dark, largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)), - border: Border.all(style: BorderStyle.none), trailing: _buildNotificationIconButton(), + border: Border.all(style: BorderStyle.none), ), SliverPersistentHeader( delegate: _SliverAppBarDelegate( @@ -184,39 +184,40 @@ class HomeTab extends StatelessWidget { ), ), ), - SliverPersistentHeader( - pinned: true, - delegate: _SliverAppBarDelegate( - minHeight: 40.0, - maxHeight: 40.0, - child: Container( - decoration: BoxDecoration(color: Colors.white, boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.2), - spreadRadius: 2, - blurRadius: 4, - offset: const Offset(0, 2), - ) - ]), - child: Row( - children: [ - SizedBox( - child: TabBar( - isScrollable: true, - controller: c.tabController, - indicatorColor: Styles.themeMainColor, - labelColor: Styles.themeMainColor, - unselectedLabelColor: Colors.black45, - tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')], - onTap: (int selected) {}, + if (Global.siteConfig.enableFollowing) // 仅在站点开启关注功能时显示 + SliverPersistentHeader( + pinned: true, + delegate: _SliverAppBarDelegate( + minHeight: 40.0, + maxHeight: 40.0, + child: Container( + decoration: BoxDecoration(color: Colors.white, boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.2), + spreadRadius: 2, + blurRadius: 4, + offset: const Offset(0, 2), + ) + ]), + child: Row( + children: [ + SizedBox( + child: TabBar( + isScrollable: true, + controller: c.tabController, + indicatorColor: Styles.themeMainColor, + labelColor: Styles.themeMainColor, + unselectedLabelColor: Colors.black45, + tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')], + onTap: (int selected) {}, + ), ), - ), - const Expanded(child: Text('')), - ], + const Expanded(child: Text('')), + ], + ), ), ), ), - ), CupertinoSliverRefreshControl( onRefresh: c.handleRefresh, ), diff --git a/lib/styles.dart b/lib/styles.dart index 408a256..693cec8 100755 --- a/lib/styles.dart +++ b/lib/styles.dart @@ -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( fontWeight: FontWeight.normal, fontSize: 32, color: CupertinoColors.label, inherit: false); @@ -40,25 +64,12 @@ abstract class Styles { fontWeight: FontWeight.bold, ); - static const TextStyle pageCardTitle = TextStyle( - fontSize: 20, - fontStyle: FontStyle.normal, - fontWeight: FontWeight.bold, - ); - static const TextStyle articleTitle = TextStyle( fontSize: 28, fontStyle: FontStyle.normal, 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 listTileSubTitle = TextStyle(fontSize: 16); @@ -70,6 +81,7 @@ abstract class Styles { static const Color themeNavTitleColor = 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 darkThemePageBackgroundColor = Color.fromRGBO(0, 0, 0, 1); static const Color themeNavSegmentTextColor = Color.fromRGBO(12, 12, 12, 1); static const Color panelBackgroundColor = Color.fromRGBO(255, 255, 255, 1); static const Color linkColor = CupertinoColors.link;