first commit
This commit is contained in:
119
lib/app/core/navigation/app_navigator.dart
Normal file
119
lib/app/core/navigation/app_navigator.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
typedef NavResult<T> = void Function(T? result);
|
||||
typedef NavOpened = void Function();
|
||||
|
||||
class AppNavigator {
|
||||
AppNavigator._();
|
||||
|
||||
static Future<T?> push<T extends Object?>(
|
||||
BuildContext context,
|
||||
String route, {
|
||||
Object? extra,
|
||||
NavOpened? onOpened,
|
||||
NavResult<T>? onResult,
|
||||
}) async {
|
||||
final future = context.push<T>(route, extra: extra);
|
||||
|
||||
if (onOpened != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
onOpened();
|
||||
});
|
||||
}
|
||||
|
||||
if (onResult != null) {
|
||||
await future.then(onResult);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
static Future<void> clearAndPush(
|
||||
BuildContext context,
|
||||
String route, {
|
||||
Object? extra,
|
||||
NavOpened? onOpened,
|
||||
}) async {
|
||||
context.go(route, extra: extra);
|
||||
|
||||
if (onOpened != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
onOpened();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void pop<T extends Object?>(
|
||||
BuildContext context, [
|
||||
T? result,
|
||||
]) {
|
||||
context.pop(result);
|
||||
}
|
||||
|
||||
static void popToRoot(BuildContext context) {
|
||||
while (context.canPop()) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> maybePop(BuildContext context) async {
|
||||
if (context.canPop()) {
|
||||
context.pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<T?> pushFade<T extends Object?>(
|
||||
BuildContext context,
|
||||
String route, {
|
||||
Object? extra,
|
||||
Duration duration = const Duration(milliseconds: 200),
|
||||
NavOpened? onOpened,
|
||||
NavResult<T>? onResult,
|
||||
}) async {
|
||||
final future = context.push<T>(
|
||||
route,
|
||||
extra: extra,
|
||||
);
|
||||
|
||||
if (onOpened != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
onOpened();
|
||||
});
|
||||
}
|
||||
|
||||
if (onResult != null) {
|
||||
await future.then(onResult);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
static Future<T?> pushSlide<T extends Object?>(
|
||||
BuildContext context,
|
||||
String route, {
|
||||
Object? extra,
|
||||
Duration duration = const Duration(milliseconds: 250),
|
||||
NavOpened? onOpened,
|
||||
NavResult<T>? onResult,
|
||||
}) async {
|
||||
final future = context.push<T>(
|
||||
route,
|
||||
extra: extra,
|
||||
);
|
||||
|
||||
if (onOpened != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
onOpened();
|
||||
});
|
||||
}
|
||||
|
||||
if (onResult != null) {
|
||||
await future.then(onResult);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
}
|
||||
47
lib/app/core/navigation/app_resume_once.dart
Normal file
47
lib/app/core/navigation/app_resume_once.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class _OneShotResumeObserver with WidgetsBindingObserver {
|
||||
_OneShotResumeObserver(this._onResume);
|
||||
final Future<void> Function() _onResume;
|
||||
bool _done = false;
|
||||
|
||||
void start() => WidgetsBinding.instance.addObserver(this);
|
||||
void stop() {
|
||||
if (_done) return;
|
||||
_done = true;
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
if (state == AppLifecycleState.resumed && !_done) {
|
||||
try {
|
||||
await _onResume();
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> waitRecheckOnNextResume(
|
||||
Future<void> Function() onResume, {
|
||||
Duration timeout = const Duration(seconds: 45),
|
||||
}) async {
|
||||
final c = Completer<void>();
|
||||
final obs = _OneShotResumeObserver(() async {
|
||||
try {
|
||||
await onResume();
|
||||
} finally {
|
||||
if (!c.isCompleted) c.complete();
|
||||
}
|
||||
})..start();
|
||||
|
||||
Future.delayed(timeout, () {
|
||||
obs.stop();
|
||||
if (!c.isCompleted) c.complete();
|
||||
});
|
||||
|
||||
return c.future;
|
||||
}
|
||||
59
lib/app/core/navigation/app_router.dart
Normal file
59
lib/app/core/navigation/app_router.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:frontend_eccp_mobile/app/core/constants/globalkey.dart';
|
||||
import 'package:frontend_eccp_mobile/app/core/navigation/app_routes.dart';
|
||||
import 'package:frontend_eccp_mobile/app/core/navigation/main_navigation_page.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/auth/login/presentation/screen/login_page.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/splash/presentation/screen/splash_screen.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/update/presentation/update_page.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class AppRouter {
|
||||
AppRouter._();
|
||||
|
||||
static final router = GoRouter(
|
||||
initialLocation: AppRoutes.splash,
|
||||
|
||||
navigatorKey: navigatorKey,
|
||||
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: AppRoutes.splash,
|
||||
builder: (context, state) => const SplashScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.updateApp,
|
||||
builder: (context, state) {
|
||||
final extra = state.extra as Map<String, dynamic>?;
|
||||
|
||||
final version = extra?['version'] as String? ?? '';
|
||||
final link = extra?['link'] as String? ?? '';
|
||||
|
||||
return UpdatePage(
|
||||
version: version,
|
||||
link: link,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.login,
|
||||
builder: (context, state) => const LoginPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.home,
|
||||
builder: (context, state) => const MainNavigationPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: AppRoutes.berkas,
|
||||
builder: (context, state) => const MainNavigationPage(
|
||||
initialIndex: 1,
|
||||
),
|
||||
),
|
||||
|
||||
GoRoute(
|
||||
path: AppRoutes.settings,
|
||||
builder: (context, state) => const MainNavigationPage(
|
||||
initialIndex: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
10
lib/app/core/navigation/app_routes.dart
Normal file
10
lib/app/core/navigation/app_routes.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
class AppRoutes {
|
||||
AppRoutes._();
|
||||
static const splash = '/';
|
||||
static const login = '/login';
|
||||
static const updateApp = '/update-app';
|
||||
|
||||
static const home = '/home';
|
||||
static const settings = '/settings';
|
||||
static const berkas = '/berkas';
|
||||
}
|
||||
52
lib/app/core/navigation/main_navigation_page.dart
Normal file
52
lib/app/core/navigation/main_navigation_page.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend_eccp_mobile/app/core/widgets/app_bottom_nav.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/berkas/presentation/screen/berkas_page.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/home/presentation/screen/home_page.dart';
|
||||
import 'package:frontend_eccp_mobile/modules/profile/presentation/screen/profile_page.dart';
|
||||
|
||||
class MainNavigationPage extends StatefulWidget {
|
||||
const MainNavigationPage({
|
||||
super.key,
|
||||
this.initialIndex = 0,
|
||||
});
|
||||
|
||||
final int initialIndex;
|
||||
|
||||
@override
|
||||
State<MainNavigationPage> createState() => _MainNavigationPageState();
|
||||
}
|
||||
|
||||
class _MainNavigationPageState extends State<MainNavigationPage> {
|
||||
late int _currentIndex;
|
||||
|
||||
final List<Widget> _pages = [
|
||||
const HomePage(),
|
||||
const BerkasPage(),
|
||||
const ProfilePage(),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentIndex = widget.initialIndex;
|
||||
}
|
||||
|
||||
void _onTabChanged(int index) {
|
||||
if (_currentIndex == index) return;
|
||||
setState(() => _currentIndex = index);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: IndexedStack(
|
||||
index: _currentIndex,
|
||||
children: _pages,
|
||||
),
|
||||
bottomNavigationBar: AppBottomNav(
|
||||
currentIndex: _currentIndex,
|
||||
onTap: _onTabChanged,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
51
lib/app/core/navigation/settings_navigator.dart
Normal file
51
lib/app/core/navigation/settings_navigator.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:android_intent_plus/android_intent.dart';
|
||||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:permission_handler/permission_handler.dart' as ph;
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SettingsNavigator {
|
||||
SettingsNavigator._();
|
||||
static Future<bool> openAppSettings() async {
|
||||
try {
|
||||
final ok = await Geolocator.openAppSettings();
|
||||
if (ok) return true;
|
||||
} catch (_) {}
|
||||
try {
|
||||
final ok = await ph.openAppSettings();
|
||||
if (ok) return true;
|
||||
} catch (_) {}
|
||||
try {
|
||||
await AppSettings.openAppSettings();
|
||||
return true;
|
||||
} catch (_) {}
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
final uri = Uri.parse('app-settings:');
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
return true;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<bool> openLocationSettings() async {
|
||||
if (Platform.isIOS) return openAppSettings();
|
||||
try {
|
||||
final ok = await Geolocator.openLocationSettings();
|
||||
if (ok) return true;
|
||||
} catch (_) {}
|
||||
try {
|
||||
const i = AndroidIntent(
|
||||
action: 'android.settings.LOCATION_SOURCE_SETTINGS',
|
||||
);
|
||||
await i.launch();
|
||||
return true;
|
||||
} catch (_) {}
|
||||
return openAppSettings();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user