♻️ Remove fab menu
This commit is contained in:
@@ -9,15 +9,16 @@ import 'package:island/models/chat.dart';
|
||||
import 'package:island/pods/chat/chat_summary.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/screens/chat/chat_form.dart';
|
||||
import 'package:island/screens/realm/realms.dart';
|
||||
import 'package:island/services/event_bus.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/account/account_picker.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/chat_room_widgets.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:island/widgets/response.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
@@ -372,23 +373,6 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
};
|
||||
}, [tabController]);
|
||||
|
||||
useEffect(() {
|
||||
// Set FAB type to chat
|
||||
final fabMenuNotifier = ref.read(fabMenuTypeProvider.notifier);
|
||||
Future(() {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.chat);
|
||||
});
|
||||
return () {
|
||||
// Clean up: reset FAB type to main
|
||||
final fabMenu = ref.read(fabMenuTypeProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (fabMenu == FabMenuType.chat) {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.main);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (isAside) {
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
@@ -461,8 +445,74 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
|
||||
final appbarFeColor = Theme.of(context).appBarTheme.foregroundColor;
|
||||
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
|
||||
return AppScaffold(
|
||||
extendBody: false, // Prevent conflicts with tabs navigation
|
||||
floatingActionButton: userInfo.value != null
|
||||
? FloatingActionButton(
|
||||
child: const Icon(Symbols.add),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useRootNavigator: true,
|
||||
builder: (context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Gap(40),
|
||||
ListTile(
|
||||
title: const Text('createChatRoom').tr(),
|
||||
leading: const Icon(Symbols.add),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => const EditChatScreen(),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
eventBus.fire(const ChatRoomsRefreshEvent());
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('createDirectMessage').tr(),
|
||||
leading: const Icon(Symbols.person),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
onTap: () async {
|
||||
final result = await showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => const AccountPickerSheet(),
|
||||
);
|
||||
if (result == null) return;
|
||||
final client = ref.read(apiClientProvider);
|
||||
try {
|
||||
await client.post(
|
||||
'/sphere/chat/direct',
|
||||
data: {'related_user_id': result.id},
|
||||
);
|
||||
eventBus.fire(const ChatRoomsRefreshEvent());
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
}
|
||||
},
|
||||
),
|
||||
const Gap(16),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).padding(bottom: isWideScreen(context) ? null : 56)
|
||||
: null,
|
||||
appBar: AppBar(
|
||||
flexibleSpace: Container(
|
||||
height: 48,
|
||||
|
||||
@@ -19,8 +19,8 @@ import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/widgets/extended_refresh_indicator.dart';
|
||||
import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:island/widgets/post/compose_sheet.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/post_item_skeleton.dart';
|
||||
import 'package:island/widgets/post/post_list.dart';
|
||||
@@ -46,24 +46,6 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
final selectedTagIds = useState<List<String>>([]);
|
||||
final notifier = ref.watch(activityListProvider.notifier);
|
||||
|
||||
useEffect(() {
|
||||
// Set FAB type to chat
|
||||
|
||||
final fabMenuNotifier = ref.read(fabMenuTypeProvider.notifier);
|
||||
Future(() {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.compose);
|
||||
});
|
||||
return () {
|
||||
// Clean up: reset FAB type to main
|
||||
final fabMenu = ref.read(fabMenuTypeProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (fabMenu == FabMenuType.compose) {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.main);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
void handleFilterChange(String? filter) {
|
||||
currentFilter.value = filter;
|
||||
notifier.applyFilter(filter);
|
||||
@@ -200,6 +182,8 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
).padding(horizontal: 8, vertical: 4),
|
||||
);
|
||||
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
|
||||
final appBar = isWide
|
||||
? null
|
||||
: _buildAppBar(
|
||||
@@ -230,6 +214,49 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
AppScaffold(
|
||||
isNoBackground: false,
|
||||
appBar: appBar,
|
||||
floatingActionButton: userInfo.value != null
|
||||
? FloatingActionButton(
|
||||
child: const Icon(Symbols.create),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useRootNavigator: true,
|
||||
builder: (context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Gap(40),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
leading: const Icon(Symbols.post_add_rounded),
|
||||
title: Text('postCompose').tr(),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await PostComposeSheet.show(context);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
leading: const Icon(Symbols.article),
|
||||
title: Text('articleCompose').tr(),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
GoRouter.of(
|
||||
context,
|
||||
).pushNamed('articleCompose');
|
||||
},
|
||||
),
|
||||
const Gap(16),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).padding(bottom: isWideScreen(context) ? null : 56)
|
||||
: null,
|
||||
body: isWide
|
||||
? _buildWideBody(
|
||||
context,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
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:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/realm.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:island/widgets/response.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
@@ -43,22 +43,7 @@ class RealmListScreen extends HookConsumerWidget {
|
||||
final realms = ref.watch(realmsJoinedProvider);
|
||||
final realmInvites = ref.watch(realmInvitesProvider);
|
||||
|
||||
useEffect(() {
|
||||
// Set FAB type to realm
|
||||
final fabMenuNotifier = ref.read(fabMenuTypeProvider.notifier);
|
||||
Future(() {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.realm);
|
||||
});
|
||||
return () {
|
||||
// Clean up: reset FAB type to main
|
||||
final fabMenu = ref.read(fabMenuTypeProvider);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (fabMenu == FabMenuType.realm) {
|
||||
fabMenuNotifier.setMenuType(FabMenuType.main);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
|
||||
return AppScaffold(
|
||||
isNoBackground: false,
|
||||
@@ -98,6 +83,41 @@ class RealmListScreen extends HookConsumerWidget {
|
||||
const Gap(8),
|
||||
],
|
||||
),
|
||||
floatingActionButton: userInfo.value != null
|
||||
? FloatingActionButton(
|
||||
child: const Icon(Symbols.add),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useRootNavigator: true,
|
||||
builder: (context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Gap(40),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
leading: const Icon(Symbols.group_add),
|
||||
title: Text('createRealm').tr(),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
context.pushNamed('realmNew').then((value) {
|
||||
if (value != null) {
|
||||
// Fire realm refresh event if needed
|
||||
// eventBus.fire(const RealmsRefreshEvent());
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const Gap(16),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).padding(bottom: isWideScreen(context) ? null : 56)
|
||||
: null,
|
||||
body: ExtendedRefreshIndicator(
|
||||
child: realms.when(
|
||||
data: (value) => Column(
|
||||
|
||||
@@ -407,46 +407,6 @@ class SettingsScreen extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
|
||||
// FAB position settings
|
||||
ListTile(
|
||||
minLeadingWidth: 48,
|
||||
title: Text('fabLocation').tr(),
|
||||
contentPadding: const EdgeInsets.only(left: 24, right: 17),
|
||||
leading: const Icon(Symbols.adjust),
|
||||
trailing: DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<String>(
|
||||
isExpanded: true,
|
||||
items: [
|
||||
DropdownMenuItem<String>(
|
||||
value: 'left',
|
||||
child: Text('Left').fontSize(14),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'center',
|
||||
child: Text('Center').fontSize(14),
|
||||
),
|
||||
DropdownMenuItem<String>(
|
||||
value: 'right',
|
||||
child: Text('Right').fontSize(14),
|
||||
),
|
||||
],
|
||||
value: settings.fabPosition,
|
||||
onChanged: (String? value) {
|
||||
if (value != null) {
|
||||
ref.read(appSettingsProvider.notifier).setFabPosition(value);
|
||||
showSnackBar('settingsApplied'.tr());
|
||||
}
|
||||
},
|
||||
buttonStyleData: const ButtonStyleData(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5),
|
||||
height: 40,
|
||||
width: 120,
|
||||
),
|
||||
menuItemStyleData: const MenuItemStyleData(height: 40),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Card background opacity settings
|
||||
ListTile(
|
||||
isThreeLine: true,
|
||||
|
||||
@@ -10,10 +10,7 @@ import 'package:island/screens/notification.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/navigation/conditional_bottom_nav.dart';
|
||||
import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/chat/chat_summary.dart';
|
||||
|
||||
final currentRouteProvider = NotifierProvider<CurrentRouteNotifier, String?>(
|
||||
@@ -31,13 +28,13 @@ class CurrentRouteNotifier extends Notifier<String?> {
|
||||
}
|
||||
}
|
||||
|
||||
const kWideScreenRouteStart = 4;
|
||||
const kWideScreenRouteStart = 5;
|
||||
const kTabRoutes = [
|
||||
'/',
|
||||
'/explore',
|
||||
'/chat',
|
||||
'/account',
|
||||
'/realms',
|
||||
'/account',
|
||||
'/files',
|
||||
'/thought',
|
||||
'/creators',
|
||||
@@ -84,7 +81,10 @@ class TabsScreen extends HookConsumerWidget {
|
||||
child: const Icon(Symbols.forum_rounded),
|
||||
),
|
||||
),
|
||||
|
||||
NavigationDestination(
|
||||
label: 'realms'.tr(),
|
||||
icon: const Icon(Symbols.group_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'account'.tr(),
|
||||
icon: Badge.count(
|
||||
@@ -107,10 +107,6 @@ class TabsScreen extends HookConsumerWidget {
|
||||
),
|
||||
if (wideScreen)
|
||||
...([
|
||||
NavigationDestination(
|
||||
label: 'realms'.tr(),
|
||||
icon: const Icon(Symbols.group_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'files'.tr(),
|
||||
icon: const Icon(Symbols.folder_rounded),
|
||||
@@ -146,13 +142,6 @@ class TabsScreen extends HookConsumerWidget {
|
||||
|
||||
final currentIndex = getCurrentIndex();
|
||||
|
||||
final routes = kTabRoutes.sublist(
|
||||
0,
|
||||
isWideScreen(context) ? null : kWideScreenRouteStart,
|
||||
);
|
||||
final shouldShowFab = routes.contains(currentLocation) && !wideScreen;
|
||||
final settings = ref.watch(appSettingsProvider);
|
||||
|
||||
if (isWideScreen(context)) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
@@ -170,10 +159,6 @@ class TabsScreen extends HookConsumerWidget {
|
||||
.toList(),
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
trailingAtBottom: true,
|
||||
trailing: const FabMenu(
|
||||
elevation: 0,
|
||||
).padding(bottom: MediaQuery.of(context).padding.bottom + 16),
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
@@ -199,10 +184,6 @@ class TabsScreen extends HookConsumerWidget {
|
||||
),
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
floatingActionButton: shouldShowFab ? const FabMenu() : null,
|
||||
floatingActionButtonLocation: shouldShowFab
|
||||
? _DockedFabLocation(context, settings.fabPosition)
|
||||
: null,
|
||||
bottomNavigationBar: ConditionalBottomNav(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
@@ -225,47 +206,18 @@ class TabsScreen extends HookConsumerWidget {
|
||||
),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: BottomAppBar(
|
||||
child: NavigationBar(
|
||||
height: 56,
|
||||
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||
shape: AutomaticNotchedShape(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surface.withOpacity(0.8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: () {
|
||||
final navItems = destinations.asMap().entries.map<Widget>(
|
||||
(entry) {
|
||||
int index = entry.key;
|
||||
NavigationDestination dest = entry.value;
|
||||
return IconButton(
|
||||
icon: dest.icon,
|
||||
onPressed: () => onDestinationSelected(index),
|
||||
color: index == currentIndex
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
);
|
||||
},
|
||||
).toList();
|
||||
// Add mock item to leave space for FAB based on position
|
||||
final gapIndex = switch (settings.fabPosition) {
|
||||
'left' => 0,
|
||||
'right' => navItems.length,
|
||||
_ => navItems.length ~/ 2, // center
|
||||
};
|
||||
navItems.insert(
|
||||
gapIndex,
|
||||
SizedBox(
|
||||
width: settings.fabPosition == 'center' ? 72 : 48,
|
||||
),
|
||||
);
|
||||
return navItems;
|
||||
}(),
|
||||
),
|
||||
destinations: destinations,
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: onDestinationSelected,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withOpacity(0.8),
|
||||
indicatorColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -275,40 +227,3 @@ class TabsScreen extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DockedFabLocation extends FloatingActionButtonLocation {
|
||||
final BuildContext context;
|
||||
final String fabPosition;
|
||||
|
||||
const _DockedFabLocation(this.context, this.fabPosition);
|
||||
|
||||
@override
|
||||
Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final safeAreaPadding = mediaQuery.padding;
|
||||
|
||||
// Position horizontally based on setting
|
||||
final double fabX = switch (fabPosition) {
|
||||
'left' => scaffoldGeometry.minInsets.left + 24,
|
||||
'right' =>
|
||||
scaffoldGeometry.scaffoldSize.width -
|
||||
scaffoldGeometry.floatingActionButtonSize.width -
|
||||
scaffoldGeometry.minInsets.right -
|
||||
24,
|
||||
_ =>
|
||||
(scaffoldGeometry.scaffoldSize.width -
|
||||
scaffoldGeometry.floatingActionButtonSize.width) /
|
||||
2, // center
|
||||
};
|
||||
|
||||
// Position closer to bottom with reduced padding
|
||||
final double fabY =
|
||||
scaffoldGeometry.scaffoldSize.height -
|
||||
scaffoldGeometry.floatingActionButtonSize.height -
|
||||
scaffoldGeometry.bottomSheetSize.height -
|
||||
safeAreaPadding.bottom -
|
||||
16;
|
||||
|
||||
return Offset(fabX, fabY);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user