Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions lib/presentation/shared/router/app_route_transition.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

enum AppRouteTransition {
fade,
standard,
bottomNavFromLeft,
bottomNavFromRight,
scheduleFlow,
}

CustomTransitionPage<T> buildAppRoutePage<T>({
required LocalKey key,
required Widget child,
AppRouteTransition transition = AppRouteTransition.standard,
}) {
final spec = _AppRouteTransitionSpec.fromTransition(transition);

return CustomTransitionPage<T>(
key: key,
transitionDuration: spec.duration,
reverseTransitionDuration: spec.reverseDuration,
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return _AppRouteTransitionView(
animation: animation,
transition: transition,
child: child,
);
},
);
}

class _AppRouteTransitionSpec {
const _AppRouteTransitionSpec({
required this.duration,
required this.reverseDuration,
});

final Duration duration;
final Duration reverseDuration;

static _AppRouteTransitionSpec fromTransition(AppRouteTransition transition) {
switch (transition) {
case AppRouteTransition.fade:
return const _AppRouteTransitionSpec(
duration: Duration(milliseconds: 180),
reverseDuration: Duration(milliseconds: 140),
);
case AppRouteTransition.standard:
return const _AppRouteTransitionSpec(
duration: Duration(milliseconds: 260),
reverseDuration: Duration(milliseconds: 220),
);
case AppRouteTransition.bottomNavFromLeft:
case AppRouteTransition.bottomNavFromRight:
return const _AppRouteTransitionSpec(
duration: Duration(milliseconds: 220),
reverseDuration: Duration(milliseconds: 180),
);
case AppRouteTransition.scheduleFlow:
return const _AppRouteTransitionSpec(
duration: Duration(milliseconds: 200),
reverseDuration: Duration(milliseconds: 160),
);
}
}
}

class _AppRouteTransitionView extends StatelessWidget {
const _AppRouteTransitionView({
required this.animation,
required this.transition,
required this.child,
});

final Animation<double> animation;
final AppRouteTransition transition;
final Widget child;

@override
Widget build(BuildContext context) {
switch (transition) {
case AppRouteTransition.fade:
return FadeTransition(
opacity: _curvedOpacityAnimation(animation, begin: 0),
child: child,
);
case AppRouteTransition.standard:
return FadeTransition(
opacity: _curvedOpacityAnimation(animation, begin: 0.08),
child: SlideTransition(
position: _curvedOffsetAnimation(
animation,
begin: const Offset(0, 0.032),
),
child: child,
),
);
case AppRouteTransition.bottomNavFromLeft:
return FadeTransition(
opacity: _curvedOpacityAnimation(animation, begin: 0.2),
child: SlideTransition(
position: _curvedOffsetAnimation(
animation,
begin: const Offset(-0.16, 0),
),
child: child,
),
);
case AppRouteTransition.bottomNavFromRight:
return FadeTransition(
opacity: _curvedOpacityAnimation(animation, begin: 0.2),
child: SlideTransition(
position: _curvedOffsetAnimation(
animation,
begin: const Offset(0.16, 0),
),
child: child,
),
);
case AppRouteTransition.scheduleFlow:
return FadeTransition(
opacity: _curvedOpacityAnimation(animation, begin: 0),
child: ScaleTransition(
scale: Tween<double>(begin: 0.985, end: 1).animate(
CurvedAnimation(parent: animation, curve: Curves.easeOutCubic),
),
child: child,
),
);
}
}

Animation<double> _curvedOpacityAnimation(
Animation<double> animation, {
required double begin,
}) {
return Tween<double>(
begin: begin,
end: 1,
).animate(CurvedAnimation(parent: animation, curve: Curves.easeOutCubic));
}

Animation<Offset> _curvedOffsetAnimation(
Animation<double> animation, {
required Offset begin,
}) {
return Tween<Offset>(
begin: begin,
end: Offset.zero,
).animate(CurvedAnimation(parent: animation, curve: Curves.easeOutCubic));
}
}
146 changes: 93 additions & 53 deletions lib/presentation/shared/router/go_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_pr
import 'package:on_time_front/presentation/schedule_create/screens/schedule_create_screen.dart';
import 'package:on_time_front/presentation/schedule_create/screens/schedule_edit_screen.dart';
import 'package:on_time_front/presentation/shared/components/bottom_nav_bar_scaffold.dart';
import 'package:on_time_front/presentation/shared/router/app_route_transition.dart';
import 'package:on_time_front/presentation/shared/router/route_arguments.dart';
import 'package:on_time_front/presentation/shared/utils/stream_to_listenable.dart';
import 'package:on_time_front/presentation/startup/screens/startup_screen.dart';
Expand Down Expand Up @@ -87,25 +88,43 @@ GoRouter goRouterConfig(
routes: [
GoRoute(
path: '/startup',
builder: (context, state) => const StartupScreen(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: const StartupScreen(),
),
),
GoRoute(
path: '/allowNotification',
builder: (context, state) {
return NotificationAllowScreen();
},
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: NotificationAllowScreen(),
),
),
GoRoute(
path: '/allowAlarm',
builder: (context, state) => const AlarmAllowScreen(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: const AlarmAllowScreen(),
),
),
GoRoute(
path: '/onboarding',
builder: (context, state) => OnboardingScreen(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: OnboardingScreen(),
),
routes: [
GoRoute(
path: '/start',
builder: (context, state) => OnboardingStartScreen(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: OnboardingStartScreen(),
),
),
],
),
Expand All @@ -114,58 +133,86 @@ GoRouter goRouterConfig(
routes: [
GoRoute(
path: '/home',
pageBuilder: (context, state) => _buildBottomNavSlidePage(
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
beginOffset: const Offset(-1, 0),
transition: AppRouteTransition.bottomNavFromLeft,
child: HomeScreenTmp(),
),
),
GoRoute(
path: '/myPage',
pageBuilder: (context, state) => _buildBottomNavSlidePage(
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
beginOffset: const Offset(1, 0),
transition: AppRouteTransition.bottomNavFromRight,
child: MyPageScreen(),
),
),
],
),
GoRoute(
path: '/defaultPreparationSpareTimeEdit',
builder: (context, state) => PreparationSpareTimeEditScreen(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
child: PreparationSpareTimeEditScreen(),
),
),
GoRoute(
path: '/signIn',
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.fade,
child: SignInMainScreen(),
),
),
GoRoute(path: '/signIn', builder: (context, state) => SignInMainScreen()),
GoRoute(
path: '/calendar',
builder: (context, state) =>
CalendarScreen(initialDate: calendarInitialDateFromState(state)),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
child: CalendarScreen(
initialDate: calendarInitialDateFromState(state),
),
),
),
GoRoute(
path: '/scheduleCreate',
builder: (context, state) => ScheduleCreateScreen(),
pageBuilder: (context, state) =>
_buildAppRoutePage(state: state, child: ScheduleCreateScreen()),
),
GoRoute(
path: '/scheduleEdit/:scheduleId',
builder: (context, state) =>
ScheduleEditScreen(scheduleId: state.pathParameters['scheduleId']!),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
child: ScheduleEditScreen(
scheduleId: state.pathParameters['scheduleId']!,
),
),
),
GoRoute(
path: '/preparationEdit',
builder: (context, state) => const PreparationEditForm(),
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
child: const PreparationEditForm(),
),
),
GoRoute(
path: '/scheduleStart',
name: 'scheduleStart',
builder: (context, state) {
pageBuilder: (context, state) {
final extra = scheduleStartRouteExtraFromState(state);
return _ScheduleStartRouteGate(extra: extra);
return _buildAppRoutePage(
state: state,
transition: AppRouteTransition.scheduleFlow,
child: _ScheduleStartRouteGate(extra: extra),
);
},
),
GoRoute(
path: '/alarmScreen',
builder: (context, state) {
return AlarmScreen();
},
pageBuilder: (context, state) => _buildAppRoutePage(
state: state,
transition: AppRouteTransition.scheduleFlow,
child: AlarmScreen(),
),
),
GoRoute(
path: '/earlyLate',
Expand All @@ -174,51 +221,44 @@ GoRouter goRouterConfig(
? '/home'
: null;
},
builder: (context, state) {
pageBuilder: (context, state) {
final arguments = earlyLateRouteArgumentsFromState(state);
if (arguments == null) {
return const LoadingScreen();
return _buildAppRoutePage(
state: state,
transition: AppRouteTransition.scheduleFlow,
child: const LoadingScreen(),
);
}

return EarlyLateScreen(
earlyLateTime: arguments.earlyLateTime,
isLate: arguments.isLate,
return _buildAppRoutePage(
state: state,
transition: AppRouteTransition.scheduleFlow,
child: EarlyLateScreen(
earlyLateTime: arguments.earlyLateTime,
isLate: arguments.isLate,
),
);
},
),
GoRoute(path: '/moving', builder: (context, state) => MovingScreen()),
GoRoute(
path: '/moving',
pageBuilder: (context, state) =>
_buildAppRoutePage(state: state, child: MovingScreen()),
),
],
);
}

CustomTransitionPage<void> _buildBottomNavSlidePage({
CustomTransitionPage<void> _buildAppRoutePage({
required GoRouterState state,
required Offset beginOffset,
AppRouteTransition transition = AppRouteTransition.standard,
required Widget child,
}) {
final slideTween = Tween<Offset>(
begin: beginOffset,
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeOutCubic));
final secondarySlideTween = Tween<Offset>(
begin: Offset.zero,
end: beginOffset,
).chain(CurveTween(curve: Curves.easeOutCubic));

return CustomTransitionPage<void>(
return buildAppRoutePage<void>(
key: state.pageKey,
transitionDuration: const Duration(milliseconds: 280),
reverseTransitionDuration: const Duration(milliseconds: 280),
transition: transition,
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: secondaryAnimation.drive(secondarySlideTween),
child: SlideTransition(
position: animation.drive(slideTween),
child: child,
),
);
},
);
}

Expand Down
Loading
Loading