diff --git a/lib/router.dart b/lib/router.dart index 6bfcdff..aa990f0 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -40,20 +40,12 @@ abstract class AppRouter { ); static final ShellRoute _feedRoute = ShellRoute( - builder: (context, state, child) => BasicShell( - state: state, - sidebarFirst: true, - showAppBar: false, - sidebar: const FeedScreen(), - child: child, - ), + builder: (context, state, child) => child, routes: [ GoRoute( path: '/', name: 'feed', - builder: (context, state) => SolianTheme.isExtraLargeScreen(context) - ? const EmptyPagePlaceholder() - : const FeedScreen(), + builder: (context, state) => const FeedScreen(), ), GoRoute( path: '/posts/view/:alias', diff --git a/lib/screens/posts/post_detail.dart b/lib/screens/posts/post_detail.dart index 8a4c5c4..1c2e97a 100644 --- a/lib/screens/posts/post_detail.dart +++ b/lib/screens/posts/post_detail.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:solian/exts.dart'; import 'package:solian/models/post.dart'; import 'package:solian/providers/content/post.dart'; +import 'package:solian/widgets/centered_container.dart'; import 'package:solian/widgets/posts/post_item.dart'; import 'package:solian/widgets/posts/post_replies.dart'; @@ -47,21 +48,29 @@ class _PostDetailScreenState extends State { return CustomScrollView( slivers: [ SliverToBoxAdapter( - child: PostItem( - item: item!, - isClickable: true, - isFullDate: true, - isShowReply: false, + child: CenteredContainer( + child: PostItem( + item: item!, + isClickable: true, + isFullDate: true, + isShowReply: false, + ), ), ), - const SliverToBoxAdapter( - child: Divider(thickness: 0.3, height: 0.3), + SliverToBoxAdapter( + child: const Divider(thickness: 0.3, height: 1) + .paddingOnly(top: 4), ), SliverToBoxAdapter( - child: Text( - 'postReplies'.tr, - style: Theme.of(context).textTheme.headlineSmall, - ).paddingOnly(left: 24, right: 24, top: 16), + child: CenteredContainer( + child: Align( + alignment: Alignment.centerLeft, + child: Text( + 'postReplies'.tr, + style: Theme.of(context).textTheme.headlineSmall, + ).paddingOnly(left: 24, right: 24, top: 16), + ), + ), ), PostReplyList(item: item!), SliverToBoxAdapter( diff --git a/lib/shells/centered_shell.dart b/lib/shells/centered_shell.dart new file mode 100644 index 0000000..6d06f86 --- /dev/null +++ b/lib/shells/centered_shell.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:go_router/go_router.dart'; +import 'package:solian/router.dart'; +import 'package:solian/theme.dart'; +import 'package:solian/widgets/app_bar_title.dart'; +import 'package:solian/widgets/prev_page.dart'; + +class CenteredShell extends StatelessWidget { + final bool showAppBar; + final GoRouterState state; + final Widget child; + + const CenteredShell({ + super.key, + required this.child, + required this.state, + this.showAppBar = true, + }); + + @override + Widget build(BuildContext context) { + final canPop = AppRouter.instance.canPop(); + + return Scaffold( + appBar: showAppBar + ? AppBar( + title: AppBarTitle(state.topRoute?.name?.tr ?? 'page'.tr), + centerTitle: false, + toolbarHeight: SolianTheme.toolbarHeight(context), + leading: canPop ? const PrevPageButton() : null, + automaticallyImplyLeading: false, + ) + : null, + body: Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 640), + child: child, + ), + ), + ); + } +} diff --git a/lib/shells/nav_shell.dart b/lib/shells/nav_shell.dart deleted file mode 100644 index f0674aa..0000000 --- a/lib/shells/nav_shell.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:solian/router.dart'; -import 'package:solian/theme.dart'; -import 'package:solian/widgets/navigation/app_navigation.dart'; -import 'package:solian/widgets/prev_page.dart'; -import 'package:solian/widgets/navigation/app_navigation_bottom_bar.dart'; -import 'package:solian/widgets/navigation/app_navigation_rail.dart'; -import 'package:solian/widgets/sidebar/sidebar_placeholder.dart'; - -class NavShell extends StatelessWidget { - final bool showAppBar; - final bool showSidebar; - final bool showNavigation; - final bool? showBottomNavigation; - final Widget child; - - final bool sidebarFirst; - final Widget? sidebar; - - const NavShell({ - super.key, - required this.child, - this.showAppBar = true, - this.showSidebar = true, - this.showNavigation = true, - this.showBottomNavigation, - this.sidebarFirst = false, - this.sidebar, - }); - - List buildContent(BuildContext context) { - return [ - Flexible( - flex: 2, - child: child, - ), - if (SolianTheme.isExtraLargeScreen(context)) - const VerticalDivider(thickness: 0.3, width: 1), - if (SolianTheme.isExtraLargeScreen(context)) - Flexible( - flex: 1, - child: sidebar ?? const SidebarPlaceholder(), - ), - ]; - } - - @override - Widget build(BuildContext context) { - final canPop = AppRouter.instance.canPop(); - - final routeName = - AppRouter.instance.routerDelegate.currentConfiguration.last.route.name; - final showBottom = showBottomNavigation ?? - AppNavigation.destinationPages.contains(routeName); - - return Scaffold( - appBar: showAppBar - ? AppBar( - title: Text(routeName ?? 'page'.tr), - centerTitle: false, - titleSpacing: canPop ? null : 24, - elevation: SolianTheme.isLargeScreen(context) ? 1 : 0, - leading: canPop ? const PrevPageButton() : null, - automaticallyImplyLeading: false, - ) - : null, - bottomNavigationBar: (SolianTheme.isLargeScreen(context) || - !(showNavigation && showBottom)) - ? null - : const AppNavigationBottomBar(), - body: SolianTheme.isLargeScreen(context) - ? Row( - children: [ - if (showNavigation) const AppNavigationRail(), - if (showNavigation) - const VerticalDivider(thickness: 0.3, width: 1), - if (showSidebar && sidebarFirst) - ...buildContent(context).reversed - else if (showSidebar) - ...buildContent(context) - else - Expanded(child: child), - ], - ) - : child, - ); - } -} diff --git a/lib/widgets/centered_container.dart b/lib/widgets/centered_container.dart new file mode 100644 index 0000000..6f50c89 --- /dev/null +++ b/lib/widgets/centered_container.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class CenteredContainer extends StatelessWidget { + final Widget child; + final double maxWidth; + + const CenteredContainer({ + super.key, + required this.child, + this.maxWidth = 720, + }); + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + constraints: BoxConstraints(maxWidth: maxWidth), + child: child, + ), + ); + } +} diff --git a/lib/widgets/posts/post_list.dart b/lib/widgets/posts/post_list.dart index 45c58f4..6a0f7f2 100644 --- a/lib/widgets/posts/post_list.dart +++ b/lib/widgets/posts/post_list.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:solian/models/post.dart'; import 'package:solian/router.dart'; +import 'package:solian/widgets/centered_container.dart'; import 'package:solian/widgets/posts/post_action.dart'; import 'package:solian/widgets/posts/post_item.dart'; @@ -27,29 +28,31 @@ class PostListWidget extends StatelessWidget { builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) { return RepaintBoundary( - child: GestureDetector( - child: PostItem( - key: Key('p${item.alias}'), - item: item, - isShowEmbed: isShowEmbed, - isClickable: isNestedClickable, - ).paddingSymmetric(vertical: 8), - onTap: () { - if (!isClickable) return; - AppRouter.instance.pushNamed( - 'postDetail', - pathParameters: {'alias': item.alias}, - ); - }, - onLongPress: () { - showModalBottomSheet( - useRootNavigator: true, - context: context, - builder: (context) => PostAction(item: item), - ).then((value) { - if (value != null) controller.refresh(); - }); - }, + child: CenteredContainer( + child: GestureDetector( + child: PostItem( + key: Key('p${item.alias}'), + item: item, + isShowEmbed: isShowEmbed, + isClickable: isNestedClickable, + ).paddingSymmetric(vertical: 8), + onTap: () { + if (!isClickable) return; + AppRouter.instance.pushNamed( + 'postDetail', + pathParameters: {'alias': item.alias}, + ); + }, + onLongPress: () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + builder: (context) => PostAction(item: item), + ).then((value) { + if (value != null) controller.refresh(); + }); + }, + ), ), ); }, diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 33debae..7d98d50 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -83,6 +83,12 @@ PODS: - WebRTC-SDK (= 114.5735.10) - macos_window_utils (1.0.0): - FlutterMacOS + - media_kit_libs_macos_video (1.0.4): + - FlutterMacOS + - media_kit_native_event_loop (1.0.0): + - FlutterMacOS + - media_kit_video (0.0.1): + - FlutterMacOS - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -96,14 +102,13 @@ PODS: - PromisesObjC (2.4.0) - protocol_handler_macos (0.0.1): - FlutterMacOS + - screen_brightness_macos (0.1.0): + - FlutterMacOS - Sentry/HybridSDK (8.29.0) - sentry_flutter (8.3.0): - Flutter - FlutterMacOS - Sentry/HybridSDK (= 8.29.0) - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - sqflite (0.0.3): - Flutter - FlutterMacOS @@ -111,9 +116,6 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - - video_player_avfoundation (0.0.1): - - Flutter - - FlutterMacOS - wakelock_plus (0.0.1): - FlutterMacOS - WebRTC-SDK (114.5735.10) @@ -131,15 +133,17 @@ DEPENDENCIES: - irondash_engine_context (from `Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos`) - livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`) - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) + - media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`) + - media_kit_native_event_loop (from `Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos`) + - media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - protocol_handler_macos (from `Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos`) + - screen_brightness_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos`) - sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`) - - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`) - super_native_extensions (from `Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) SPEC REPOS: @@ -181,24 +185,28 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos macos_window_utils: :path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos + media_kit_libs_macos_video: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos + media_kit_native_event_loop: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_native_event_loop/macos + media_kit_video: + :path: Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin protocol_handler_macos: :path: Flutter/ephemeral/.symlinks/plugins/protocol_handler_macos/macos + screen_brightness_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_brightness_macos/macos sentry_flutter: :path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos - shared_preferences_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin super_native_extensions: :path: Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - video_player_avfoundation: - :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin wakelock_plus: :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos @@ -222,18 +230,20 @@ SPEC CHECKSUMS: irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 livekit_client: 9b39e0f1b8e1a8ec794bb72a4f9bbfc28c959ece macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 + media_kit_libs_macos_video: b3e2bbec2eef97c285f2b1baa7963c67c753fb82 + media_kit_native_event_loop: 81fd5b45192b72f8b5b69eaf5b540f45777eb8d5 + media_kit_video: c75b07f14d59706c775778e4dd47dd027de8d1e5 nanopb: 438bc412db1928dac798aa6fd75726007be04262 package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 protocol_handler_macos: d10a6c01d6373389ffd2278013ab4c47ed6d6daa + screen_brightness_macos: 2d6d3af2165592d9a55ffcd95b7550970e41ebda Sentry: 016de45ee5ce5fca2a829996f1bfafeb5e62e8b4 sentry_flutter: 5fb57c5b7e6427a9dc1fedde4269eb65823982d4 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 - video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 WebRTC-SDK: 8c0edd05b880a39648118192c252667ea06dea51