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

View File

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

View File

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