🎨 Use feature based folder structure
This commit is contained in:
247
lib/settings/tabs_screen.dart
Normal file
247
lib/settings/tabs_screen.dart
Normal file
@@ -0,0 +1,247 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/accounts/accounts_pod.dart';
|
||||
import 'package:island/core/services/responsive.dart';
|
||||
import 'package:island/drive/drive_widgets/cloud_files.dart';
|
||||
import 'package:island/core/navigation/conditional_bottom_nav.dart';
|
||||
import 'package:island/notifications/notification.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:island/chat/chat_pod/chat_summary.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
final currentRouteProvider = NotifierProvider<CurrentRouteNotifier, String?>(
|
||||
CurrentRouteNotifier.new,
|
||||
);
|
||||
|
||||
class CurrentRouteNotifier extends Notifier<String?> {
|
||||
@override
|
||||
String? build() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void updateRoute(String? route) {
|
||||
state = route;
|
||||
}
|
||||
}
|
||||
|
||||
const kWideScreenRouteStart = 5;
|
||||
const kTabRoutes = [
|
||||
'/',
|
||||
'/explore',
|
||||
'/chat',
|
||||
'/realms',
|
||||
'/account',
|
||||
'/files',
|
||||
'/thought',
|
||||
'/creators',
|
||||
'/developers',
|
||||
];
|
||||
|
||||
class TabsScreen extends HookConsumerWidget {
|
||||
final Widget? child;
|
||||
const TabsScreen({super.key, this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// final useHorizontalLayout = isWideScreen(context);
|
||||
final currentLocation = GoRouterState.of(context).uri.toString();
|
||||
|
||||
// Update the current route provider whenever the location changes
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(currentRouteProvider.notifier).updateRoute(currentLocation);
|
||||
});
|
||||
return null;
|
||||
}, [currentLocation]);
|
||||
|
||||
final notificationUnreadCount = ref.watch(notificationUnreadCountProvider);
|
||||
|
||||
final chatUnreadCount = ref.watch(chatUnreadCountProvider);
|
||||
|
||||
final wideScreen = isWideScreen(context);
|
||||
|
||||
final destinations = [
|
||||
NavigationDestination(
|
||||
label: 'dashboard'.tr(),
|
||||
icon: const Icon(Symbols.dashboard_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'explore'.tr(),
|
||||
icon: const Icon(Symbols.explore_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'chat'.tr(),
|
||||
icon: Badge.count(
|
||||
count: chatUnreadCount.value ?? 0,
|
||||
isLabelVisible: (chatUnreadCount.value ?? 0) > 0,
|
||||
child: const Icon(Symbols.forum_rounded),
|
||||
),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'realms'.tr(),
|
||||
icon: const Icon(Symbols.groups_3),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'account'.tr(),
|
||||
icon: Badge.count(
|
||||
count: notificationUnreadCount.value ?? 0,
|
||||
isLabelVisible: (notificationUnreadCount.value ?? 0) > 0,
|
||||
child: Consumer(
|
||||
child: const Icon(Symbols.account_circle_rounded),
|
||||
builder: (context, ref, fallbackChild) {
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
if (userInfo.value?.profile.picture != null) {
|
||||
return ProfilePictureWidget(
|
||||
file: userInfo.value!.profile.picture,
|
||||
radius: 12,
|
||||
);
|
||||
}
|
||||
return fallbackChild!;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (wideScreen)
|
||||
...([
|
||||
NavigationDestination(
|
||||
label: 'files'.tr(),
|
||||
icon: const Icon(Symbols.folder_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'aiThought'.tr(),
|
||||
icon: const Icon(Symbols.bubble_chart),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'creatorHub'.tr(),
|
||||
icon: const Icon(Symbols.design_services_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'developerHub'.tr(),
|
||||
icon: const Icon(Symbols.data_object_rounded),
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
int getCurrentIndex() {
|
||||
if (currentLocation == '/') return 0;
|
||||
final idx = kTabRoutes.indexWhere(
|
||||
(p) => currentLocation.startsWith(p),
|
||||
1,
|
||||
);
|
||||
final value = math.max(idx, 0);
|
||||
return math.min(value, destinations.length - 1);
|
||||
}
|
||||
|
||||
void onDestinationSelected(int index) {
|
||||
context.go(kTabRoutes[index]);
|
||||
}
|
||||
|
||||
final currentIndex = getCurrentIndex();
|
||||
|
||||
if (isWideScreen(context)) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: Row(
|
||||
children: [
|
||||
NavigationRail(
|
||||
backgroundColor: Colors.transparent,
|
||||
destinations: destinations.mapIndexed((idx, d) {
|
||||
if (d.icon is Icon) {
|
||||
return NavigationRailDestination(
|
||||
icon: Icon(
|
||||
(d.icon as Icon).icon,
|
||||
fill: currentIndex == idx ? 1 : null,
|
||||
),
|
||||
label: Text(d.label),
|
||||
);
|
||||
}
|
||||
return NavigationRailDestination(
|
||||
icon: d.icon,
|
||||
label: Text(d.label),
|
||||
);
|
||||
}).toList(),
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
),
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
extendBody: true,
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
bottomNavigationBar: ConditionalBottomNav(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
topRight: Radius.circular(16),
|
||||
),
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeTop: true,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface.withOpacity(0.8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: NavigationBar(
|
||||
height: 56,
|
||||
destinations: destinations.mapIndexed((idx, d) {
|
||||
if (d.icon is Icon) {
|
||||
return NavigationDestination(
|
||||
icon: Icon(
|
||||
(d.icon as Icon).icon,
|
||||
fill: currentIndex == idx ? 1 : null,
|
||||
),
|
||||
label: d.label,
|
||||
);
|
||||
}
|
||||
return d;
|
||||
}).toList(),
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||
backgroundColor: Colors.transparent,
|
||||
indicatorColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withOpacity(0.2),
|
||||
).padding(horizontal: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user