[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147import 'package:flutter/services.dart';import 'package:flutter_riverpod/flutter_riverpod.dart';import 'package:material_ui/material_ui.dart';import 'package:get_it/get_it.dart';import 'package:posthog_flutter/posthog_flutter.dart';import 'package:skeletonizer/skeletonizer.dart';import 'package:spark/src/core/design_system/theme/app_theme.dart';import 'package:spark/src/core/l10n/app_localizations.dart';import 'package:spark/src/core/l10n/app_localization_delegates.dart';import 'package:spark/src/core/routing/app_router.dart';import 'package:spark/src/core/ui/theme/providers/theme_provider.dart' show themeModeProvider, themeProvider;import 'package:spark/src/core/utils/logging/log_service.dart';import 'package:spark/src/core/utils/logging/logger.dart';import 'package:spark/src/features/feed/providers/feed_provider.dart';import 'package:spark/src/features/settings/providers/settings_provider.dart';
class SprkApp extends ConsumerStatefulWidget { const SprkApp({required this.onFirstRouteReady, super.key});
final VoidCallback onFirstRouteReady;
@override ConsumerState<SprkApp> createState() => _SprkAppState();}
class _SprkAppState extends ConsumerState<SprkApp> { final _appRouter = AppRouter(); final SparkLogger _logger = GetIt.instance<LogService>().getLogger('SprkApp'); bool _didReleaseFirstFrame = false;
@override void initState() { super.initState(); _appRouter.addListener(_releaseFirstFrameWhenRouteIsReady); // Register AppRouter globally for push notification navigation if (!GetIt.instance.isRegistered<AppRouter>()) { GetIt.instance.registerSingleton<AppRouter>(_appRouter); }
ref.read(themeProvider.notifier).initialize(); // Defer initialization to after the widget tree is built // to avoid modifying providers during build WidgetsBinding.instance.addPostFrameCallback((_) { _initializeApp(); }); }
@override void dispose() { _appRouter.removeListener(_releaseFirstFrameWhenRouteIsReady); super.dispose(); }
void _releaseFirstFrameWhenRouteIsReady() { if (_didReleaseFirstFrame || !_appRouter.hasEntries) { return; }
_didReleaseFirstFrame = true; _appRouter.removeListener(_releaseFirstFrameWhenRouteIsReady); WidgetsBinding.instance.addPostFrameCallback((_) { widget.onFirstRouteReady(); }); }
Future<void> _initializeApp() async { try { _logger.d('Syncing user preferences from server...'); final settingsNotifier = ref.read(settingsProvider.notifier); // syncPreferencesFromServer already calls loadSettings internally await settingsNotifier.syncPreferencesFromServer(); _logger.d('User preferences synced successfully');
if (!mounted) return;
final activeFeed = ref.read(settingsProvider).activeFeed; _logger.d('Active feed: ${activeFeed.config.value}');
ref.read(feedProvider(activeFeed).notifier).loadAndUpdateFirstLoad(); _logger.d('Feed loading started'); } catch (e) { _logger.w('Error during app initialization', error: e); } }
@override Widget build(BuildContext context) { // Force dark status bar and navigation bar icons SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarIconBrightness: Brightness.light, statusBarBrightness: Brightness.dark, systemNavigationBarIconBrightness: Brightness.light, ), );
final themeMode = ref.watch(themeModeProvider);
return MaterialApp.router( title: 'Spark', builder: (context, child) => SkeletonizerConfig( data: SkeletonizerConfigData(brightness: Theme.of(context).brightness), child: child!, ), theme: AppTheme.light, darkTheme: AppTheme.dark, debugShowCheckedModeBanner: false, themeMode: themeMode, supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: appLocalizationDelegates, localeResolutionCallback: (locale, supportedLocales) { for (final supportedLocale in supportedLocales) { if (supportedLocale.languageCode == locale?.languageCode) { return supportedLocale; } } return supportedLocales.first; }, routerConfig: _appRouter.config( placeholder: (_) => const ColoredBox(color: Colors.black), navigatorObservers: () => [ PosthogObserver(nameExtractor: _postHogScreenName), ], ), ); }}
String? _postHogScreenName(RouteSettings settings) { final name = settings.name?.trim(); if (name == null || name.isEmpty) { return null; }
if (name == '/') { return 'Root'; }
const routeSuffix = 'Route'; if (name.endsWith(routeSuffix)) { return name.substring(0, name.length - routeSuffix.length); }
return name;}