From d2430d760245e5031355e7cdf7b3e30e161e2539 Mon Sep 17 00:00:00 2001 From: Lex Lim Date: Thu, 19 Jan 2023 17:30:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BA=95=E9=83=A8=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E6=A0=8F=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/components/bottom_nav_bar.dart | 128 +++++++++++++++++++++++++++++ lib/pages/article.dart | 2 + 2 files changed, 130 insertions(+) create mode 100644 lib/components/bottom_nav_bar.dart diff --git a/lib/components/bottom_nav_bar.dart b/lib/components/bottom_nav_bar.dart new file mode 100644 index 0000000..fe8b4f7 --- /dev/null +++ b/lib/components/bottom_nav_bar.dart @@ -0,0 +1,128 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:get/get.dart'; + +/// Standard iOS navigation bar height without the status bar. +/// +/// This height is constant and independent of accessibility as it is in iOS. +const double _kNavBarPersistentHeight = kMinInteractiveDimensionCupertino; + +const Color _kDefaultNavBarBorderColor = Color(0x4D000000); + +const Border _kDefaultNavBarBorder = Border( + top: BorderSide( + color: _kDefaultNavBarBorderColor, + width: 0.0, // 0.0 means one physical pixel + ), +); + +/// Returns `child` wrapped with background and a bottom border if background color +/// is opaque. Otherwise, also blur with [BackdropFilter]. +/// +/// When `updateSystemUiOverlay` is true, the nav bar will update the OS +/// status bar's color theme based on the background color of the nav bar. +Widget _wrapWithBackground({ + Border? border, + required Color backgroundColor, + Brightness? brightness, + required Widget child, + bool updateSystemUiOverlay = true, +}) { + Widget result = child; + if (updateSystemUiOverlay) { + final bool isDark = backgroundColor.computeLuminance() < 0.179; + final Brightness newBrightness = brightness ?? (isDark ? Brightness.dark : Brightness.light); + final SystemUiOverlayStyle overlayStyle; + switch (newBrightness) { + case Brightness.dark: + overlayStyle = SystemUiOverlayStyle.light; + break; + case Brightness.light: + overlayStyle = SystemUiOverlayStyle.dark; + break; + } + result = AnnotatedRegion( + value: overlayStyle, + child: result, + ); + } + final DecoratedBox childWithBackground = DecoratedBox( + decoration: BoxDecoration( + border: border, + color: backgroundColor, + ), + child: result, + ); + + if (backgroundColor.alpha == 0xFF) { + return childWithBackground; + } + + return ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: childWithBackground, + ), + ); +} + +class BottomNavigationBar extends StatefulWidget implements ObstructingPreferredSizeWidget { + final Widget child; + final Color? backgroundColor; + final Brightness? brightness; + final Border? border; + + const BottomNavigationBar({ + super.key, + required this.child, + this.backgroundColor, + this.brightness, + this.border = _kDefaultNavBarBorder, + }); + + @override + bool shouldFullyObstruct(BuildContext context) { + final Color backgroundColor = + CupertinoDynamicColor.maybeResolve(this.backgroundColor, context) ?? + CupertinoTheme.of(context).barBackgroundColor; + return backgroundColor.alpha == 0xFF; + } + + @override + Size get preferredSize { + double scaleFactor = MediaQuery.of(Get.context!).textScaleFactor; + return Size.fromHeight(_kNavBarPersistentHeight * scaleFactor); + } + + @override + State createState() => _BottomNavigationBarState(); +} + +class _BottomNavigationBarState extends State { + @override + Widget build(BuildContext context) { + final Color backgroundColor = + CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? + CupertinoTheme.of(context).barBackgroundColor; + + final scaleFactor = MediaQuery.of(context).textScaleFactor; + + return _wrapWithBackground( + border: widget.border, + backgroundColor: backgroundColor, + brightness: widget.brightness, + child: DefaultTextStyle( + style: CupertinoTheme.of(context).textTheme.textStyle, + child: SizedBox( + height: (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.bottom, + child: SafeArea( + top: false, + child: widget.child, + ), + ), + ), + ); + } +} diff --git a/lib/pages/article.dart b/lib/pages/article.dart index 8030c6a..edb2fa9 100755 --- a/lib/pages/article.dart +++ b/lib/pages/article.dart @@ -6,6 +6,7 @@ import 'package:isekai_wiki/api/mw/mw_api.dart'; import 'package:isekai_wiki/api/mw/parse.dart'; import 'package:isekai_wiki/api/response/page_info.dart'; import 'package:isekai_wiki/api/response/parse.dart'; +import 'package:isekai_wiki/components/bottom_nav_bar.dart'; import 'package:isekai_wiki/components/safearea_builder.dart'; import 'package:isekai_wiki/components/wikipage_parser.dart'; import 'package:isekai_wiki/reactive/reactive.dart'; @@ -114,6 +115,7 @@ class _ArticlePageState extends ReactiveState { navigationBar: IsekaiNavigationBar( middle: Obx(() => Text(c.displayTitle.value)), ), + bottomNavigationBar: BottomNavigationBar(child: SizedBox()), child: SafeAreaBuilder( builder: (context, padding) => Obx( () => WikiPageParser(