Files
feeddeck/app/lib/main.dart
Rico Berger 280a222210 [podcast] Fix Audio Playback on Windows and Linux (#182)
Fix the audio playback on Windows and Linux for the podcast player by
adding the missing initialization for `JustAudioMediaKit`.
2024-08-06 07:45:15 +02:00

220 lines
8.5 KiB
Dart

import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:just_audio_background/just_audio_background.dart';
import 'package:just_audio_media_kit/just_audio_media_kit.dart';
import 'package:media_kit/media_kit.dart';
import 'package:provider/provider.dart';
import 'package:window_manager/window_manager.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/repositories/layout_repository.dart';
import 'package:feeddeck/repositories/profile_repository.dart';
import 'package:feeddeck/repositories/settings_repository.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/widgets/confirmation/confirmation.dart';
import 'package:feeddeck/widgets/home/home.dart';
import 'package:feeddeck/widgets/reset_password/reset_password.dart';
/// Before we are calling [runApp] we have to ensure that the widget bindings
/// are initialized, so that we can preserve the splash screen until we are done
/// with the initalization of the app.
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
/// We have to initialize the [windowManager] before we are calling [runApp]
/// on all desktop platforms, so we can set the window size, title and so on.
if (!kIsWeb && (Platform.isLinux || Platform.isMacOS || Platform.isWindows)) {
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
size: Size(1024, 768),
center: true,
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.normal,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.setTitle('FeedDeck');
await windowManager.show();
await windowManager.focus();
});
}
/// Initialize the [media_kit] packages, so that we can play audio and video
/// files.
MediaKit.ensureInitialized();
JustAudioMediaKit.ensureInitialized();
/// Initialize the [just_audio_background] package, so that we can play audio
/// files in the background.
///
/// We can not initialize the [just_audio_background] package on Windows and
/// Linux, because then the returned duration in the `_player.durationStream`
/// isn't working correctly in the [ItemAudioPlayer] widget.
if (kIsWeb || Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
await JustAudioBackground.init(
androidNotificationChannelId: 'com.ryanheise.bg_demo.channel.audio',
androidNotificationChannelName: 'Audio playback',
androidNotificationOngoing: true,
);
}
/// For the ewb we have to use the path url strategy, so that the redirect
/// within Supabase is working in all cases. On all other platforms this is a
/// noop.
usePathUrlStrategy();
/// We also have to initialize the [SettingsRepository], which contains all
/// the settings, to use the app with Supabase. This way a user can also use
/// his own Supabase backend.
await SettingsRepository().init();
runApp(const FeedDeckApp());
}
/// [FeedDeckScrollBehavior] changes the scrolling behavior of the app. This is
/// required to enable scrolling on desktop via drag, which is the only way that
/// a user can scroll vertically with a mouse via drag.
///
/// E.g. this is required for the [ColumnLayoutSources] widget, where we rely on
/// the drag gesture to scroll the list of sources.
class FeedDeckScrollBehavior extends MaterialScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
};
}
/// [onGenerateRoute] is used in `onGenerateRoute` and `onGenerateInitialRoutes`
/// of the [MaterialApp] to add handling for some special routes were do not
/// want to render the [Home] widget (e.g. set a new password).
///
/// For that we have to check the current path and when the user call one of
/// this "special" routes we render the corresponding widget and pass all the
/// required values from the query paramters to this widget.
Route onGenerateRoute(RouteSettings settings) {
if (settings.name != null) {
var uriData = Uri.parse(settings.name!);
switch (uriData.path) {
case '/confirmation':
return MaterialPageRoute(
builder: (_) => Confirmation(
template: uriData.queryParameters['template'] ?? '',
confirmationUrl: uriData.queryParameters['confirmation_url'] ?? '',
),
);
case '/reset-password':
return MaterialPageRoute(
builder: (_) => const ResetPassword(),
);
}
}
return MaterialPageRoute(
builder: (_) => const Home(),
);
}
/// The [FeedDeckApp] is the root widget of the app. The widget is used to
/// initialize some of our providers via the [MultiProvider] widget and to set
/// the theme for the app.
class FeedDeckApp extends StatelessWidget {
const FeedDeckApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => LayoutRepository()),
ChangeNotifierProvider(create: (_) => AppRepository()),
ChangeNotifierProvider(create: (_) => ProfileRepository()),
],
child: TooltipVisibility(
visible: false,
child: MaterialApp(
title: 'FeedDeck',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: const ColorScheme(
brightness: Constants.brightness,
primary: Constants.primary,
onPrimary: Constants.onPrimary,
secondary: Constants.secondary,
onSecondary: Constants.onSecondary,
error: Constants.error,
onError: Constants.onError,
background: Constants.background,
onBackground: Constants.onBackground,
surface: Constants.surface,
onSurface: Constants.onSurface,
),
canvasColor: Constants.canvasColor,
appBarTheme: const AppBarTheme(
centerTitle: true,
backgroundColor: Constants.appBarBackgroundColor,
scrolledUnderElevation: Constants.scrolledUnderElevation,
elevation: Constants.appBarElevation,
),
snackBarTheme: const SnackBarThemeData(
backgroundColor: Constants.surface,
contentTextStyle: TextStyle(
color: Constants.onSurface,
),
),
dialogTheme: const DialogTheme(
backgroundColor: Constants.background,
surfaceTintColor: Constants.background,
contentTextStyle: TextStyle(
color: Constants.onBackground,
),
),
popupMenuTheme: const PopupMenuThemeData(
color: Constants.background,
surfaceTintColor: Constants.background,
textStyle: TextStyle(
color: Constants.onBackground,
),
),
drawerTheme: const DrawerThemeData(
backgroundColor: Constants.background,
surfaceTintColor: Constants.background,
),
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: Constants.background,
surfaceTintColor: Constants.background,
),
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.linux: CupertinoPageTransitionsBuilder(),
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.windows: CupertinoPageTransitionsBuilder(),
},
),
),
scrollBehavior: FeedDeckScrollBehavior(),
onGenerateInitialRoutes: (initialRoute) =>
[onGenerateRoute(RouteSettings(name: initialRoute))],
onGenerateRoute: (RouteSettings settings) =>
onGenerateRoute(settings),
),
),
);
}
}