💫 Optimize nav transition

This commit is contained in:
LittleSheep 2024-11-15 23:08:29 +08:00
parent 37c61a0406
commit ee2cb0c989
5 changed files with 150 additions and 78 deletions

View File

@ -13,7 +13,6 @@ import 'package:surface/providers/theme.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/providers/websocket.dart'; import 'package:surface/providers/websocket.dart';
import 'package:surface/router.dart'; import 'package:surface/router.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@ -84,9 +83,6 @@ class AppMainContent extends StatelessWidget {
...context.localizationDelegates, ...context.localizationDelegates,
], ],
routerConfig: appRouter, routerConfig: appRouter,
builder: (context, child) {
return AppRootScaffold(body: child ?? const SizedBox.shrink());
},
); );
} }
} }

View File

@ -24,6 +24,14 @@ class NavigationProvider extends ChangeNotifier {
int? get currentIndex => _currentIndex; int? get currentIndex => _currentIndex;
static const List<String> kShowBottomNavScreen = [
'home',
'explore',
'account',
'album',
'chat',
];
static const List<AppNavDestination> kAllDestination = [ static const List<AppNavDestination> kAllDestination = [
AppNavDestination( AppNavDestination(
icon: Icon(Symbols.home, weight: 400, opticalSize: 20), icon: Icon(Symbols.home, weight: 400, opticalSize: 20),

View File

@ -1,3 +1,4 @@
import 'package:animations/animations.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:surface/screens/account.dart'; import 'package:surface/screens/account.dart';
import 'package:surface/screens/account/profile_edit.dart'; import 'package:surface/screens/account/profile_edit.dart';
@ -14,13 +15,13 @@ import 'package:surface/screens/post/post_detail.dart';
import 'package:surface/screens/post/post_editor.dart'; import 'package:surface/screens/post/post_editor.dart';
import 'package:surface/screens/settings.dart'; import 'package:surface/screens/settings.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/widgets/navigation/app_background.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
final _appRoutes = [ final _appRoutes = [
ShellRoute( ShellRoute(
builder: (context, state, child) => AppScaffold( builder: (context, state, child) => AppPageScaffold(
body: child, body: child,
showBottomNavigation: true,
showAppBar: false, showAppBar: false,
), ),
routes: [ routes: [
@ -37,6 +38,52 @@ final _appRoutes = [
pageBuilder: (context, state) => NoTransitionPage( pageBuilder: (context, state) => NoTransitionPage(
child: const ExploreScreen(), child: const ExploreScreen(),
), ),
routes: [
GoRoute(
path: '/post/write/:mode',
name: 'postEditor',
pageBuilder: (context, state) => CustomTransitionPage(
child: PostEditorScreen(
mode: state.pathParameters['mode']!,
postEditId: int.tryParse(
state.uri.queryParameters['editing'] ?? '',
),
postReplyId: int.tryParse(
state.uri.queryParameters['replying'] ?? '',
),
postRepostId: int.tryParse(
state.uri.queryParameters['reposting'] ?? '',
),
),
transitionsBuilder:
(context, animation, secondaryAnimation, child) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: AppBackground(isLessOptimization: true, child: child),
);
},
),
),
GoRoute(
path: '/post/:slug',
name: 'postDetail',
pageBuilder: (context, state) => CustomTransitionPage(
child: PostDetailScreen(
slug: state.pathParameters['slug']!,
preload: state.extra as SnPost?,
),
transitionsBuilder:
(context, animation, secondaryAnimation, child) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: AppBackground(isLessOptimization: true, child: child),
);
},
),
),
],
), ),
GoRoute( GoRoute(
path: '/account', path: '/account',
@ -62,36 +109,7 @@ final _appRoutes = [
], ],
), ),
ShellRoute( ShellRoute(
builder: (context, state, child) => child, builder: (context, state, child) => AppPageScaffold(body: child),
routes: [
GoRoute(
path: '/post/write/:mode',
name: 'postEditor',
builder: (context, state) => PostEditorScreen(
mode: state.pathParameters['mode']!,
postEditId: int.tryParse(
state.uri.queryParameters['editing'] ?? '',
),
postReplyId: int.tryParse(
state.uri.queryParameters['replying'] ?? '',
),
postRepostId: int.tryParse(
state.uri.queryParameters['reposting'] ?? '',
),
),
),
GoRoute(
path: '/post/:slug',
name: 'postDetail',
builder: (context, state) => PostDetailScreen(
slug: state.pathParameters['slug']!,
preload: state.extra as SnPost?,
),
)
],
),
ShellRoute(
builder: (context, state, child) => AppScaffold(body: child),
routes: [ routes: [
GoRoute( GoRoute(
path: '/auth/login', path: '/auth/login',
@ -128,7 +146,7 @@ final _appRoutes = [
], ],
), ),
ShellRoute( ShellRoute(
builder: (context, state, child) => AppScaffold(body: child), builder: (context, state, child) => AppPageScaffold(body: child),
routes: [ routes: [
GoRoute( GoRoute(
path: '/settings', path: '/settings',
@ -140,5 +158,10 @@ final _appRoutes = [
]; ];
final appRouter = GoRouter( final appRouter = GoRouter(
routes: [
ShellRoute(
routes: _appRoutes, routes: _appRoutes,
builder: (context, state, child) => AppRootScaffold(body: child),
),
],
); );

View File

@ -6,21 +6,44 @@ import 'package:path_provider/path_provider.dart';
class AppBackground extends StatelessWidget { class AppBackground extends StatelessWidget {
final Widget child; final Widget child;
const AppBackground({super.key, required this.child}); final bool isLessOptimization;
const AppBackground({
super.key,
required this.child,
this.isLessOptimization = false,
});
@override Widget _buildWithBackgroundImage(
Widget build(BuildContext context) { BuildContext context,
File imageFile,
Widget child,
) {
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
return ScaffoldMessenger( if (isLessOptimization) {
child: FutureBuilder( final size = MediaQuery.of(context).size;
future: return Container(
kIsWeb ? Future.value(null) : getApplicationDocumentsDirectory(), color: Theme.of(context).colorScheme.surface,
builder: (context, snapshot) { child: Container(
if (snapshot.hasData) { decoration: BoxDecoration(
final path = '${snapshot.data!.path}/app_background_image'; backgroundBlendMode: BlendMode.darken,
final file = File(path); color: Theme.of(context).colorScheme.surface,
if (file.existsSync()) { image: DecorationImage(
opacity: 0.2,
image: ResizeImage(
FileImage(imageFile),
width: (size.width * devicePixelRatio).round(),
height: (size.height * devicePixelRatio).round(),
policy: ResizeImagePolicy.fit,
),
fit: BoxFit.cover,
),
),
child: child,
),
);
}
return Container( return Container(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).colorScheme.surface,
child: LayoutBuilder( child: LayoutBuilder(
@ -32,11 +55,9 @@ class AppBackground extends StatelessWidget {
image: DecorationImage( image: DecorationImage(
opacity: 0.2, opacity: 0.2,
image: ResizeImage( image: ResizeImage(
FileImage(file), FileImage(imageFile),
width: (constraints.maxWidth * devicePixelRatio) width: (constraints.maxWidth * devicePixelRatio).round(),
.round(), height: (constraints.maxHeight * devicePixelRatio).round(),
height: (constraints.maxHeight * devicePixelRatio)
.round(),
policy: ResizeImagePolicy.fit, policy: ResizeImagePolicy.fit,
), ),
fit: BoxFit.cover, fit: BoxFit.cover,
@ -48,6 +69,20 @@ class AppBackground extends StatelessWidget {
), ),
); );
} }
@override
Widget build(BuildContext context) {
return ScaffoldMessenger(
child: FutureBuilder(
future:
kIsWeb ? Future.value(null) : getApplicationDocumentsDirectory(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final path = '${snapshot.data!.path}/app_background_image';
final file = File(path);
if (file.existsSync()) {
return _buildWithBackgroundImage(context, file, child);
}
} }
return Material( return Material(

View File

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:responsive_framework/responsive_framework.dart'; import 'package:responsive_framework/responsive_framework.dart';
import 'package:surface/providers/navigation.dart';
import 'package:surface/widgets/connection_indicator.dart'; import 'package:surface/widgets/connection_indicator.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/navigation/app_background.dart'; import 'package:surface/widgets/navigation/app_background.dart';
@ -9,12 +10,12 @@ import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
import 'package:surface/widgets/navigation/app_drawer_navigation.dart'; import 'package:surface/widgets/navigation/app_drawer_navigation.dart';
import 'package:surface/widgets/navigation/app_rail_navigation.dart'; import 'package:surface/widgets/navigation/app_rail_navigation.dart';
class AppScaffold extends StatelessWidget { class AppPageScaffold extends StatelessWidget {
final String? title; final String? title;
final Widget? body; final Widget? body;
final bool showAppBar; final bool showAppBar;
final bool showBottomNavigation; final bool showBottomNavigation;
const AppScaffold({ const AppPageScaffold({
super.key, super.key,
this.title, this.title,
this.body, this.body,
@ -24,14 +25,12 @@ class AppScaffold extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isShowBottomNavigation = (showBottomNavigation)
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
: false;
final state = GoRouter.maybeOf(context); final state = GoRouter.maybeOf(context);
final autoTitle = state != null final routeName =
? 'screen${state.routerDelegate.currentConfiguration.last.route.name?.capitalize()}' state?.routerDelegate.currentConfiguration.last.route.name;
: 'screen';
final autoTitle =
state != null ? 'screen${routeName?.capitalize()}' : 'screen';
return Scaffold( return Scaffold(
appBar: showAppBar appBar: showAppBar
@ -40,8 +39,6 @@ class AppScaffold extends StatelessWidget {
) )
: null, : null,
body: body, body: body,
bottomNavigationBar:
isShowBottomNavigation ? AppBottomNavigationBar() : null,
); );
} }
} }
@ -58,6 +55,17 @@ class AppRootScaffold extends StatelessWidget {
ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE); ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE);
final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET); final isExpandDrawer = ResponsiveBreakpoints.of(context).largerThan(TABLET);
final routeName = GoRouter.of(context)
.routerDelegate
.currentConfiguration
.last
.route
.name;
final isShowBottomNavigation =
NavigationProvider.kShowBottomNavScreen.contains(routeName)
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
: false;
final innerWidget = isCollapseDrawer final innerWidget = isCollapseDrawer
? body ? body
: Row( : Row(
@ -88,6 +96,8 @@ class AppRootScaffold extends StatelessWidget {
], ],
), ),
drawer: !isExpandDrawer ? AppNavigationDrawer() : null, drawer: !isExpandDrawer ? AppNavigationDrawer() : null,
bottomNavigationBar:
isShowBottomNavigation ? AppBottomNavigationBar() : null,
), ),
); );
} }