💄 Redesign explore

This commit is contained in:
2025-09-28 23:07:22 +08:00
parent 5b62f89531
commit 22bf6d1c33
3 changed files with 448 additions and 417 deletions

View File

@@ -51,7 +51,6 @@ Widget notificationIndicatorWidget(
], ],
), ),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
minTileHeight: 40,
contentPadding: EdgeInsets.only(left: 16, right: 15), contentPadding: EdgeInsets.only(left: 16, right: 15),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('notifications'); GoRouter.of(context).pushNamed('notifications');
@@ -99,10 +98,6 @@ class ExploreScreen extends HookConsumerWidget {
final events = ref.watch(eventCalendarProvider(query.value)); final events = ref.watch(eventCalendarProvider(query.value));
final selectedDay = useState(now); final selectedDay = useState(now);
// Function to handle day selection for synchronizing between widgets
void onDaySelected(DateTime day) {
selectedDay.value = day;
}
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
@@ -110,12 +105,10 @@ class ExploreScreen extends HookConsumerWidget {
notificationUnreadCountNotifierProvider, notificationUnreadCountNotifierProvider,
); );
return AppScaffold( final isWide = isWideScreen(context);
isNoBackground: false,
appBar: AppBar( final filterBar = Card(
toolbarHeight: 0, margin: EdgeInsets.zero,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@@ -130,10 +123,7 @@ class ExploreScreen extends HookConsumerWidget {
message: 'explore'.tr(), message: 'explore'.tr(),
child: Icon( child: Icon(
Symbols.explore, Symbols.explore,
color: color: Theme.of(context).appBarTheme.foregroundColor!,
Theme.of(
context,
).appBarTheme.foregroundColor!,
), ),
), ),
), ),
@@ -142,10 +132,7 @@ class ExploreScreen extends HookConsumerWidget {
message: 'exploreFilterSubscriptions'.tr(), message: 'exploreFilterSubscriptions'.tr(),
child: Icon( child: Icon(
Symbols.subscriptions, Symbols.subscriptions,
color: color: Theme.of(context).appBarTheme.foregroundColor!,
Theme.of(
context,
).appBarTheme.foregroundColor!,
), ),
), ),
), ),
@@ -154,10 +141,7 @@ class ExploreScreen extends HookConsumerWidget {
message: 'exploreFilterFriends'.tr(), message: 'exploreFilterFriends'.tr(),
child: Icon( child: Icon(
Symbols.people, Symbols.people,
color: color: Theme.of(context).appBarTheme.foregroundColor!,
Theme.of(
context,
).appBarTheme.foregroundColor!,
), ),
), ),
), ),
@@ -233,14 +217,11 @@ class ExploreScreen extends HookConsumerWidget {
tooltip: 'search'.tr(), tooltip: 'search'.tr(),
), ),
], ],
) ).padding(horizontal: 8),
.padding(horizontal: 8) );
.border(
bottom: 1 / MediaQuery.of(context).devicePixelRatio, return AppScaffold(
color: Theme.of(context).dividerColor, isNoBackground: false,
),
),
),
floatingActionButton: InkWell( floatingActionButton: InkWell(
onLongPress: () { onLongPress: () {
context.pushNamed('postCompose', queryParameters: {'type': '1'}).then( context.pushNamed('postCompose', queryParameters: {'type': '1'}).then(
@@ -264,20 +245,83 @@ class ExploreScreen extends HookConsumerWidget {
), ),
), ),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: Builder( body:
builder: (context) { isWide
final isWide = isWideScreen(context); ? _buildWideBody(
final bodyView = _buildActivityList(
context, context,
ref, ref,
filterBar,
user,
notificationCount,
query,
events,
selectedDay,
currentFilter.value, currentFilter.value,
)
: _buildNarrowBody(context, ref, filterBar, currentFilter.value),
);
}
Widget _buildActivityList(
BuildContext context,
WidgetRef ref,
String? filter,
) {
final activitiesNotifier = ref.watch(
activityListNotifierProvider(filter).notifier,
);
final isWide = isWideScreen(context);
return PagingHelperSliverView(
provider: activityListNotifierProvider(filter),
futureRefreshable: activityListNotifierProvider(filter).future,
notifierRefreshable: activityListNotifierProvider(filter).notifier,
contentBuilder:
(data, widgetCount, endItemView) => _ActivityListView(
data: data,
widgetCount: widgetCount,
endItemView: endItemView,
activitiesNotifier: activitiesNotifier,
isWide: isWide,
),
);
}
Widget _buildWideBody(
BuildContext context,
WidgetRef ref,
Widget filterBar,
AsyncValue<dynamic> user,
AsyncValue<int?> notificationCount,
ValueNotifier<EventCalendarQuery> query,
AsyncValue<List<dynamic>> events,
ValueNotifier<DateTime> selectedDay,
String? currentFilter,
) {
final bodyView = _buildActivityList(context, ref, currentFilter);
final activitiesNotifier = ref.watch(
activityListNotifierProvider(currentFilter).notifier,
); );
if (isWide) {
return Row( return Row(
spacing: 12,
children: [ children: [
Flexible(flex: 3, child: bodyView.padding(left: 8)), Flexible(
flex: 3,
child: ExtendedRefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
child: CustomScrollView(
slivers: [
const SliverGap(12),
SliverToBoxAdapter(child: filterBar),
const SliverGap(8),
bodyView,
],
),
),
),
if (user.value != null) if (user.value != null)
Flexible( Flexible(
flex: 2, flex: 2,
@@ -285,17 +329,12 @@ class ExploreScreen extends HookConsumerWidget {
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
spacing: 8,
children: [ children: [
CheckInWidget( CheckInWidget(
margin: EdgeInsets.only( margin: EdgeInsets.only(top: 12),
left: 8,
right: 12,
top: 16,
),
onChecked: () { onChecked: () {
ref.invalidate( ref.invalidate(eventCalendarProvider(query.value));
eventCalendarProvider(query.value),
);
}, },
), ),
if (notificationCount.value != null && if (notificationCount.value != null &&
@@ -303,26 +342,16 @@ class ExploreScreen extends HookConsumerWidget {
notificationIndicatorWidget( notificationIndicatorWidget(
context, context,
count: notificationCount.value ?? 0, count: notificationCount.value ?? 0,
margin: EdgeInsets.only( margin: EdgeInsets.zero,
left: 8,
right: 12,
top: 8,
),
),
PostFeaturedList().padding(
left: 8,
right: 12,
top: 8,
), ),
PostFeaturedList(),
FortuneGraphWidget( FortuneGraphWidget(
margin: EdgeInsets.only( margin: EdgeInsets.zero,
left: 8, events: events as AsyncValue<List<SnEventCalendarEntry>>,
right: 12,
top: 8,
),
events: events,
constrainWidth: true, constrainWidth: true,
onPointSelected: onDaySelected, onPointSelected: (DateTime day) {
selectedDay.value = day;
},
), ),
], ],
), ),
@@ -349,43 +378,66 @@ class ExploreScreen extends HookConsumerWidget {
).padding(horizontal: 36, vertical: 16), ).padding(horizontal: 36, vertical: 16),
), ),
], ],
); ).padding(horizontal: 12);
} }
return bodyView; Widget _buildNarrowBody(
},
),
);
}
Widget _buildActivityList(
BuildContext context, BuildContext context,
WidgetRef ref, WidgetRef ref,
String? filter, Widget filterBar,
String? currentFilter,
) { ) {
final activitiesNotifier = ref.watch( final user = ref.watch(userInfoProvider);
activityListNotifierProvider(filter).notifier, final notificationCount = ref.watch(
notificationUnreadCountNotifierProvider,
); );
final isWide = isWideScreen(context); final activitiesNotifier = ref.watch(
activityListNotifierProvider(currentFilter).notifier,
);
return ExtendedRefreshIndicator( final bodyView = _buildActivityList(context, ref, currentFilter);
return Column(
spacing: 8,
children: [
filterBar.padding(horizontal: 8, top: 8),
Expanded(
child: ExtendedRefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh), onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
child: PagingHelperView( child: ClipRRect(
provider: activityListNotifierProvider(filter), borderRadius: const BorderRadius.all(Radius.circular(8)),
futureRefreshable: activityListNotifierProvider(filter).future, child: CustomScrollView(
notifierRefreshable: activityListNotifierProvider(filter).notifier, slivers: [
contentBuilder: if (user.value != null)
(data, widgetCount, endItemView) => Center( SliverToBoxAdapter(
child: _ActivityListView( child: CheckInWidget(
data: data, margin: const EdgeInsets.only(bottom: 8),
widgetCount: widgetCount,
endItemView: endItemView,
activitiesNotifier: activitiesNotifier,
contentOnly: isWide || filter != null,
), ),
), ),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: PostFeaturedList(),
), ),
),
if (notificationCount.value != null &&
notificationCount.value! > 0)
SliverToBoxAdapter(
child: notificationIndicatorWidget(
context,
count: notificationCount.value ?? 0,
margin: const EdgeInsets.only(bottom: 8),
),
),
bodyView,
SliverGap(getTabbedPadding(context).bottom),
],
),
).padding(horizontal: 8),
),
),
],
); );
} }
} }
@@ -464,7 +516,7 @@ class _DiscoveryActivityItem extends StatelessWidget {
}; };
return Card( return Card(
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), margin: EdgeInsets.zero,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -505,48 +557,22 @@ class _ActivityListView extends HookConsumerWidget {
final CursorPagingData<SnActivity> data; final CursorPagingData<SnActivity> data;
final int widgetCount; final int widgetCount;
final Widget endItemView; final Widget endItemView;
final bool contentOnly;
final ActivityListNotifier activitiesNotifier; final ActivityListNotifier activitiesNotifier;
final bool isWide;
const _ActivityListView({ const _ActivityListView({
required this.data, required this.data,
required this.widgetCount, required this.widgetCount,
required this.endItemView, required this.endItemView,
required this.activitiesNotifier, required this.activitiesNotifier,
this.contentOnly = false, required this.isWide,
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userInfoProvider); return SliverList.separated(
final notificationCount = ref.watch(
notificationUnreadCountNotifierProvider,
);
return CustomScrollView(
slivers: [
SliverGap(12),
if (user.value != null && !contentOnly)
SliverToBoxAdapter(
child: CheckInWidget(
margin: EdgeInsets.only(left: 8, right: 8, bottom: 4),
),
),
if (!contentOnly)
SliverToBoxAdapter(
child: PostFeaturedList().padding(horizontal: 8, bottom: 4, top: 4),
),
if (!contentOnly && (notificationCount.value ?? 0) > 0)
SliverToBoxAdapter(
child: notificationIndicatorWidget(
context,
count: notificationCount.value ?? 0,
margin: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
),
),
SliverList.builder(
itemCount: widgetCount, itemCount: widgetCount,
separatorBuilder: (_, _) => const Gap(8),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == widgetCount - 1) { if (index == widgetCount - 1) {
return endItemView; return endItemView;
@@ -574,10 +600,7 @@ class _ActivityListView extends HookConsumerWidget {
); );
}, },
); );
itemWidget = Card( itemWidget = Card(margin: EdgeInsets.zero, child: itemWidget);
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: itemWidget,
);
break; break;
case 'discovery': case 'discovery':
itemWidget = _DiscoveryActivityItem(data: item.data!); itemWidget = _DiscoveryActivityItem(data: item.data!);
@@ -588,9 +611,6 @@ class _ActivityListView extends HookConsumerWidget {
return itemWidget; return itemWidget;
}, },
),
SliverGap(getTabbedPadding(context).bottom),
],
); );
} }
} }

View File

@@ -68,9 +68,12 @@ class TabsScreen extends HookConsumerWidget {
final currentIndex = getCurrentIndex(); final currentIndex = getCurrentIndex();
if (isWideScreen(context)) { if (isWideScreen(context)) {
return Row( return Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Row(
children: [ children: [
NavigationRail( NavigationRail(
backgroundColor: Colors.transparent,
destinations: destinations:
destinations destinations
.map( .map(
@@ -83,15 +86,27 @@ class TabsScreen extends HookConsumerWidget {
selectedIndex: currentIndex, selectedIndex: currentIndex,
onDestinationSelected: onDestinationSelected, onDestinationSelected: onDestinationSelected,
), ),
const VerticalDivider(width: 1), Expanded(
Expanded(child: child ?? const SizedBox.shrink()), child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: child ?? const SizedBox.shrink(),
),
),
], ],
),
); );
} }
return Stack( return Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Stack(
children: [ children: [
Positioned.fill(child: child ?? const SizedBox.shrink()), Positioned.fill(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: child ?? const SizedBox.shrink(),
),
),
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
@@ -130,6 +145,7 @@ class TabsScreen extends HookConsumerWidget {
), ),
), ),
], ],
),
); );
} }
} }

View File

@@ -68,24 +68,14 @@ class WindowScaffold extends HookConsumerWidget {
if (!kIsWeb && if (!kIsWeb &&
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) { (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
return Material( return Material(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
Column( Column(
children: [ children: [
Container( DragToMoveArea(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor,
width: 1 / devicePixelRatio,
),
),
),
child: DragToMoveArea(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: mainAxisAlignment:
@@ -94,7 +84,8 @@ class WindowScaffold extends HookConsumerWidget {
: MainAxisAlignment.start, : MainAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: Platform.isMacOS child:
Platform.isMacOS
? Text( ? Text(
'Solar Network', 'Solar Network',
textAlign: TextAlign.center, textAlign: TextAlign.center,
@@ -102,7 +93,8 @@ class WindowScaffold extends HookConsumerWidget {
: Row( : Row(
children: [ children: [
Image.asset( Image.asset(
Theme.of(context).brightness == Brightness.dark Theme.of(context).brightness ==
Brightness.dark
? 'assets/icons/icon-dark.png' ? 'assets/icons/icon-dark.png'
: 'assets/icons/icon.png', : 'assets/icons/icon.png',
width: 20, width: 20,
@@ -127,7 +119,11 @@ class WindowScaffold extends HookConsumerWidget {
color: Theme.of(context).iconTheme.color, color: Theme.of(context).iconTheme.color,
), ),
IconButton( IconButton(
icon: Icon(isMaximized.value ? Symbols.fullscreen_exit : Symbols.fullscreen), icon: Icon(
isMaximized.value
? Symbols.fullscreen_exit
: Symbols.fullscreen,
),
onPressed: () async { onPressed: () async {
if (await windowManager.isMaximized()) { if (await windowManager.isMaximized()) {
windowManager.restore(); windowManager.restore();
@@ -152,7 +148,6 @@ class WindowScaffold extends HookConsumerWidget {
], ],
), ),
), ),
),
Expanded(child: child), Expanded(child: child),
], ],
), ),