mirror of
https://github.com/feeddeck/feeddeck.git
synced 2026-03-12 01:54:51 -05:00
On Windows and Linux it could happen that the audio playback for a podcast wasn't stopped when the item details view for a podcast item was closed. This commit "fixes" the problem, by pausing the audio player, before the widget is disposed. This commit also fixes the condition when the background audio services should be initialized in the `main.dart` file. Instead of the macOS check, we checked for iOS twice.
218 lines
8.4 KiB
Dart
218 lines
8.4 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: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();
|
|
|
|
/// 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),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|