feat(mobile): frosted glass navbar + channel detail polish (#404)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Wes
2026-04-26 19:09:14 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent a05565e687
commit 4fc720cc0d
19 changed files with 391 additions and 110 deletions
@@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../channels/channel.dart';
import '../channels/channel_detail_page.dart';
import '../channels/channels_provider.dart';
@@ -83,7 +85,16 @@ class ActivityPage extends HookConsumerWidget {
body = const _LoadingSkeleton();
}
return Scaffold(body: SafeArea(child: body));
return FrostedScaffold(
appBar: const FrostedAppBar(title: Text('Activity')),
body: SafeArea(
top: false,
child: Padding(
padding: EdgeInsets.only(top: frostedAppBarHeight(context)),
child: body,
),
),
);
}
List<FeedItem> _filteredItems(HomeFeedResponse feed, _Filter filter) {
@@ -7,6 +7,8 @@ import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/relay/relay.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../profile/presence_cache_provider.dart';
import '../profile/profile_provider.dart';
import '../profile/user_cache_provider.dart';
@@ -92,8 +94,8 @@ class ChannelDetailPage extends HookConsumerWidget {
return null;
}, [channel.id]);
return Scaffold(
appBar: AppBar(
return FrostedScaffold(
appBar: FrostedAppBar(
title: resolvedChannel.isDm
? _DmAppBarTitle(
channel: resolvedChannel,
@@ -108,11 +110,28 @@ class ChannelDetailPage extends HookConsumerWidget {
),
const SizedBox(width: Grid.half),
Expanded(
child: Text(
resolvedChannel.displayLabel(
currentPubkey: currentPubkey,
),
overflow: TextOverflow.ellipsis,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
resolvedChannel.displayLabel(
currentPubkey: currentPubkey,
),
overflow: TextOverflow.ellipsis,
),
if (resolvedChannel.isStream)
Text(
resolvedChannel.description.isNotEmpty
? resolvedChannel.description
: '${resolvedChannel.memberCount} member${resolvedChannel.memberCount == 1 ? '' : 's'}',
style: context.textTheme.bodySmall?.copyWith(
color: context.colors.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
@@ -153,9 +172,6 @@ class ChannelDetailPage extends HookConsumerWidget {
),
body: Column(
children: [
_DetailConnectionBanner(
status: ref.watch(relaySessionProvider).status,
),
Expanded(
child: resolvedChannel.isForum
? ForumPostsView(
@@ -163,13 +179,22 @@ class ChannelDetailPage extends HookConsumerWidget {
currentPubkey: currentPubkey,
)
: messagesState.when(
loading: () =>
const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(
child: Text(
'Failed to load messages',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
loading: () => Padding(
padding: EdgeInsets.only(
top: frostedAppBarHeight(context),
),
child: const Center(child: CircularProgressIndicator()),
),
error: (e, _) => Padding(
padding: EdgeInsets.only(
top: frostedAppBarHeight(context),
),
child: Center(
child: Text(
'Failed to load messages',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
),
),
),
),
@@ -190,6 +215,9 @@ class ChannelDetailPage extends HookConsumerWidget {
},
),
),
_DetailConnectionBanner(
status: ref.watch(relaySessionProvider).status,
),
if (!resolvedChannel.isForum && typingEntries.isNotEmpty)
_TypingIndicator(entries: typingEntries),
if (!resolvedChannel.isForum &&
@@ -304,9 +332,11 @@ class _MessageList extends HookConsumerWidget {
return ListView.builder(
controller: scrollController,
reverse: true,
padding: const EdgeInsets.symmetric(
horizontal: Grid.xs,
vertical: Grid.xxs,
padding: EdgeInsets.only(
left: Grid.xs,
right: Grid.xs,
top: frostedAppBarHeight(context),
bottom: Grid.xxs,
),
itemCount: entries.length + (isLoadingOlder.value ? 1 : 0),
itemBuilder: (context, index) {
@@ -1065,9 +1095,7 @@ class _DmAppBarTitle extends ConsumerWidget {
},
shape: BoxShape.circle,
border: Border.all(
color:
context.theme.appBarTheme.backgroundColor ??
context.theme.scaffoldBackgroundColor,
color: context.colors.surface,
width: 1.5,
),
),
@@ -1080,6 +1108,7 @@ class _DmAppBarTitle extends ConsumerWidget {
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
channel.displayLabel(currentPubkey: currentPubkey),
@@ -1089,8 +1118,8 @@ class _DmAppBarTitle extends ConsumerWidget {
),
Text(
presenceLabel,
style: context.textTheme.labelSmall?.copyWith(
color: context.colors.outline,
style: context.textTheme.bodySmall?.copyWith(
color: context.colors.onSurfaceVariant,
),
),
],
+92 -33
View File
@@ -8,6 +8,8 @@ import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/relay/relay.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../profile/profile_avatar.dart';
import '../profile/profile_provider.dart';
import '../settings/settings_page.dart';
@@ -20,6 +22,10 @@ import 'channels_provider.dart';
enum _QuickAction { createChannel, createForum, newDm }
/// Height of the [_ConnectionBanner]: vertical padding (Grid.quarter + 2) × 2
/// plus the ~16px row content (12px spinner / labelSmall text).
const double _kBannerHeight = 24.0;
class ChannelsPage extends HookConsumerWidget {
const ChannelsPage({super.key});
@@ -90,15 +96,13 @@ class ChannelsPage extends HookConsumerWidget {
}
}
return Scaffold(
appBar: AppBar(
titleSpacing: Grid.xs,
title: Text(
'\u{1F331} Sprout',
style: context.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
),
return FrostedScaffold(
appBar: FrostedAppBar(
leading: const Padding(
padding: EdgeInsets.only(left: Grid.xs),
child: Text('\u{1F331}', style: TextStyle(fontSize: 28)),
),
title: const SizedBox.shrink(),
actions: [
ProfileAvatar(
onTap: () => Navigator.of(context).push(
@@ -114,35 +118,91 @@ class ChannelsPage extends HookConsumerWidget {
shape: const CircleBorder(),
child: const Icon(LucideIcons.plus),
),
body: channels != null
? Column(
children: [
_ConnectionBanner(status: sessionState.status),
Expanded(
child: _ChannelsList(
channels: channels,
currentPubkey: currentPubkey,
onSelectChannel: openChannel,
),
),
],
)
: channelsAsync.hasError
? _ErrorView(
error: channelsAsync.error!,
onRetry: () => ref.read(channelsProvider.notifier).refresh(),
)
: const _ConnectionBanner(status: SessionStatus.connecting),
body: _ChannelsBody(
channels: channels,
channelsAsync: channelsAsync,
sessionStatus: sessionState.status,
currentPubkey: currentPubkey,
onRefresh: () => ref.read(channelsProvider.notifier).refresh(),
onSelectChannel: openChannel,
),
);
}
}
class _ChannelsList extends HookConsumerWidget {
class _ChannelsBody extends StatelessWidget {
final List<Channel>? channels;
final AsyncValue<List<Channel>> channelsAsync;
final SessionStatus sessionStatus;
final String? currentPubkey;
final Future<void> Function() onRefresh;
final Future<void> Function(Channel channel) onSelectChannel;
const _ChannelsBody({
required this.channels,
required this.channelsAsync,
required this.sessionStatus,
required this.currentPubkey,
required this.onRefresh,
required this.onSelectChannel,
});
@override
Widget build(BuildContext context) {
final barHeight = frostedAppBarHeight(context);
if (channels != null) {
return Stack(
children: [
RefreshIndicator(
onRefresh: onRefresh,
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(child: SizedBox(height: barHeight)),
// Extra space for the connection banner when visible.
if (sessionStatus != SessionStatus.connected &&
sessionStatus != SessionStatus.disconnected)
const SliverToBoxAdapter(
child: SizedBox(height: _kBannerHeight),
),
_SliverChannelsList(
channels: channels!,
currentPubkey: currentPubkey,
onSelectChannel: onSelectChannel,
),
],
),
),
Positioned(
top: barHeight,
left: 0,
right: 0,
child: _ConnectionBanner(status: sessionStatus),
),
],
);
}
if (channelsAsync.hasError) {
return Padding(
padding: EdgeInsets.only(top: barHeight),
child: _ErrorView(error: channelsAsync.error!, onRetry: onRefresh),
);
}
return Padding(
padding: EdgeInsets.only(top: barHeight),
child: const _ConnectionBanner(status: SessionStatus.connecting),
);
}
}
class _SliverChannelsList extends HookConsumerWidget {
final List<Channel> channels;
final String? currentPubkey;
final Future<void> Function(Channel channel) onSelectChannel;
const _ChannelsList({
const _SliverChannelsList({
required this.channels,
required this.currentPubkey,
required this.onSelectChannel,
@@ -167,10 +227,9 @@ class _ChannelsList extends HookConsumerWidget {
final forumsExpanded = useState(true);
final dmsExpanded = useState(true);
return RefreshIndicator(
onRefresh: () => ref.read(channelsProvider.notifier).refresh(),
child: ListView(
padding: const EdgeInsets.only(top: Grid.xxs, bottom: 80),
return SliverPadding(
padding: const EdgeInsets.only(top: Grid.xxs, bottom: 80),
sliver: SliverList.list(
children: [
if (visibleChannels.isEmpty)
const _EmptyState()
@@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../profile/user_cache_provider.dart';
import '../profile/user_profile.dart';
import 'channel_management_provider.dart';
@@ -86,15 +88,17 @@ class ThreadDetailPage extends HookConsumerWidget {
}
});
return Scaffold(
appBar: AppBar(title: const Text('Thread')),
return FrostedScaffold(
appBar: const FrostedAppBar(title: Text('Thread')),
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: Grid.xs,
vertical: Grid.xxs,
padding: EdgeInsets.only(
left: Grid.xs,
right: Grid.xs,
top: frostedAppBarHeight(context),
bottom: Grid.xxs,
),
itemCount: replies.length + 1, // +1 for thread head
itemBuilder: (context, index) {
@@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../channels/channel.dart';
import '../channels/compose_bar.dart';
import 'forum_models.dart';
@@ -57,12 +58,18 @@ class ForumPostsView extends HookConsumerWidget {
)
: null,
body: postsAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(
child: Text(
'Failed to load posts',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
loading: () => Padding(
padding: EdgeInsets.only(top: frostedAppBarHeight(context)),
child: const Center(child: CircularProgressIndicator()),
),
error: (e, _) => Padding(
padding: EdgeInsets.only(top: frostedAppBarHeight(context)),
child: Center(
child: Text(
'Failed to load posts',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
),
),
),
),
@@ -80,7 +87,12 @@ class ForumPostsView extends HookConsumerWidget {
await ref.read(forumPostsProvider(channel.id).future);
},
child: ListView.separated(
padding: const EdgeInsets.all(Grid.xs),
padding: EdgeInsets.only(
top: frostedAppBarHeight(context),
left: Grid.xs,
right: Grid.xs,
bottom: Grid.xs,
),
itemCount: posts.length,
separatorBuilder: (_, _) =>
const SizedBox(height: Grid.xxs),
@@ -5,6 +5,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../channels/compose_bar.dart';
import '../channels/message_content.dart';
import '../profile/user_cache_provider.dart';
@@ -55,8 +57,8 @@ class ForumThreadPage extends HookConsumerWidget {
.value ??
false;
return Scaffold(
appBar: AppBar(
return FrostedScaffold(
appBar: FrostedAppBar(
title: const Text('Thread'),
actions: [
if (isOwnPost)
@@ -69,12 +71,18 @@ class ForumThreadPage extends HookConsumerWidget {
],
),
body: threadAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(
child: Text(
'Failed to load thread',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
loading: () => Padding(
padding: EdgeInsets.only(top: frostedAppBarHeight(context)),
child: const Center(child: CircularProgressIndicator()),
),
error: (e, _) => Padding(
padding: EdgeInsets.only(top: frostedAppBarHeight(context)),
child: Center(
child: Text(
'Failed to load thread',
style: context.textTheme.bodyMedium?.copyWith(
color: context.colors.error,
),
),
),
),
@@ -208,7 +216,10 @@ class _ThreadContent extends HookConsumerWidget {
children: [
Expanded(
child: ListView(
padding: const EdgeInsets.only(bottom: Grid.xs),
padding: EdgeInsets.only(
top: frostedAppBarHeight(context),
bottom: Grid.xs,
),
children: [
// Original post
_OriginalPost(post: post),
+6 -3
View File
@@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import '../channels/channel.dart';
import '../channels/channel_detail_page.dart';
import '../channels/channel_management_provider.dart';
@@ -34,9 +36,9 @@ class SearchPage extends HookConsumerWidget {
() => textController.text.isNotEmpty,
);
return Scaffold(
appBar: AppBar(
titleSpacing: Grid.xs,
return FrostedScaffold(
resizeToAvoidBottomInset: true,
appBar: FrostedAppBar(
title: Container(
height: 36,
padding: const EdgeInsets.symmetric(horizontal: Grid.half),
@@ -77,6 +79,7 @@ class SearchPage extends HookConsumerWidget {
),
body: Column(
children: [
SizedBox(height: frostedAppBarHeight(context)),
_FilterChips(
active: activeFilter.value,
onChanged: (f) => activeFilter.value = f,
@@ -7,6 +7,8 @@ import 'package:nostr/nostr.dart' as nostr;
import '../../shared/auth/auth.dart';
import '../../shared/relay/relay.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
import 'theme_picker_page.dart';
class SettingsPage extends HookConsumerWidget {
@@ -18,10 +20,15 @@ class SettingsPage extends HookConsumerWidget {
final selectedAccent = ref.watch(accentProvider);
final selectedScheme = ref.watch(schemeProvider);
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
return FrostedScaffold(
appBar: const FrostedAppBar(title: Text('Settings')),
body: ListView(
padding: const EdgeInsets.all(Grid.xs),
padding: EdgeInsets.only(
top: frostedAppBarHeight(context),
left: Grid.xs,
right: Grid.xs,
bottom: Grid.xs,
),
children: [
// Connection info
Text('Connection', style: context.textTheme.titleMedium),
@@ -4,6 +4,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../shared/theme/theme.dart';
import '../../shared/widgets/frosted_app_bar.dart';
import '../../shared/widgets/frosted_scaffold.dart';
class ThemePickerPage extends HookConsumerWidget {
const ThemePickerPage({super.key});
@@ -64,10 +66,11 @@ class ThemePickerPage extends HookConsumerWidget {
return null;
}, const []);
return Scaffold(
appBar: AppBar(title: const Text('Color Scheme')),
return FrostedScaffold(
appBar: const FrostedAppBar(title: Text('Color Scheme')),
body: Column(
children: [
SizedBox(height: frostedAppBarHeight(context)),
// Always-visible search bar
Padding(
padding: const EdgeInsets.symmetric(
+2 -5
View File
@@ -53,10 +53,6 @@ class AppTheme {
);
}
/// Legacy getters for backwards compatibility.
static ThemeData get lightTheme => light();
static ThemeData get darkTheme => dark();
static ThemeData _buildTheme({
required ColorScheme scheme,
required AppColors appColors,
@@ -71,10 +67,11 @@ class AppTheme {
fontFamily: 'Geist',
textTheme: textTheme,
appBarTheme: AppBarTheme(
backgroundColor: scheme.surface,
backgroundColor: Colors.transparent,
foregroundColor: scheme.onSurface,
surfaceTintColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
titleTextStyle: textTheme.titleMedium?.copyWith(
color: scheme.onSurface,
),
@@ -0,0 +1,111 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../theme/theme.dart';
/// Height of the frosted app bar content area below the safe area.
const _kBarContentHeight = Grid.xxs + 32 + Grid.xxs; // 48
/// Returns the total height of the [FrostedAppBar] including safe area padding.
///
/// Use this to add top spacing to body content so it starts below the bar.
double frostedAppBarHeight(BuildContext context) {
return MediaQuery.paddingOf(context).top + _kBarContentHeight;
}
/// A frosted-glass floating app bar designed to sit inside a [Stack].
///
/// Renders as a [Positioned] widget pinned to the top of its parent Stack.
/// Content scrolls underneath with a translucent backdrop blur effect.
class FrostedAppBar extends StatelessWidget {
/// Widget displayed on the leading (left) side. If null and the navigator
/// can pop, a back button is shown automatically.
final Widget? leading;
/// Widget displayed in the center/title area.
final Widget? title;
/// Widgets displayed on the trailing (right) side.
final List<Widget> actions;
const FrostedAppBar({
super.key,
this.leading,
this.title,
this.actions = const [],
});
@override
Widget build(BuildContext context) {
final topPadding = MediaQuery.paddingOf(context).top;
final canPop = Navigator.canPop(context);
final effectiveLeading =
leading ??
(canPop
? SizedBox(
width: 48,
height: 48,
child: IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(LucideIcons.chevronLeft),
tooltip: 'Back',
),
)
: null);
return Positioned(
top: 0,
left: 0,
right: 0,
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
padding: EdgeInsets.only(top: topPadding),
decoration: BoxDecoration(
color: context.colors.surface.withValues(alpha: 0.5),
border: Border(
bottom: BorderSide(
color: context.colors.outlineVariant.withValues(alpha: 0.3),
),
),
),
child: SizedBox(
height: _kBarContentHeight,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: Grid.quarter),
child: Row(
children: [
?effectiveLeading,
if (title != null)
Expanded(
child: Padding(
padding: EdgeInsets.only(
left: effectiveLeading != null ? 0 : Grid.xs,
),
child: DefaultTextStyle.merge(
style: context.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
child: title!,
),
),
)
else
const Spacer(),
...actions,
],
),
),
),
),
),
),
);
}
}
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'frosted_app_bar.dart';
/// A convenience [Scaffold] that overlays a [FrostedAppBar] on top of its body.
///
/// The body is rendered full-bleed inside a [Stack] with the frosted app bar
/// floating above it. The body is responsible for adding its own top spacing
/// using [frostedAppBarHeight] so content starts below the bar.
class FrostedScaffold extends StatelessWidget {
/// The frosted app bar displayed at the top of the screen.
final FrostedAppBar appBar;
/// The primary content of the scaffold. Must handle its own top spacing
/// using [frostedAppBarHeight] — the scaffold does NOT add automatic padding.
final Widget body;
/// Optional floating action button, passed through to [Scaffold].
final Widget? floatingActionButton;
/// Whether the body should resize when the on-screen keyboard appears.
final bool? resizeToAvoidBottomInset;
const FrostedScaffold({
super.key,
required this.appBar,
required this.body,
this.floatingActionButton,
this.resizeToAvoidBottomInset,
});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
floatingActionButton: floatingActionButton,
body: Stack(children: [body, appBar]),
);
}
}
@@ -17,10 +17,7 @@ void main() {
Widget buildTestable({required List<Override> overrides}) {
return ProviderScope(
overrides: overrides,
child: MaterialApp(
theme: AppTheme.lightTheme,
home: const ActivityPage(),
),
child: MaterialApp(theme: AppTheme.light(), home: const ActivityPage()),
);
}
@@ -156,7 +156,7 @@ Widget _buildTestable({
),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
navigatorObservers: navigatorObservers,
home: ChannelDetailPage(channel: resolvedChannel),
),
@@ -949,7 +949,7 @@ void main() {
),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: ChannelDetailPage(channel: _testChannel),
),
),
@@ -18,10 +18,7 @@ void main() {
presenceProvider.overrideWith(() => _FakePresenceNotifier()),
...overrides,
],
child: MaterialApp(
theme: AppTheme.lightTheme,
home: const ChannelsPage(),
),
child: MaterialApp(theme: AppTheme.light(), home: const ChannelsPage()),
);
}
@@ -79,7 +76,7 @@ void main() {
expect(find.text('CHANNELS'), findsOneWidget);
expect(find.text('FORUMS'), findsOneWidget);
expect(find.text('DMS'), findsOneWidget);
expect(find.text('\u{1F331} Sprout'), findsOneWidget);
expect(find.text('\u{1F331}'), findsOneWidget);
expect(find.byTooltip('Create or start conversation'), findsOneWidget);
});
@@ -120,7 +120,7 @@ Widget _buildComposeBar({
),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: Scaffold(
body: SafeArea(
child: ComposeBar(channelId: 'channel-1', onSend: onSend),
@@ -7,7 +7,7 @@ import 'package:sprout_mobile/shared/theme/theme.dart';
Widget _testable(Widget child) {
return MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: Scaffold(body: child),
);
}
@@ -74,7 +74,7 @@ Widget _buildPostCard({
userCacheProvider.overrideWith(() => _FakeUserCacheNotifier(users)),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: Scaffold(
body: ForumPostCard(
post: post,
@@ -103,7 +103,7 @@ Widget _buildPostsView({
),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: Scaffold(
body: ForumPostsView(channel: ch, currentPubkey: 'self'),
),
@@ -132,7 +132,7 @@ Widget _buildThreadPage({
),
],
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: ForumThreadPage(
channelId: _channelId,
postEventId: postEventId,
+1 -1
View File
@@ -11,7 +11,7 @@ class WidgetHelpers {
return ProviderScope(
overrides: overrides,
child: MaterialApp(
theme: AppTheme.lightTheme,
theme: AppTheme.light(),
home: Scaffold(body: child),
),
);