Solian/lib/widgets/navigation/app_navigation_region.dart

219 lines
7.1 KiB
Dart
Raw Normal View History

2024-08-07 10:24:16 +00:00
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart';
2024-08-07 10:24:16 +00:00
import 'package:solian/providers/content/channel.dart';
import 'package:solian/providers/content/realm.dart';
import 'package:solian/providers/navigation.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/channel/channel_list.dart';
2024-08-07 10:24:16 +00:00
class AppNavigationRegion extends StatefulWidget {
2024-08-21 09:00:59 +00:00
final bool isCollapsed;
2024-09-15 10:25:04 +00:00
final Function onSelected;
2024-08-07 10:24:16 +00:00
2024-08-21 11:11:27 +00:00
const AppNavigationRegion({
2024-08-21 09:00:59 +00:00
super.key,
this.isCollapsed = false,
2024-09-15 10:25:04 +00:00
required this.onSelected,
2024-08-21 09:00:59 +00:00
});
2024-08-07 10:24:16 +00:00
@override
State<AppNavigationRegion> createState() => _AppNavigationRegionState();
}
class _AppNavigationRegionState extends State<AppNavigationRegion> {
bool _isTryingExit = false;
void _focusRealm(Realm item) {
setState(
() => Get.find<NavigationStateProvider>().focusedRealm.value = item,
);
}
void _unFocusRealm() {
setState(
() => Get.find<NavigationStateProvider>().focusedRealm.value = null,
);
}
@override
void dispose() {
super.dispose();
}
Widget _buildRealmFocusAvatar() {
final focusedRealm = Get.find<NavigationStateProvider>().focusedRealm.value;
return GestureDetector(
child: MouseRegion(
child: AnimatedSwitcher(
switchInCurve: Curves.fastOutSlowIn,
switchOutCurve: Curves.fastOutSlowIn,
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: _isTryingExit
? CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primary,
child: const Icon(
Icons.arrow_back,
color: Colors.white,
size: 16,
),
).paddingSymmetric(
vertical: 8,
)
: _buildEntryAvatar(focusedRealm!),
),
onEnter: (_) => setState(() => _isTryingExit = true),
onExit: (_) => setState(() => _isTryingExit = false),
),
onTap: () => _unFocusRealm(),
2024-08-07 10:24:16 +00:00
);
}
2024-08-07 10:24:16 +00:00
Widget _buildEntryAvatar(Realm item) {
return Hero(
tag: Key('region-realm-avatar-${item.id}'),
child: (item.avatar?.isNotEmpty ?? false)
? AccountAvatar(content: item.avatar)
: CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primary,
child: const Icon(
Icons.workspaces,
color: Colors.white,
size: 16,
),
).paddingSymmetric(
vertical: 8,
),
);
2024-08-07 10:24:16 +00:00
}
Widget _buildEntry(BuildContext context, Realm item) {
const padding = EdgeInsets.symmetric(horizontal: 20, vertical: 8);
2024-08-07 10:24:16 +00:00
if (widget.isCollapsed) {
2024-08-21 09:00:59 +00:00
return InkWell(
child: _buildEntryAvatar(item).paddingSymmetric(vertical: 8),
onTap: () => _focusRealm(item),
2024-08-21 09:00:59 +00:00
);
}
2024-08-07 10:24:16 +00:00
return ListTile(
minTileHeight: 0,
leading: _buildEntryAvatar(item),
2024-08-07 10:24:16 +00:00
contentPadding: padding,
title: Text(item.name),
subtitle: Text(
item.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onTap: () => _focusRealm(item),
2024-08-07 10:24:16 +00:00
);
}
@override
Widget build(BuildContext context) {
final RealmProvider realms = Get.find();
2024-08-07 10:24:16 +00:00
final ChannelProvider channels = Get.find();
final AuthProvider auth = Get.find();
final NavigationStateProvider navState = Get.find();
return Obx(
() => AnimatedSwitcher(
switchInCurve: Curves.fastOutSlowIn,
switchOutCurve: Curves.fastOutSlowIn,
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: Material(
color: Theme.of(context).colorScheme.surface,
child: child,
),
);
},
child: navState.focusedRealm.value == null
? widget.isCollapsed
? CustomScrollView(
slivers: [
const SliverPadding(padding: EdgeInsets.only(top: 16)),
SliverList.builder(
itemCount: realms.availableRealms.length,
itemBuilder: (context, index) {
final element = realms.availableRealms[index];
return Tooltip(
message: element.name,
child: _buildEntry(context, element),
);
},
),
],
)
: CustomScrollView(
slivers: [
SliverList.builder(
itemCount: realms.availableRealms.length,
itemBuilder: (context, index) {
final element = realms.availableRealms[index];
return _buildEntry(context, element);
},
),
],
)
: Column(
children: [
if (widget.isCollapsed)
Tooltip(
message: navState.focusedRealm.value!.name,
child: _buildRealmFocusAvatar().paddingOnly(
top: 24,
bottom: 8,
),
)
else
ListTile(
minTileHeight: 0,
tileColor:
Theme.of(context).colorScheme.surfaceContainerLow,
leading: _buildRealmFocusAvatar(),
contentPadding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 8),
title: Text(navState.focusedRealm.value!.name),
subtitle: Text(
navState.focusedRealm.value!.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Expanded(
child: Obx(
() => ChannelListWidget(
useReplace: true,
channels: channels.availableChannels
.where((x) =>
x.realm?.id == navState.focusedRealm.value?.id)
.toList(),
isCollapsed: widget.isCollapsed,
selfId: auth.userProfile.value!['id'],
noCategory: true,
2024-09-15 10:25:04 +00:00
onSelected: (_) => widget.onSelected(),
),
),
),
],
),
),
);
2024-08-07 10:24:16 +00:00
}
}