From f228c7ee175e47279d5333ccb663ed918e4c1444 Mon Sep 17 00:00:00 2001 From: nyne Date: Thu, 10 Oct 2024 10:13:35 +0800 Subject: [PATCH] improve ui --- android/app/src/main/res/values/styles.xml | 2 + lib/components/appbar.dart | 2 +- lib/components/message.dart | 2 + lib/components/navigation_bar.dart | 18 ++- lib/components/scroll.dart | 4 +- lib/foundation/appdata.dart | 6 +- lib/main.dart | 1 + lib/pages/comic_page.dart | 3 + lib/pages/comic_source_page.dart | 4 +- lib/pages/reader/comic_image.dart | 160 +++++++++++---------- lib/pages/reader/images.dart | 25 ++-- lib/pages/reader/reader.dart | 4 +- 12 files changed, 126 insertions(+), 105 deletions(-) diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef88..97a4e20 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -14,5 +14,7 @@ This Theme is only used starting with V2 of Flutter's Android embedding. --> diff --git a/lib/components/appbar.dart b/lib/components/appbar.dart index adfc96d..13487ba 100644 --- a/lib/components/appbar.dart +++ b/lib/components/appbar.dart @@ -296,7 +296,7 @@ class _FilledTabBarState extends State { padding: tabPadding, radius: tabRadius, ); - if (old != null) { + if (old != null && old.offsets != null && old.itemHeight != null) { painter!.update(old.offsets!, old.itemHeight!); } } diff --git a/lib/components/message.dart b/lib/components/message.dart index 7b5a562..d886f72 100644 --- a/lib/components/message.dart +++ b/lib/components/message.dart @@ -262,6 +262,8 @@ class ContentDialog extends StatelessWidget { insetPadding: context.width < 400 ? const EdgeInsets.symmetric(horizontal: 4) : const EdgeInsets.symmetric(horizontal: 16), + elevation: 2, + shadowColor: context.colorScheme.shadow, child: IntrinsicWidth( child: ConstrainedBox( constraints: BoxConstraints( diff --git a/lib/components/navigation_bar.dart b/lib/components/navigation_bar.dart index 2b9d84e..9eec4ab 100644 --- a/lib/components/navigation_bar.dart +++ b/lib/components/navigation_bar.dart @@ -127,15 +127,15 @@ class _NaviPaneState extends State } if (target == 1) { StateController.find() - .setWithPadding(true, true); + .setWithPadding(true, true, true); controller.value = target; } else if (controller.value == 1 && target == 0) { StateController.findOrNull() - ?.setWithPadding(false, false); + ?.setWithPadding(false, false, false); controller.value = target; } else { StateController.findOrNull() - ?.setWithPadding(true, false); + ?.setWithPadding(true, false, false); controller.animateTo(target); } animationTarget = target; @@ -660,9 +660,13 @@ class NaviPaddingWidgetController extends StateController { bool _withTopBarPadding = false; - void setWithPadding(bool withPadding, bool withAppbarPadding) { + bool _withBottomBarPadding = false; + + void setWithPadding( + bool withPadding, bool withAppbarPadding, bool withBottomBarPadding) { _withPadding = withPadding; _withTopBarPadding = withAppbarPadding; + _withBottomBarPadding = withBottomBarPadding; update(); } } @@ -683,8 +687,10 @@ class NaviPaddingWidget extends StatelessWidget { (controller._withTopBarPadding ? _NaviPaneState._kTopBarHeight : 0), - bottom: - _NaviPaneState._kBottomBarHeight + context.padding.bottom, + bottom: context.padding.bottom + + (controller._withBottomBarPadding + ? _NaviPaneState._kBottomBarHeight + : 0), ) : EdgeInsets.zero, child: child, diff --git a/lib/components/scroll.dart b/lib/components/scroll.dart index f5aa95d..0e39345 100644 --- a/lib/components/scroll.dart +++ b/lib/components/scroll.dart @@ -55,7 +55,7 @@ class _SmoothScrollProviderState extends State { return widget.builder( context, _controller, - const ClampingScrollPhysics(), + const BouncingScrollPhysics(), ); } return Listener( @@ -93,7 +93,7 @@ class _SmoothScrollProviderState extends State { _controller, _isMouseScroll ? const NeverScrollableScrollPhysics() - : const ClampingScrollPhysics(), + : const BouncingScrollPhysics(), ), ); } diff --git a/lib/foundation/appdata.dart b/lib/foundation/appdata.dart index 0ae83a1..6be3432 100644 --- a/lib/foundation/appdata.dart +++ b/lib/foundation/appdata.dart @@ -51,9 +51,9 @@ class _Appdata { return; } var json = jsonDecode(await file.readAsString()); - for(var key in json['settings'].keys) { - if(json[key] != null) { - settings[key] = json[key]; + for(var key in (json['settings'] as Map).keys) { + if(json['settings'][key] != null) { + settings[key] = json['settings'][key]; } } searchHistory = List.from(json['searchHistory']); diff --git a/lib/main.dart b/lib/main.dart index d9e6e33..ae34ad4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -56,6 +56,7 @@ class _MyAppState extends State { @override void initState() { App.registerForceRebuild(forceRebuild); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.initState(); } diff --git a/lib/pages/comic_page.dart b/lib/pages/comic_page.dart index 2af3c64..f82b11b 100644 --- a/lib/pages/comic_page.dart +++ b/lib/pages/comic_page.dart @@ -74,6 +74,7 @@ class _ComicPageState extends LoadingState buildChapters(), buildThumbnails(), buildRecommend(), + SliverPadding(padding: EdgeInsets.only(bottom: context.padding.bottom)), ], ); } @@ -109,6 +110,8 @@ class _ComicPageState extends LoadingState ], ); + yield const SliverPadding(padding: EdgeInsets.only(top: 8)); + yield Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/pages/comic_source_page.dart b/lib/pages/comic_source_page.dart index cc1da40..a4f965d 100644 --- a/lib/pages/comic_source_page.dart +++ b/lib/pages/comic_source_page.dart @@ -243,7 +243,7 @@ class _BodyState extends State<_Body> { .paddingBottom(32), Row( children: [ - TextButton(onPressed: chooseFile, child: Text("Choose file".tl)) + TextButton(onPressed: selectFile, child: Text("Select file".tl)) .paddingLeft(8), const Spacer(), TextButton( @@ -265,7 +265,7 @@ class _BodyState extends State<_Body> { ); } - void chooseFile() async { + void selectFile() async { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['js'], diff --git a/lib/pages/reader/comic_image.dart b/lib/pages/reader/comic_image.dart index 841058c..e5c1068 100644 --- a/lib/pages/reader/comic_image.dart +++ b/lib/pages/reader/comic_image.dart @@ -260,7 +260,8 @@ class _ComicImageState extends State with WidgetsBindingObserver { if (_lastException != null) { // display error and retry button on screen return SizedBox( - height: 300, + height: widget.height == null ? 300 : null, + width: widget.width == null ? 300 : null, child: Center( child: SizedBox( height: 300, @@ -295,87 +296,92 @@ class _ComicImageState extends State with WidgetsBindingObserver { ); } - var width = widget.width??MediaQuery.of(context).size.width; - double? height; + return LayoutBuilder(builder: (context, constrains) { + var width = widget.width; + var height = widget.height; - Size? cacheSize = _cache[widget.image.hashCode]; - if(cacheSize != null){ - height = cacheSize.height * (width / cacheSize.width); - height = height.ceilToDouble(); - } - - var brightness = Theme.of(context).brightness; - - if(_imageInfo != null){ - // Record the height and the width of the image - _cache[widget.image.hashCode] = Size( - _imageInfo!.image.width.toDouble(), - _imageInfo!.image.height.toDouble() - ); - // build image - Widget result = RawImage( - // Do not clone the image, because RawImage is a stateless wrapper. - // The image will be disposed by this state object when it is not needed - // anymore, such as when it is unmounted or when the image stream pushes - // a new image. - image: _imageInfo?.image, - debugImageLabel: _imageInfo?.debugLabel, - width: width, - height: height, - scale: _imageInfo?.scale ?? 1.0, - color: widget.color, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - fit: widget.fit, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - invertColors: _invertColors, - isAntiAlias: widget.isAntiAlias, - filterQuality: widget.filterQuality, - ); - - if (!widget.excludeFromSemantics) { - result = Semantics( - container: widget.semanticLabel != null, - image: true, - label: widget.semanticLabel ?? '', - child: result, + if(_imageInfo != null) { + // Record the height and the width of the image + _cache[widget.image.hashCode] = Size( + _imageInfo!.image.width.toDouble(), + _imageInfo!.image.height.toDouble() ); } - result = SizedBox( - width: width, - height: height, - child: Center( - child: result, - ), - ); - return result; - } else { - // build progress - return SizedBox( - width: width, - height: height??300, - child: Center( - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - backgroundColor: brightness == Brightness.dark - ? Colors.white24 - : Colors.black12, - strokeWidth: 3, - value: (_loadingProgress != null && - _loadingProgress!.expectedTotalBytes!=null && - _loadingProgress!.expectedTotalBytes! != 0) - ?_loadingProgress!.cumulativeBytesLoaded / _loadingProgress!.expectedTotalBytes! - :0, + + Size? cacheSize = _cache[widget.image.hashCode]; + if(cacheSize != null){ + if(width == double.infinity) { + width = constrains.maxWidth; + height = width * cacheSize.height / cacheSize.width; + } else if(height == double.infinity) { + height = constrains.maxHeight; + width = height * cacheSize.width / cacheSize.height; + } + } + + if(_imageInfo != null){ + // build image + Widget result = RawImage( + // Do not clone the image, because RawImage is a stateless wrapper. + // The image will be disposed by this state object when it is not needed + // anymore, such as when it is unmounted or when the image stream pushes + // a new image. + image: _imageInfo?.image, + debugImageLabel: _imageInfo?.debugLabel, + width: width, + height: height, + scale: _imageInfo?.scale ?? 1.0, + color: widget.color, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + fit: widget.fit, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + invertColors: _invertColors, + isAntiAlias: widget.isAntiAlias, + filterQuality: widget.filterQuality, + ); + + if (!widget.excludeFromSemantics) { + result = Semantics( + container: widget.semanticLabel != null, + image: true, + label: widget.semanticLabel ?? '', + child: result, + ); + } + result = SizedBox( + width: width, + height: height, + child: Center( + child: result, + ), + ); + return result; + } else { + // build progress + return SizedBox( + width: width, + height: height, + child: Center( + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 3, + value: (_loadingProgress != null && + _loadingProgress!.expectedTotalBytes!=null && + _loadingProgress!.expectedTotalBytes! != 0) + ?_loadingProgress!.cumulativeBytesLoaded / _loadingProgress!.expectedTotalBytes! + :0, + ), ), ), - ), - ); - } + ); + } + }); } @override diff --git a/lib/pages/reader/images.dart b/lib/pages/reader/images.dart index 17ee9b9..d25c966 100644 --- a/lib/pages/reader/images.dart +++ b/lib/pages/reader/images.dart @@ -258,7 +258,7 @@ class _GalleryModeState extends State<_GalleryMode> event.logicalKey == LogicalKeyboardKey.arrowRight) { forward = false; } - if(event is KeyDownEvent || event is KeyRepeatEvent) { + if (event is KeyDownEvent || event is KeyRepeatEvent) { if (forward == true) { controller.nextPage( duration: const Duration(milliseconds: 200), @@ -371,13 +371,12 @@ class _ContinuousModeState extends State<_ContinuousMode> if (index == 0 || index == reader.maxPage + 1) { return const SizedBox(); } - double width = MediaQuery.of(context).size.width; - double height = MediaQuery.of(context).size.height; - - double imageWidth = width; - - if (height / width < 1.2) { - imageWidth = height / 1.2; + double? width, height; + if (reader.mode == ReaderMode.continuousLeftToRight || + reader.mode == ReaderMode.continuousRightToLeft) { + height = double.infinity; + } else { + width = double.infinity; } _precacheImage(index, context); @@ -387,9 +386,9 @@ class _ContinuousModeState extends State<_ContinuousMode> return ComicImage( filterQuality: FilterQuality.medium, image: image, - width: imageWidth, - height: imageWidth * 1.2, - fit: BoxFit.cover, + width: width, + height: height, + fit: BoxFit.contain, ); }, scrollBehavior: const MaterialScrollBehavior() @@ -420,10 +419,10 @@ class _ContinuousModeState extends State<_ContinuousMode> return; } if (scrollController.offset != - scrollController.position.maxScrollExtent && + scrollController.position.maxScrollExtent && scrollController.offset != scrollController.position.minScrollExtent) { - if(reader.mode == ReaderMode.continuousTopToBottom) { + if (reader.mode == ReaderMode.continuousTopToBottom) { value = Offset(value.dx, 0); } else { value = Offset(0, value.dy); diff --git a/lib/pages/reader/reader.dart b/lib/pages/reader/reader.dart index 55bf4d0..7ee05a9 100644 --- a/lib/pages/reader/reader.dart +++ b/lib/pages/reader/reader.dart @@ -102,7 +102,9 @@ class _ReaderState extends State with _ReaderLocation, _ReaderWindow { chapter = widget.initialChapter ?? 1; mode = ReaderMode.fromKey(appdata.settings['readerMode']); history = widget.history; - updateHistory(); + Future.microtask(() { + updateHistory(); + }); super.initState(); }