From 3ec076e176f256773680dace9cf8e88cc53c246b Mon Sep 17 00:00:00 2001 From: Lex Lim Date: Sat, 28 Jan 2023 08:48:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0hover=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/app_bridge.js | 87 ++++++ ios/Podfile.lock | 10 + lib/app.dart | 58 +++- lib/components/bottom_nav_bar.dart | 13 +- lib/components/gesture_detector.dart | 90 +++++- lib/components/isekai_nav_bar.dart | 257 ++++++++++------ lib/components/isekai_page_scaffold.dart | 22 +- lib/components/nav_bar_button.dart | 26 ++ lib/components/toc.dart | 29 ++ lib/components/wikipage_parser.dart | 126 +++++++- lib/main.dart | 19 -- lib/pages/article.dart | 116 ++++++- lib/pages/home.dart | 63 +++- lib/pages/tab_page.dart | 1 - lib/pages/welcome_page.dart | 99 +++--- lib/styles.dart | 11 +- packages/flutter_slider_drawer | 1 + pubspec.lock | 369 +++++++++++------------ pubspec.yaml | 7 +- 19 files changed, 988 insertions(+), 416 deletions(-) create mode 100644 assets/js/app_bridge.js create mode 100644 lib/components/nav_bar_button.dart create mode 100644 lib/components/toc.dart create mode 160000 packages/flutter_slider_drawer diff --git a/assets/js/app_bridge.js b/assets/js/app_bridge.js new file mode 100644 index 0000000..bc76a41 --- /dev/null +++ b/assets/js/app_bridge.js @@ -0,0 +1,87 @@ +var _InAppWebViewReady = false; + +var titleList = []; +var currentTitle = '_top'; + +function onReadyStateChange() { + if (_InAppWebViewReady && document.readyState === "complete") { + titleList = document.querySelectorAll('.mw-parser-output h1, .mw-parser-output h2, .mw-parser-output h3, .mw-parser-output h4, .mw-parser-output h5, .mw-parser-output h6'); + window.flutter_inappwebview.callHandler('pageLoaded', true); + } +} +window.addEventListener("flutterInAppWebViewPlatformReady", function(event) { + _InAppWebViewReady = true; + window.flutter_inappwebview.callHandler('bridgeConnected', true); + onReadyStateChange(); +}); + +document.addEventListener("readystatechange", onReadyStateChange); + +var _debouceTimer = null; +function debouce(callback, ms) { + return function() { + var _args = arguments; + if (!_debouceTimer) { + _debouceTimer = setTimeout(function() { + callback.apply(this, _args); + _debouceTimer = null; + }, ms); + } + } +} + +document.addEventListener('scroll', debouce(function() { + if (!titleList || titleList.length === 0) return; + + var offsetTop = (navigator.safeArea ? navigator.safeArea.top : 0) + 10; + + var currentTitleEl = null; + var isFirstSection = false; + for (var i = 0; i < titleList.length; i ++) { + var el = titleList[i]; + var pos = el.getBoundingClientRect(); + if (pos.top - offsetTop > 0) { + if (i === 0) { + isFirstSection = true; + } else { + currentTitleEl = titleList[i - 1]; + } + break; + } + } + if (!currentTitleEl && !isFirstSection) { + currentTitleEl = titleList[titleList.length - 1]; + } + + var newCurrentTitle; + if (isFirstSection) { + newCurrentTitle = '_firstSection'; + } else { + var currentTitleAnchorEl = currentTitleEl.querySelector('.mw-headline'); + if (currentTitleAnchorEl) { + newCurrentTitle = currentTitleAnchorEl.id; + } + } + + if (newCurrentTitle && newCurrentTitle !== currentTitle) { + currentTitle = newCurrentTitle; + window.flutter_inappwebview.callHandler('sectionChange', newCurrentTitle); + } +}, 200), { passive: true }); + +var MugenApp = { + scrollToTitle: function (anchor) { + var el = document.getElementById(anchor); + if (el) { + var scrollTop = el.offsetTop; + if (navigator.safeArea) { + scrollTop += navigator.safeArea.top; + } + + window.scrollTo({ + top: scrollTop, + behavior: "smooth", + }); + } + }, +}; \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 43fe255..934d9cf 100755 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,9 @@ PODS: - OrderedSet (~> 5.0) - flutter_web_browser (0.17.1): - Flutter + - fluttertoast (0.0.2): + - Flutter + - Toast - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) @@ -20,6 +23,7 @@ PODS: - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) + - Toast (4.0.0) - url_launcher_ios (0.0.1): - Flutter - video_player_avfoundation (0.0.1): @@ -33,6 +37,7 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) - flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -45,6 +50,7 @@ SPEC REPOS: trunk: - FMDB - OrderedSet + - Toast EXTERNAL SOURCES: Flutter: @@ -53,6 +59,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_inappwebview/ios" flutter_web_browser: :path: ".symlinks/plugins/flutter_web_browser/ios" + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_ios: @@ -72,11 +80,13 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f + fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f diff --git a/lib/app.dart b/lib/app.dart index 402f916..970da85 100755 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'package:isekai_wiki/global.dart'; @@ -10,6 +11,47 @@ import 'models/model.dart'; import 'pages/tab_page.dart'; import 'styles.dart'; +class IsekaiWikiAppWrapper extends StatefulWidget { + final Widget child; + + const IsekaiWikiAppWrapper({super.key, required this.child}); + + @override + State createState() => _IsekaiWikiAppWrapperState(); +} + +class _IsekaiWikiAppWrapperState extends State { + @override + void initState() { + super.initState(); + + if (GetPlatform.isAndroid) { + // 仅允许竖屏 + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + + if (GetPlatform.isAndroid) { + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + statusBarBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.light, + systemStatusBarContrastEnforced: false, + systemNavigationBarColor: Colors.transparent.withAlpha(1), + systemNavigationBarContrastEnforced: false, + systemNavigationBarIconBrightness: Brightness.dark, + ), + ); + } + } + } + + @override + Widget build(BuildContext context) => widget.child; +} + class IsekaiWikiApp extends StatelessWidget { const IsekaiWikiApp({super.key}); @@ -49,12 +91,16 @@ class IsekaiWikiApp extends StatelessWidget { Styles.textScaleFactor = MediaQuery.of(context).textScaleFactor; Styles.isXs = MediaQuery.of(context).size.width <= 340; var brightness = MediaQuery.of(context).platformBrightness; - return Theme( - data: brightness != Brightness.dark - ? Styles.materialLightTheme - : Styles.materialDarkTheme, - child: CupertinoTheme( - data: Styles.cupertinoTheme.copyWith(brightness: brightness), child: child), + return IsekaiWikiAppWrapper( + child: Theme( + data: brightness != Brightness.dark + ? Styles.materialLightTheme + : Styles.materialDarkTheme, + child: CupertinoTheme( + data: + Styles.cupertinoTheme.copyWith(brightness: brightness), + child: child), + ), ); } }, diff --git a/lib/components/bottom_nav_bar.dart b/lib/components/bottom_nav_bar.dart index fe8b4f7..a5623a7 100644 --- a/lib/components/bottom_nav_bar.dart +++ b/lib/components/bottom_nav_bar.dart @@ -33,7 +33,8 @@ 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: @@ -43,10 +44,10 @@ Widget _wrapWithBackground({ overlayStyle = SystemUiOverlayStyle.dark; break; } - result = AnnotatedRegion( + /*result = AnnotatedRegion( value: overlayStyle, child: result, - ); + );*/ } final DecoratedBox childWithBackground = DecoratedBox( decoration: BoxDecoration( @@ -68,7 +69,8 @@ Widget _wrapWithBackground({ ); } -class BottomNavigationBar extends StatefulWidget implements ObstructingPreferredSizeWidget { +class BottomNavigationBar extends StatefulWidget + implements ObstructingPreferredSizeWidget { final Widget child; final Color? backgroundColor; final Brightness? brightness; @@ -116,7 +118,8 @@ class _BottomNavigationBarState extends State { child: DefaultTextStyle( style: CupertinoTheme.of(context).textTheme.textStyle, child: SizedBox( - height: (_kNavBarPersistentHeight * scaleFactor) + MediaQuery.of(context).padding.bottom, + height: (_kNavBarPersistentHeight * scaleFactor) + + MediaQuery.of(context).padding.bottom, child: SafeArea( top: false, child: widget.child, diff --git a/lib/components/gesture_detector.dart b/lib/components/gesture_detector.dart index 602931c..d9ec28e 100755 --- a/lib/components/gesture_detector.dart +++ b/lib/components/gesture_detector.dart @@ -1,3 +1,4 @@ +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import '../styles.dart'; @@ -22,11 +23,92 @@ class _OpacityGestureDetectorState extends State { @override Widget build(BuildContext context) { - return Text(widget.text, + return Text( + widget.text, style: const TextStyle( - color: Styles.themeNavTitleColor, - fontWeight: FontWeight.normal + color: Styles.themeNavTitleColor, fontWeight: FontWeight.normal), + ); + } +} + +enum PointerActiveMode { none, hover, active } + +class ClickableBuilder extends StatefulWidget { + final Widget Function( + BuildContext context, PointerActiveMode mode, Widget child) builder; + final Widget? child; + + const ClickableBuilder({super.key, required this.builder, this.child}); + + @override + State createState() => _ClickableBuilder(); +} + +class _ClickableBuilder extends State { + bool isHover = false; + bool isActive = false; + + bool isPointerDown = false; + bool isPersistActive = false; + + void onMouseEnter(PointerEnterEvent event) { + setState(() { + isHover = true; + }); + } + + void onMouseExit(PointerExitEvent event) { + setState(() { + isHover = false; + }); + } + + void onPointerDown(dynamic _) { + isPointerDown = true; + + setState(() { + isActive = true; + }); + Future.delayed(const Duration(milliseconds: 300)).then((value) { + if (isPointerDown) { + isPersistActive = true; + } else { + setState(() { + isActive = false; + }); + } + }); + } + + void onPointerUp(dynamic _) { + isPointerDown = false; + if (isPersistActive) { + isPersistActive = false; + setState(() { + isActive = false; + }); + } + } + + @override + Widget build(BuildContext context) { + var child = widget.child ?? const SizedBox(); + Widget innerItem; + + if (isActive) { + innerItem = widget.builder(context, PointerActiveMode.active, child); + } else if (isHover) { + innerItem = widget.builder(context, PointerActiveMode.hover, child); + } + innerItem = widget.builder(context, PointerActiveMode.none, child); + + return Listener( + onPointerDown: (event) {}, + child: MouseRegion( + onEnter: onMouseEnter, + onExit: onMouseExit, + child: innerItem, ), ); } -} \ No newline at end of file +} diff --git a/lib/components/isekai_nav_bar.dart b/lib/components/isekai_nav_bar.dart index f8e6ce8..171f5dc 100755 --- a/lib/components/isekai_nav_bar.dart +++ b/lib/components/isekai_nav_bar.dart @@ -52,7 +52,8 @@ 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) { @@ -129,7 +130,8 @@ 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: @@ -231,7 +233,8 @@ 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, @@ -444,7 +447,8 @@ 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, @@ -480,7 +484,9 @@ 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, @@ -488,8 +494,10 @@ 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, @@ -688,7 +696,8 @@ 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 @@ -705,7 +714,8 @@ 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, @@ -726,7 +736,8 @@ 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, @@ -734,17 +745,18 @@ 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, @@ -782,17 +794,20 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg @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 @@ -836,7 +851,9 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg 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!, @@ -864,7 +881,9 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg } return Hero( - tag: heroTag == _defaultHeroTag ? _HeroTag(Navigator.of(context)) : heroTag, + tag: heroTag == _defaultHeroTag + ? _HeroTag(Navigator.of(context)) + : heroTag, createRectTween: _linearTranslateWithLargestRectSizeTween, flightShuttleBuilder: _navBarHeroFlightShuttleBuilder, placeholderBuilder: _navBarHeroLaunchPadBuilder, @@ -874,10 +893,13 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg // 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, @@ -972,7 +994,8 @@ 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, @@ -1331,10 +1354,11 @@ 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( @@ -1342,12 +1366,13 @@ class IsekaiNavigationBarBackButton extends StatelessWidget { child: Semantics( container: true, excludeSemantics: true, - label: 'Back', + label: '返回', button: true, child: DefaultTextStyle( style: actionTextStyle, child: ConstrainedBox( - constraints: const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth), + constraints: + const BoxConstraints(minWidth: _kNavBarBackButtonTapWidth), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -1432,7 +1457,8 @@ 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( @@ -1442,7 +1468,7 @@ class _BackLabel extends StatelessWidget { ); if (previousTitle.length > 12) { - textWidget = const Text('Back'); + textWidget = const Text('返回'); } return Align( @@ -1456,7 +1482,8 @@ 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 @@ -1507,8 +1534,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 ' @@ -1618,19 +1645,31 @@ 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 @@ -1638,7 +1677,8 @@ 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, @@ -1691,7 +1731,8 @@ 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( @@ -1738,11 +1779,13 @@ 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, ); } @@ -1767,8 +1810,10 @@ 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; @@ -1784,7 +1829,8 @@ 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 @@ -1796,10 +1842,13 @@ 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, @@ -1831,14 +1880,16 @@ 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, @@ -1855,7 +1906,8 @@ 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( @@ -1874,8 +1926,9 @@ 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( @@ -1901,9 +1954,12 @@ 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. @@ -1942,7 +1998,8 @@ 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. @@ -1960,8 +2017,10 @@ 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; @@ -1997,8 +2056,9 @@ 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, @@ -2037,7 +2097,8 @@ 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, @@ -2046,14 +2107,16 @@ 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, @@ -2071,15 +2134,17 @@ 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, @@ -2106,20 +2171,24 @@ 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, @@ -2131,7 +2200,9 @@ 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, @@ -2177,7 +2248,8 @@ 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; @@ -2189,9 +2261,10 @@ 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; @@ -2228,14 +2301,16 @@ 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, @@ -2244,14 +2319,15 @@ 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( @@ -2341,7 +2417,8 @@ 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/isekai_page_scaffold.dart b/lib/components/isekai_page_scaffold.dart index 111c9ef..a308a7a 100755 --- a/lib/components/isekai_page_scaffold.dart +++ b/lib/components/isekai_page_scaffold.dart @@ -89,7 +89,8 @@ class IsekaiPageScaffold extends StatefulWidget { class _IsekaiPageScaffoldState extends State { void _handleStatusBarTap() { - final ScrollController? primaryScrollController = PrimaryScrollController.of(context); + final ScrollController? primaryScrollController = + PrimaryScrollController.of(context); // Only act on the scroll controller if it has any attached scroll positions. if (primaryScrollController != null && primaryScrollController.hasClients) { primaryScrollController.animateTo( @@ -111,7 +112,8 @@ class _IsekaiPageScaffoldState extends State { final bool topFullObstruction; if (widget.navigationBar != null) { // Propagate top padding and include viewInsets if appropriate - topPadding = widget.navigationBar!.preferredSize.height + existingMediaQuery.padding.top; + topPadding = widget.navigationBar!.preferredSize.height + + existingMediaQuery.padding.top; topFullObstruction = widget.navigationBar!.shouldFullyObstruct(context); } else { @@ -125,12 +127,11 @@ class _IsekaiPageScaffoldState extends State { final bool bottomFullObstruction; if (widget.bottomNavigationBar != null) { // Propagate bottom padding and include viewInsets if appropriate - final double bottomBarHeight = widget.bottomNavigationBar?.preferredSize.height ?? 0; - bottomPadding = widget.resizeToAvoidBottomInset - ? existingMediaQuery.padding.bottom + bottomBarHeight - : bottomBarHeight; + bottomPadding = widget.bottomNavigationBar!.preferredSize.height + + existingMediaQuery.padding.bottom; - bottomFullObstruction = widget.bottomNavigationBar!.shouldFullyObstruct(context); + bottomFullObstruction = + widget.bottomNavigationBar!.shouldFullyObstruct(context); } else { // Propagate bottom padding and include viewInsets if appropriate bottomPadding = existingMediaQuery.padding.bottom; @@ -154,7 +155,9 @@ class _IsekaiPageScaffoldState extends State { ), child: Padding( padding: EdgeInsets.only( - top: (widget.navigationBar == null) ? 0 : (topFullObstruction ? topPadding : 0), + top: (widget.navigationBar == null) + ? 0 + : (topFullObstruction ? topPadding : 0), bottom: (widget.bottomNavigationBar == null) ? 0 : (bottomFullObstruction ? bottomPadding : 0), @@ -165,7 +168,8 @@ class _IsekaiPageScaffoldState extends State { return DecoratedBox( decoration: BoxDecoration( - color: CupertinoDynamicColor.maybeResolve(widget.backgroundColor, context) ?? + color: CupertinoDynamicColor.maybeResolve( + widget.backgroundColor, context) ?? CupertinoTheme.of(context).scaffoldBackgroundColor, ), child: Stack( diff --git a/lib/components/nav_bar_button.dart b/lib/components/nav_bar_button.dart new file mode 100644 index 0000000..0db793e --- /dev/null +++ b/lib/components/nav_bar_button.dart @@ -0,0 +1,26 @@ +import 'package:flutter/cupertino.dart'; + +class NavBarButton extends StatelessWidget { + final IconData icon; + final VoidCallback? onPressed; + final String? semanticLabel; + + const NavBarButton( + {super.key, required this.icon, this.onPressed, this.semanticLabel}); + + @override + Widget build(BuildContext context) { + final theme = CupertinoTheme.of(context); + var color = theme.textTheme.navActionTextStyle.color ?? theme.primaryColor; + + return CupertinoButton( + padding: EdgeInsets.zero, + onPressed: onPressed, + child: Icon( + icon, + color: color, + size: 26, + ), + ); + } +} diff --git a/lib/components/toc.dart b/lib/components/toc.dart new file mode 100644 index 0000000..c43fb27 --- /dev/null +++ b/lib/components/toc.dart @@ -0,0 +1,29 @@ +import 'package:flutter/cupertino.dart'; +import 'package:isekai_wiki/components/gesture_detector.dart'; +import 'package:isekai_wiki/reactive/reactive.dart'; + +class TOCList extends StatefulWidget { + const TOCList({super.key}); + + @override + State createState() => _TOCListState(); +} + +class _TOCListState extends ReactiveState { + @override + Widget render(BuildContext context) { + return Container(); + } +} + +class TOCItem extends StatelessWidget { + final VoidCallback? onTap; + final bool active; + + const TOCItem({super.key, this.onTap, this.active = false}); + + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/components/wikipage_parser.dart b/lib/components/wikipage_parser.dart index a2b5179..ccd6cda 100644 --- a/lib/components/wikipage_parser.dart +++ b/lib/components/wikipage_parser.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_html/flutter_html.dart'; @@ -12,11 +13,14 @@ import 'package:isekai_wiki/models/settings.dart'; import 'package:isekai_wiki/models/user.dart'; import 'package:isekai_wiki/reactive/reactive.dart'; import 'package:isekai_wiki/utils/simpleTemplate.dart'; +import 'package:mime/mime.dart'; class WikiPageParserController extends GetxController { static SimpleTemplate? renderTemplate; + static String jsBridge = ""; InAppWebViewController? webviewCotroller; + ScrollController scrollController = ScrollController(); var pageInfo = Rx(null); var parseInfo = Rx(null); @@ -25,6 +29,7 @@ class WikiPageParserController extends GetxController { var textZoom = 100.obs; var loading = true.obs; + var pageHeight = 0.obs; @override void onInit() { @@ -47,6 +52,12 @@ class WikiPageParserController extends GetxController { }); } + Future initJsBridge() async { + if (jsBridge.isEmpty || kDebugMode) { + jsBridge = await rootBundle.loadString("assets/js/app_bridge.js"); + } + } + Future getRenderTemplate() async { if (renderTemplate == null) { String tplData; @@ -55,7 +66,7 @@ class WikiPageParserController extends GetxController { var uri = Uri.parse(Global.siteConfig.renderTemplateUrl); tplData = await BaseApi.get(uri); } else { - tplData = await DefaultAssetBundle.of(Get.context!).loadString("assets/tpl/wikiPage.html"); + tplData = await rootBundle.loadString("assets/tpl/wikiPage.html"); } renderTemplate = SimpleTemplate(tpl: tplData); } @@ -66,6 +77,7 @@ class WikiPageParserController extends GetxController { List tagList = []; var resUri = Uri.parse(Global.siteConfig.resourceLoaderUrl); + // CSS if (Global.siteConfig.moduleStyles.isNotEmpty) { var uri = resUri.replace(queryParameters: { "lang": pageInfo.value?.pagelanguage ?? "en", @@ -76,16 +88,23 @@ class WikiPageParserController extends GetxController { tagList.add(''); } - if (parseInfo.value?.modulestyles != null && parseInfo.value!.modulestyles.isNotEmpty) { + if (parseInfo.value?.modulestyles != null && + parseInfo.value!.modulestyles.isNotEmpty) { + var pageModules = parseInfo.value!.modulestyles + .where((item) => !Global.siteConfig.moduleStyles.contains(item)); + var uri = resUri.replace(queryParameters: { "lang": pageInfo.value?.pagelanguage ?? "en", - "modules": parseInfo.value!.modulestyles.join("|"), + "modules": pageModules.join("|"), "only": "styles", "skin": Global.siteConfig.renderTheme, }); tagList.add(''); } + // JS Bridge + tagList.add(''); + return tagList.join(); } @@ -103,10 +122,17 @@ class WikiPageParserController extends GetxController { classList.add("skin-${Global.siteConfig.renderTheme}"); + if (GetPlatform.isAndroid) { + classList.add("mugenapp-android"); + } else if (GetPlatform.isIOS) { + classList.add("mugenapp-ios"); + } + return classList.join(" "); } void reloadHtml() async { + await initJsBridge(); var tpl = await getRenderTemplate(); var tplParams = { @@ -128,27 +154,83 @@ class WikiPageParserController extends GetxController { void onWebViewCreated(InAppWebViewController controller) { webviewCotroller = controller; + controller.addJavaScriptHandler( + handlerName: "bridgeConnected", + callback: (args) { + debugPrint("JSBridge: bridgeConnected"); + return {}; + }, + ); + + controller.addJavaScriptHandler( + handlerName: "pageLoaded", + callback: (args) { + loading.value = false; + debugPrint("JSBridge: pageLoaded"); + return {}; + }, + ); + + controller.addJavaScriptHandler( + handlerName: "sectionChange", + callback: (args) { + if (args.isEmpty && args[0] is! String) { + return; + } + var anchor = args[0] as String; + + debugPrint("sectionChange: $anchor"); + }, + ); + if (contentHtml.value.isNotEmpty) { reloadHtml(); } } void onPageCommitVisible(InAppWebViewController controller, Uri? uri) { - controller.injectCSSCode(source: """ -:root { - --safe-area-top: ${safeAreaPadding.value.top}px; - --safe-area-bottom: ${safeAreaPadding.value.bottom}px; - --safe-area-left: ${safeAreaPadding.value.left}px; - --safe-area-right: ${safeAreaPadding.value.right}px; -} + controller.evaluateJavascript(source: """ +document.documentElement.style.setProperty('--safe-area-top', '${safeAreaPadding.value.top}px'); +document.documentElement.style.setProperty('--safe-area-right', '${safeAreaPadding.value.right}px'); +document.documentElement.style.setProperty('--safe-area-bottom', '${safeAreaPadding.value.bottom}px'); +document.documentElement.style.setProperty('--safe-area-left', '${safeAreaPadding.value.left}px'); +navigator.safeArea = { + top: ${safeAreaPadding.value.top}, + right: ${safeAreaPadding.value.right}, + bottom: ${safeAreaPadding.value.bottom}, + left: ${safeAreaPadding.value.left} +}; """); if (contentHtml.value.isNotEmpty) { - Future.delayed(const Duration(milliseconds: 100)).then((value) { + Future.delayed(const Duration(milliseconds: 1000)).then((value) { loading.value = false; }); } } + + Future onLoadResourceCustomScheme( + InAppWebViewController controller, Uri uri) async { + if (uri.scheme == "mugenappres") { + debugPrint("Webview load assets: \"$uri\""); + try { + var bytes = await rootBundle.load("assets/${uri.path}"); + var data = bytes.buffer.asUint8List(); + var mimeType = MimeTypeResolver() + .lookup(uri.path, headerBytes: data.sublist(0, 20)); + CustomSchemeResponse( + data: data, + contentType: mimeType ?? "text/plain", + contentEncoding: "utf-8", + ); + } catch (_) { + debugPrint("Try to load assets \"${uri.path}\" which is not exists."); + } + } + return null; + } + + void onScrollChanged(InAppWebViewController controller, int x, int y) {} } class WikiPageParser extends StatefulWidget { @@ -156,7 +238,8 @@ class WikiPageParser extends StatefulWidget { final MWParseInfo? parseInfo; final EdgeInsets? padding; - const WikiPageParser({super.key, this.pageInfo, this.parseInfo, this.padding}); + const WikiPageParser( + {super.key, this.pageInfo, this.parseInfo, this.padding}); @override State createState() { @@ -179,7 +262,8 @@ class _WikiParserState extends ReactiveState { c.parseInfo.value = widget.parseInfo; c.contentHtml.value = widget.parseInfo?.text ?? ""; c.safeAreaPadding.value = widget.padding ?? const EdgeInsets.all(0); - c.textZoom.value = (MediaQuery.of(Get.context!).textScaleFactor * 100).round(); + c.textZoom.value = + (MediaQuery.of(Get.context!).textScaleFactor * 100).round(); } Widget _buildRender() { @@ -212,9 +296,22 @@ class _WikiParserState extends ReactiveState { child: InAppWebView( onWebViewCreated: c.onWebViewCreated, onPageCommitVisible: c.onPageCommitVisible, + onLoadResourceCustomScheme: c.onLoadResourceCustomScheme, + onScrollChanged: c.onScrollChanged, initialOptions: InAppWebViewGroupOptions( + crossPlatform: InAppWebViewOptions( + disableHorizontalScroll: true, + supportZoom: true, + resourceCustomSchemes: ["mugenappres"], + ), android: AndroidInAppWebViewOptions( textZoom: c.textZoom.value, + forceDark: AndroidForceDark.FORCE_DARK_AUTO, + ), + ios: IOSInAppWebViewOptions( + allowsLinkPreview: false, + allowsBackForwardNavigationGestures: false, + alwaysBounceVertical: true, ), ), ), @@ -231,6 +328,7 @@ class _WikiParserState extends ReactiveState { @override Widget build(BuildContext context) { var sc = Get.find(); - return Obx(() => sc.betaPageRender.value ? _buildRender() : _buildWebview()); + return Obx( + () => sc.betaPageRender.value ? _buildRender() : _buildWebview()); } } diff --git a/lib/main.dart b/lib/main.dart index 119abcd..53056ac 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,25 +13,6 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'app.dart'; Future init() async { - // 仅允许竖屏 - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - DeviceOrientation.portraitDown, - ]); - SystemChrome.setSystemUIOverlayStyle( - const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - systemStatusBarContrastEnforced: false, - systemNavigationBarColor: Colors.transparent, - systemNavigationBarContrastEnforced: false, - systemNavigationBarIconBrightness: Brightness.dark, - ), - ); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom], - ); - if (kIsWeb) { // 设置web origin Global.webOrigin = Uri.base.origin; diff --git a/lib/pages/article.dart b/lib/pages/article.dart index edb2fa9..e028cd8 100755 --- a/lib/pages/article.dart +++ b/lib/pages/article.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_slider_drawer/flutter_slider_drawer.dart'; import 'package:get/get.dart'; import 'package:isekai_wiki/api/mw/list.dart'; import 'package:isekai_wiki/api/mw/mw_api.dart'; @@ -7,8 +8,10 @@ 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/nav_bar_button.dart'; import 'package:isekai_wiki/components/safearea_builder.dart'; import 'package:isekai_wiki/components/wikipage_parser.dart'; +import 'package:isekai_wiki/models/favorite_list.dart'; import 'package:isekai_wiki/reactive/reactive.dart'; import 'package:isekai_wiki/utils/dialog.dart'; import 'package:isekai_wiki/utils/error.dart'; @@ -22,7 +25,11 @@ class MinimumArticleData { final String? mainCategory; final DateTime? updateTime; - MinimumArticleData({required this.title, this.description, this.mainCategory, this.updateTime}); + MinimumArticleData( + {required this.title, + this.description, + this.mainCategory, + this.updateTime}); } class ArticleCategoryData { @@ -35,6 +42,8 @@ class ArticleCategoryData { class ArticlePageController extends GetxController { var loading = true.obs; + var menuSlider = GlobalKey(); + var pageTitle = "".obs; var pageId = 0.obs; @@ -50,9 +59,11 @@ class ArticlePageController extends GetxController { loading.value = true; MWResponse> pageInfoRes; if (pageId.value != 0) { - pageInfoRes = await MWApiList.getPageInfoList(pageids: [pageId.value]); + pageInfoRes = + await MWApiList.getPageInfoList(pageids: [pageId.value]); } else { - pageInfoRes = await MWApiList.getPageInfoList(titles: [pageTitle.value]); + pageInfoRes = + await MWApiList.getPageInfoList(titles: [pageTitle.value]); } if (pageInfoRes.data.isEmpty) { throw MWApiErrorException(code: 'no-page', info: "页面信息丢失"); @@ -67,7 +78,8 @@ class ArticlePageController extends GetxController { var parseRes = await MWApiParse.parse(pageId: pageId.value); parseInfo.value = parseRes.data; } catch (err, stack) { - alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), title: "错误"); + alert(Get.overlayContext!, ErrorUtils.getErrorMessage(err), + title: "错误"); if (kDebugMode) { print("Exception in page: $err"); stack.printError(); @@ -77,6 +89,14 @@ class ArticlePageController extends GetxController { } } } + + void handleFlowButtonClick() { + alert(Get.overlayContext!, "评论功能暂未完成"); + } + + void handleMenuButtonClick() { + menuSlider.currentState?.openSlider(); + } } class ArticlePage extends StatefulWidget { @@ -84,7 +104,8 @@ class ArticlePage extends StatefulWidget { final String? targetPage; final int? targetPageId; - const ArticlePage({super.key, this.targetPage, this.targetPageId, this.initialArticleData}); + const ArticlePage( + {super.key, this.targetPage, this.targetPageId, this.initialArticleData}); @override State createState() => _ArticlePageState(); @@ -102,6 +123,13 @@ class _ArticlePageState extends ReactiveState { c.loadPageContent(); } + @override + void dispose() { + super.dispose(); + + c.dispose(); + } + @override void receiveProps() { c.pageTitle.value = widget.targetPage ?? ""; @@ -111,17 +139,75 @@ class _ArticlePageState extends ReactiveState { @override Widget render(BuildContext context) { - return IsekaiPageScaffold( - navigationBar: IsekaiNavigationBar( - middle: Obx(() => Text(c.displayTitle.value)), + final flc = Get.find(); + + return SliderDrawer( + key: c.menuSlider, + isCupertino: true, + appBar: null, + showCover: true, + slideDirection: SlideDirection.RIGHT_TO_LEFT, + slider: Container( + color: CupertinoColors.systemRed, ), - bottomNavigationBar: BottomNavigationBar(child: SizedBox()), - child: SafeAreaBuilder( - builder: (context, padding) => Obx( - () => WikiPageParser( - padding: padding, - pageInfo: c.pageInfo.value, - parseInfo: c.parseInfo.value, + child: IsekaiPageScaffold( + navigationBar: IsekaiNavigationBar( + middle: Obx(() => Text(c.displayTitle.value)), + ), + bottomNavigationBar: BottomNavigationBar( + child: Padding( + padding: const EdgeInsets.all(0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + NavBarButton( + icon: CupertinoIcons.chat_bubble_2, + onPressed: c.handleFlowButtonClick, + semanticLabel: "讨论", + ), + Obx(() { + if (c.pageInfo.value != null && + flc.isFavorite(c.pageInfo.value!)) { + return NavBarButton( + icon: CupertinoIcons.heart_fill, + onPressed: () {}, + semanticLabel: "取消收藏", + ); + } else { + return NavBarButton( + icon: CupertinoIcons.heart, + onPressed: () {}, + semanticLabel: "收藏", + ); + } + }), + NavBarButton( + icon: CupertinoIcons.share, + onPressed: c.handleFlowButtonClick, + semanticLabel: "分享", + ), + NavBarButton( + icon: CupertinoIcons.textformat_alt, + onPressed: c.handleFlowButtonClick, + semanticLabel: "阅读设置", + ), + NavBarButton( + icon: CupertinoIcons.list_bullet, + onPressed: c.handleMenuButtonClick, + semanticLabel: "目录", + ), + ], + ), + ), + ), + child: SafeAreaBuilder( + builder: (context, padding) => Obx( + () => WikiPageParser( + padding: padding, + pageInfo: c.pageInfo.value, + parseInfo: c.parseInfo.value, + ), ), ), ), diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 36f6b15..26caea5 100755 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -15,9 +15,15 @@ import '../components/collapsed_tab.dart'; import '../global.dart'; import '../styles.dart'; +const Color _kDefaultTabBarBorderColor = CupertinoDynamicColor.withBrightness( + color: Color(0x4C000000), + darkColor: Color(0x29000000), +); + enum HomeTabs { newest, followed } -class HomeController extends GetxController with GetSingleTickerProviderStateMixin { +class HomeController extends GetxController + with GetSingleTickerProviderStateMixin { double _navSearchButtonOffset = 90; var showNavSearchButton = false.obs; @@ -36,12 +42,15 @@ class HomeController extends GetxController with GetSingleTickerProviderStateMix void onInit() { tabController = TabController(length: 2, vsync: this); - _navSearchButtonOffset = 48 * MediaQuery.of(Get.context!).textScaleFactor + 48; + _navSearchButtonOffset = + 48 * MediaQuery.of(Get.context!).textScaleFactor + 48; scrollController.addListener(() { - if (scrollController.offset >= _navSearchButtonOffset && !showNavSearchButton.value) { + if (scrollController.offset >= _navSearchButtonOffset && + !showNavSearchButton.value) { showNavSearchButton.value = true; - } else if (scrollController.offset < _navSearchButtonOffset && showNavSearchButton.value) { + } else if (scrollController.offset < _navSearchButtonOffset && + showNavSearchButton.value) { showNavSearchButton.value = false; } }); @@ -99,7 +108,8 @@ class HomeTab extends StatelessWidget { duration: const Duration(milliseconds: 100), child: CupertinoButton( padding: EdgeInsets.zero, - child: const Icon(CupertinoIcons.bell, size: 26, color: Styles.themeNavTitleColor), + child: const Icon(CupertinoIcons.bell, + size: 26, color: Styles.themeNavTitleColor), onPressed: () {}, ), ), @@ -115,7 +125,8 @@ class HomeTab extends StatelessWidget { duration: const Duration(milliseconds: 100), child: CupertinoButton( padding: EdgeInsets.zero, - child: const Icon(CupertinoIcons.search, size: 26, color: Styles.themeNavTitleColor), + child: const Icon(CupertinoIcons.search, + size: 26, color: Styles.themeNavTitleColor), onPressed: () { onSearchClick?.call(); }, @@ -141,7 +152,8 @@ class HomeTab extends StatelessWidget { leading: _buildSearchIconButton(), backgroundColor: Styles.themeMainColor, brightness: Brightness.dark, - largeTitle: const Text('首页', style: TextStyle(color: Styles.themeNavTitleColor)), + largeTitle: const Text('首页', + style: TextStyle(color: Styles.themeNavTitleColor)), trailing: _buildNotificationIconButton(), border: Border.all(style: BorderStyle.none), ), @@ -161,7 +173,8 @@ class HomeTab extends StatelessWidget { ), ], ), - padding: const EdgeInsets.only(bottom: 10, left: 12, right: 12), + padding: + const EdgeInsets.only(bottom: 10, left: 12, right: 12), child: CupertinoButton( color: Colors.white, padding: const EdgeInsets.all(0), @@ -174,10 +187,12 @@ class HomeTab extends StatelessWidget { children: [ Container( padding: const EdgeInsets.all(1), - child: const Icon(CupertinoIcons.search, color: Colors.black54), + child: const Icon(CupertinoIcons.search, + color: Colors.black54), ), const Text("搜索页面...", - textAlign: TextAlign.center, style: TextStyle(color: Colors.black54)) + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black54)) ], ), ), @@ -208,7 +223,10 @@ class HomeTab extends StatelessWidget { indicatorColor: Styles.themeMainColor, labelColor: Styles.themeMainColor, unselectedLabelColor: Colors.black45, - tabs: const [CollapsedTabText('最新'), CollapsedTabText('关注')], + tabs: const [ + CollapsedTabText('最新'), + CollapsedTabText('关注') + ], onTap: (int selected) {}, ), ), @@ -218,6 +236,26 @@ class HomeTab extends StatelessWidget { ), ), ), + SliverPersistentHeader( + pinned: true, + delegate: _SliverAppBarDelegate( + minHeight: 1, + maxHeight: 1, + child: Container( + decoration: BoxDecoration( + color: Colors.transparent, + boxShadow: [ + BoxShadow( + color: Theme.of(context).shadowColor, + spreadRadius: 2, + blurRadius: 4, + offset: const Offset(0, 2), + ) + ], + ), + ), + ), + ), CupertinoSliverRefreshControl( onRefresh: c.handleRefresh, ), @@ -254,7 +292,8 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { double get maxExtent => max(maxHeight, minHeight); @override - Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { return SizedBox.expand(child: child); } diff --git a/lib/pages/tab_page.dart b/lib/pages/tab_page.dart index 46c8cd7..fd696e6 100755 --- a/lib/pages/tab_page.dart +++ b/lib/pages/tab_page.dart @@ -61,7 +61,6 @@ class IsekaiWikiTabsPage extends StatelessWidget { resizeToAvoidBottomInset: false, tabBar: CupertinoTabBar( backgroundColor: CupertinoTheme.of(context).barBackgroundColor, - border: const Border(top: BorderSide(color: CupertinoColors.systemGrey5, width: 2)), height: 56, onTap: c.handleTapTab, items: const [ diff --git a/lib/pages/welcome_page.dart b/lib/pages/welcome_page.dart index 4bc56ad..09adf74 100644 --- a/lib/pages/welcome_page.dart +++ b/lib/pages/welcome_page.dart @@ -40,7 +40,8 @@ class WelcomePageController extends GetxController { ); } catch (err, stack) { // ignore: prefer_interpolation_to_compose_strings - alert(Get.overlayContext!, "无法加载站点配置:" + ErrorUtils.getErrorMessage(err), title: "错误"); + alert(Get.overlayContext!, "无法加载站点配置:" + ErrorUtils.getErrorMessage(err), + title: "错误"); if (kDebugMode) { print("Exception in logout: $err"); stack.printError(); @@ -57,58 +58,57 @@ class WelcomePage extends StatelessWidget { Widget build(BuildContext context) { var c = Get.put(WelcomePageController()); - return AnnotatedRegion( - value: SystemUiOverlayStyle.light, - child: IsekaiPageScaffold( - backgroundColor: CupertinoColors.white, - child: Stack( - fit: StackFit.expand, - children: [ - Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: ExactAssetImage('assets/images/title.png'), - fit: BoxFit.cover, - ), + return IsekaiPageScaffold( + backgroundColor: CupertinoColors.white, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: ExactAssetImage('assets/images/title.png'), + fit: BoxFit.cover, ), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: Container( - decoration: BoxDecoration(color: Colors.black.withOpacity(0.4)), - ), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: Container( + decoration: BoxDecoration(color: Colors.black.withOpacity(0.4)), ), ), - SafeArea( - child: OrientationBuilder( - builder: (context, orientation) => Padding( - padding: orientation == Orientation.portrait - ? const EdgeInsets.only(top: 48, right: 20, bottom: 32, left: 20) - : const EdgeInsets.symmetric(horizontal: 20, vertical: 32), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Flexible( - flex: 0, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 8), - child: _buildSiteTitle(context, c), - ), + ), + SafeArea( + child: OrientationBuilder( + builder: (context, orientation) => Padding( + padding: orientation == Orientation.portrait + ? const EdgeInsets.only( + top: 48, right: 20, bottom: 32, left: 20) + : const EdgeInsets.symmetric(horizontal: 20, vertical: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + flex: 0, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 24, horizontal: 8), + child: _buildSiteTitle(context, c), ), - ], - ), - const SizedBox(height: 36), - _buildActions(context, c), - ], - ), + ), + ], + ), + const SizedBox(height: 36), + _buildActions(context, c), + ], ), ), ), - ], - ), + ), + ], ), ); } @@ -121,7 +121,8 @@ class WelcomePage extends StatelessWidget { data: MediaQueryData(textScaleFactor: 1), child: Text( Global.siteTitle, - style: TextStyle(fontSize: 48, color: Colors.white, fontWeight: FontWeight.bold), + style: TextStyle( + fontSize: 48, color: Colors.white, fontWeight: FontWeight.bold), ), ), Text( @@ -186,7 +187,9 @@ class WelcomePage extends StatelessWidget { Obx( () => CupertinoButton.filled( disabledColor: theme.primaryColor.withOpacity(0.6), - onPressed: c.policyAccepted.value && !c.isLoading.value ? c.handleClickContinue : null, + onPressed: c.policyAccepted.value && !c.isLoading.value + ? c.handleClickContinue + : null, child: const Text("继续"), ), ), diff --git a/lib/styles.dart b/lib/styles.dart index 076f683..f68c7b6 100755 --- a/lib/styles.dart +++ b/lib/styles.dart @@ -37,10 +37,12 @@ abstract class Styles { ); static ThemeData materialLightTheme = ThemeData.light().copyWith( + shadowColor: Colors.black12, cardTheme: cardTheme, ); static ThemeData materialDarkTheme = ThemeData.dark().copyWith( + shadowColor: Colors.black12, cardTheme: cardTheme.copyWith( color: const Color.fromRGBO(28, 28, 28, 1), ), @@ -55,7 +57,8 @@ abstract class Styles { static const TextStyle listTileLargeTitle = TextStyle(fontSize: 18); static const TextStyle listTileSubTitle = TextStyle(fontSize: 16); - static const TextStyle loadingDialogTitle = TextStyle(fontSize: 18, fontWeight: FontWeight.w500); + static const TextStyle loadingDialogTitle = + TextStyle(fontSize: 18, fontWeight: FontWeight.w500); static const Color websiteNavbarColor = Color.fromRGBO(33, 37, 41, 1); @@ -67,12 +70,14 @@ abstract class Styles { darkColor: Color.fromRGBO(41, 41, 41, 1), ); - static const Color themePageBackgroundColor = CupertinoDynamicColor.withBrightness( + static const Color themePageBackgroundColor = + CupertinoDynamicColor.withBrightness( color: Color.fromRGBO(240, 240, 240, 1), darkColor: Color.fromRGBO(0, 0, 0, 1), ); - static const Color themePureBackgroundColor = CupertinoDynamicColor.withBrightness( + static const Color themePureBackgroundColor = + CupertinoDynamicColor.withBrightness( color: Color.fromRGBO(255, 255, 255, 1), darkColor: Color.fromRGBO(0, 0, 0, 1), ); diff --git a/packages/flutter_slider_drawer b/packages/flutter_slider_drawer new file mode 160000 index 0000000..d9c9254 --- /dev/null +++ b/packages/flutter_slider_drawer @@ -0,0 +1 @@ +Subproject commit d9c92541d120e653b69a114fafdb6648455ec621 diff --git a/pubspec.lock b/pubspec.lock index 2313a3c..7eeafe5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,287 +5,280 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "50.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.2.0" animated_snack_bar: dependency: "direct main" description: name: animated_snack_bar - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.3.0" animations: dependency: "direct main" description: name: animations - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.7" args: dependency: transitive description: name: args - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "2.3.1" + version: "2.3.2" async: dependency: "direct main" description: name: async - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.9.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" build: dependency: transitive description: name: build - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.0" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.3" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "7.2.7" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "8.4.3" cached_network_image: dependency: "direct main" description: name: cached_network_image - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.2.3" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" characters: dependency: transitive description: name: characters - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.1" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.2" chewie: dependency: transitive description: name: chewie - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.6" chewie_audio: dependency: transitive description: name: chewie_audio - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.0" clock: dependency: transitive description: name: clock - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.4.0" collection: dependency: transitive description: name: collection - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.16.0" convert: dependency: transitive description: name: convert - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.1" cookie_jar: dependency: transitive description: name: cookie_jar - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" crypto: dependency: transitive description: name: crypto - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.2" csslib: dependency: transitive description: name: csslib - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.17.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.5" cupertino_lists: dependency: "direct main" description: name: cupertino_lists - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.4" dio: dependency: "direct main" description: name: dio - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.0.6" dio_cache_interceptor: dependency: "direct main" description: name: dio_cache_interceptor - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.3.1" dio_cookie_manager: dependency: "direct main" description: name: dio_cookie_manager - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.0" dio_http2_adapter: dependency: "direct main" description: name: dio_http2_adapter - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.0" - equatable: - dependency: transitive - description: - name: equatable - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.5" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.1.4" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.1" flutter: @@ -297,56 +290,49 @@ packages: dependency: transitive description: name: flutter_blurhash - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.7.0" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.3.0" flutter_displaymode: dependency: "direct main" description: name: flutter_displaymode - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.3.2" - flutter_hooks: - dependency: transitive - description: - name: flutter_hooks - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.18.5+1" flutter_html: dependency: "direct main" description: name: flutter_html - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.1" flutter_inappwebview: dependency: "direct main" description: name: flutter_inappwebview - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.7.2+3" flutter_layout_grid: dependency: transitive description: name: flutter_layout_grid - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.6" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" flutter_localizations: @@ -358,21 +344,21 @@ packages: dependency: transitive description: name: flutter_math_fork - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.5.0" - flutter_scale_tap: + flutter_slider_drawer: dependency: "direct main" description: - name: flutter_scale_tap - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.5" + path: "packages/flutter_slider_drawer" + relative: true + source: path + version: "2.1.2" flutter_svg: dependency: transitive description: name: flutter_svg - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.23.0+1" flutter_test: @@ -384,7 +370,7 @@ packages: dependency: "direct main" description: name: flutter_web_browser - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.17.1" flutter_web_plugins: @@ -396,420 +382,427 @@ packages: dependency: "direct main" description: name: fluttertoast - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "8.1.2" freezed: dependency: "direct dev" description: name: freezed - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.2" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.0" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.2.0" get: dependency: "direct main" description: name: get - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.6.5" get_storage: dependency: "direct main" description: name: get_storage - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.3" glob: dependency: transitive description: name: glob - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" graphs: dependency: transitive description: name: graphs - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.0" html: dependency: transitive description: name: html - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.15.1" http: dependency: transitive description: name: http - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.13.5" http2: dependency: "direct main" description: name: http2 - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.0.2" intl: dependency: "direct main" description: name: intl - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.17.0" io: dependency: transitive description: name: io - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "1.0.3" + version: "1.0.4" js: dependency: transitive description: name: js - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.6.4" json_annotation: dependency: "direct main" description: name: json_annotation - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "4.7.0" + version: "4.8.0" json_serializable: dependency: "direct dev" description: name: json_serializable - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "6.5.4" + version: "6.6.0" like_button: dependency: "direct main" description: name: like_button - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.5" lints: dependency: transitive description: name: lints - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" logging: dependency: transitive description: name: logging - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.1.5" meta: dependency: transitive description: name: meta - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.8.0" mime: dependency: transitive description: name: mime - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.4" modal_bottom_sheet: dependency: "direct main" description: name: modal_bottom_sheet - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.2" nested: dependency: transitive description: name: nested - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.0" numerus: dependency: transitive description: name: numerus - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" octo_image: dependency: transitive description: name: octo_image - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" package_config: dependency: transitive description: name: package_config - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" package_info_plus: dependency: "direct main" description: name: package_info_plus - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" path: dependency: transitive description: name: path - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.8.2" path_drawing: dependency: transitive description: name: path_drawing - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.5.1+1" path_parsing: dependency: transitive description: name: path_parsing - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.2.1" path_provider: dependency: "direct main" description: name: path_provider - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.11" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.22" path_provider_ios: dependency: transitive description: name: path_provider_ios - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.11" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.7" path_provider_macos: dependency: transitive description: name: path_provider_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.7" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.5" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.3" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.11.1" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.1.0" platform: dependency: transitive description: name: platform - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.3" pool: dependency: transitive description: name: pool - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.5.1" process: dependency: transitive description: name: process - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "4.2.4" provider: dependency: "direct main" description: name: provider - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.5" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.3" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.1" pull_down_button: dependency: "direct main" description: name: pull_down_button - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.1" quiver: dependency: transitive description: name: quiver - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.2.1" responsive_builder: dependency: "direct main" description: name: responsive_builder - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.3" roundcheckbox: dependency: "direct main" description: name: roundcheckbox - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.5" - ruby_text: - dependency: "direct main" - description: - name: ruby_text - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.0.1" rxdart: dependency: transitive description: name: rxdart - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.27.7" + scroll_bars_common: + dependency: transitive + description: + name: scroll_bars_common + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" + scroll_bottom_navigation_bar: + dependency: "direct main" + description: + name: scroll_bottom_navigation_bar + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" shelf: dependency: transitive description: name: shelf - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.4.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.3" skeletons: dependency: "direct main" description: name: skeletons - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.0.3" sky_engine: @@ -821,322 +814,322 @@ packages: dependency: transitive description: name: source_gen - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.6" source_helper: dependency: transitive description: name: source_helper - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.3" source_span: dependency: transitive description: name: source_span - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.9.0" sqflite: dependency: transitive description: name: sqflite - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.2.3" sqflite_common: dependency: transitive description: name: sqflite_common - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.4.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.1" synchronized: dependency: transitive description: name: synchronized - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.12" timing: dependency: transitive description: name: timing - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.1" tuple: dependency: transitive description: name: tuple - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.3.1" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.1.7" url_launcher_android: dependency: transitive description: name: url_launcher_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.22" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.17" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.13" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.1" uuid: dependency: transitive description: name: uuid - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.1.2" video_player: dependency: transitive description: name: video_player - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted - version: "2.4.10" + version: "2.5.0" video_player_android: dependency: transitive description: name: video_player_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.10" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.8" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "6.0.1" video_player_web: dependency: transitive description: name: video_player_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.0.13" wakelock: dependency: transitive description: name: wakelock - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.6.2" wakelock_macos: dependency: transitive description: name: wakelock_macos - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.0" wakelock_platform_interface: dependency: transitive description: name: wakelock_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.3.0" wakelock_web: dependency: transitive description: name: wakelock_web - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.4.0" wakelock_windows: dependency: transitive description: name: wakelock_windows - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.2.1" watcher: dependency: transitive description: name: watcher - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.0.2" web_smooth_scroll: dependency: "direct main" description: name: web_smooth_scroll - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.3.0" webview_flutter: dependency: transitive description: name: webview_flutter - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.8.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.10.4" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "1.9.5" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "2.9.5" win32: dependency: transitive description: name: win32 - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.3" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "0.2.0+3" xml: dependency: transitive description: name: xml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "5.4.1" yaml: dependency: transitive description: name: yaml - url: "https://pub.flutter-io.cn" + url: "https://pub.dartlang.org" source: hosted version: "3.1.1" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index 5d5cd36..cf0f3c4 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,10 +43,10 @@ dependencies: provider: ^6.0.0 animations: ^2.0.4 flutter_displaymode: ^0.3.2 - flutter_scale_tap: ^1.0.5 roundcheckbox: ^2.0.5 like_button: ^2.0.4 skeletons: ^0.0.3 + scroll_bottom_navigation_bar: ^4.0.0 modal_bottom_sheet: ^2.1.2 fluttertoast: ^8.1.2 animated_snack_bar: ^0.3.0 @@ -57,7 +57,6 @@ dependencies: web_smooth_scroll: ^1.0.0 json_annotation: ^4.7.0 flutter_html: ^2.2.1 - ruby_text: ^3.0.1 package_info_plus: ^3.0.2 pull_down_button: ^0.4.1 cached_network_image: ^3.2.3 @@ -71,6 +70,9 @@ dependencies: get_storage: ^2.0.3 freezed_annotation: ^2.2.0 + flutter_slider_drawer: + path: "./packages/flutter_slider_drawer" + dev_dependencies: flutter_test: sdk: flutter @@ -103,6 +105,7 @@ flutter: assets: - assets/images/title.png - assets/tpl/wikiPage.html + - assets/js/app_bridge.js # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware