import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:solian/models/realm.dart'; import 'package:solian/providers/auth.dart'; import 'package:solian/providers/content/channel.dart'; import 'package:solian/providers/content/realm.dart'; import 'package:solian/providers/navigation.dart'; import 'package:solian/services.dart'; import 'package:solian/widgets/account/account_avatar.dart'; import 'package:solian/widgets/auto_cache_image.dart'; import 'package:solian/widgets/channel/channel_list.dart'; class AppNavigationRegion extends StatefulWidget { final bool isCollapsed; final Function onSelected; const AppNavigationRegion({ super.key, this.isCollapsed = false, required this.onSelected, }); @override State createState() => _AppNavigationRegionState(); } class _AppNavigationRegionState extends State { bool _isTryingExit = false; void _focusRealm(Realm item) { setState( () => Get.find().focusedRealm.value = item, ); } void _unFocusRealm() { setState( () => Get.find().focusedRealm.value = null, ); } @override void dispose() { super.dispose(); } Widget _buildRealmFocusAvatar() { final focusedRealm = Get.find().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(), ); } 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, ), ); } Widget _buildEntry(BuildContext context, Realm item) { const padding = EdgeInsets.symmetric(horizontal: 20, vertical: 8); if (widget.isCollapsed) { return InkWell( child: _buildEntryAvatar(item).paddingSymmetric(vertical: 8), onTap: () => _focusRealm(item), ); } return ListTile( minTileHeight: 0, leading: _buildEntryAvatar(item), contentPadding: padding, title: Text(item.name), subtitle: Text( item.description, maxLines: 1, overflow: TextOverflow.ellipsis, ), onTap: () => _focusRealm(item), ); } @override Widget build(BuildContext context) { final RealmProvider realms = Get.find(); final ChannelProvider channels = Get.find(); final AuthProvider auth = Get.find(); final NavigationStateProvider navState = Get.find(); return Obx( () => PageTransitionSwitcher( transitionBuilder: (child, animation, secondaryAnimation) { return SharedAxisTransition( animation: animation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.horizontal, 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 && (navState.focusedRealm.value!.banner?.isNotEmpty ?? false)) AspectRatio( aspectRatio: 16 / 7, child: AutoCacheImage( ServiceFinder.buildUrl( 'uc', '/attachments/${navState.focusedRealm.value!.banner}', ), fit: BoxFit.cover, ), ), 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, onSelected: (_) => widget.onSelected(), ), ), ), ], ), ), ); } }