diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 5bd46a6c..d7aa46cd 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -51,7 +51,7 @@ "errorNoCompatibleNextcloudAppsFound": "No compatible Nextcloud apps could be found.\nWe are working hard to implement more and more apps!", "errorServerInMaintenanceMode": "The server is in maintenance mode. Please try again later or contact the server admin.", "errorMissingPermission": "Permission for {name} is missing", - "@errorMissingPermission" : { + "@errorMissingPermission": { "placeholders": { "name": { "type": "String" @@ -59,7 +59,7 @@ } }, "errorUnsupportedAppVersions": "Sorry, the version of the following apps on your Nextcloud instance are not supported. \n {names} \n Please contact your administrator to resolve the issues.", - "@errorUnsupportedAppVersions" : { + "@errorUnsupportedAppVersions": { "placeholders": { "names": { "type": "String" @@ -70,7 +70,7 @@ "errorInvalidURL": "Invalid URL provided", "errorInvalidQRcode": "Invalid QR-Code provided", "errorRouteNotFound": "Route not found: {route}", - "@errorRouteNotFound" : { + "@errorRouteNotFound": { "placeholders": { "route": { "type": "String" @@ -152,7 +152,6 @@ "globalOptionsNavigationMode": "Navigation mode", "globalOptionsNavigationModeDrawer": "Drawer", "globalOptionsNavigationModeDrawerAlwaysVisible": "Drawer always visible", - "globalOptionsNavigationModeQuickBar": "Quick bar", "accountOptionsRemove": "Remove account", "accountOptionsRemoveConfirm": "Are you sure you want to remove the account {id}?", "@accountOptionsRemoveConfirm": { diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index d287e80a..457eafc6 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -635,12 +635,6 @@ abstract class AppLocalizations { /// **'Drawer always visible'** String get globalOptionsNavigationModeDrawerAlwaysVisible; - /// No description provided for @globalOptionsNavigationModeQuickBar. - /// - /// In en, this message translates to: - /// **'Quick bar'** - String get globalOptionsNavigationModeQuickBar; - /// No description provided for @accountOptionsRemove. /// /// In en, this message translates to: diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index 45a8ccc8..4b6d1999 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -314,9 +314,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get globalOptionsNavigationModeDrawerAlwaysVisible => 'Drawer always visible'; - @override - String get globalOptionsNavigationModeQuickBar => 'Quick bar'; - @override String get accountOptionsRemove => 'Remove account'; diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index 5bf324c3..b3716a3a 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -27,8 +27,6 @@ abstract interface class AppsBlocEvents { @internal abstract interface class AppsBlocStates { - BehaviorSubject>> get apps; - BehaviorSubject>> get appImplementations; BehaviorSubject> get notificationsAppImplementation; @@ -48,7 +46,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates this._account, this._allAppImplementations, ) { - apps.listen((final result) { + _apps.listen((final result) { appImplementations .add(result.transform((final data) => _filteredAppImplementations(data.map((final a) => a.id)))); }); @@ -162,10 +160,11 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates final AccountsBloc _accountsBloc; final Account _account; final Iterable _allAppImplementations; + final _apps = BehaviorSubject>>(); @override void dispose() { - unawaited(apps.close()); + unawaited(_apps.close()); unawaited(appImplementations.close()); unawaited(notificationsAppImplementation.close()); unawaited(activeApp.close()); @@ -182,9 +181,6 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates BehaviorSubject>>> appImplementations = BehaviorSubject(); - @override - BehaviorSubject>> apps = BehaviorSubject(); - @override BehaviorSubject> notificationsAppImplementation = BehaviorSubject(); @@ -199,7 +195,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates await RequestManager.instance.wrapNextcloud( _account.id, 'apps-apps', - apps, + _apps, _account.client.core.navigation.getAppsNavigationRaw(), (final response) => response.body.ocs.data.toList(), ); diff --git a/packages/neon/neon/lib/src/blocs/push_notifications.dart b/packages/neon/neon/lib/src/blocs/push_notifications.dart index d4715cd7..5452bb3d 100644 --- a/packages/neon/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/neon/lib/src/blocs/push_notifications.dart @@ -35,7 +35,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, } final AccountsBloc _accountsBloc; - late final _storage = const AppStorage(StorageKeys.notifications); + late final _storage = const AppStorage(StorageKeys.lastEndpoint); final GlobalOptions _globalOptions; StreamSubscription>? _accountsListener; @@ -46,8 +46,6 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, _globalOptions.pushNotificationsEnabled.removeListener(_pushNotificationsEnabledListener); } - String _keyLastEndpoint(final Account account) => 'last-endpoint-${account.id}'; - Future _pushNotificationsEnabledListener() async { if (_globalOptions.pushNotificationsEnabled.value) { await _setupUnifiedPush(); @@ -72,7 +70,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, return; } - if (_storage.getString(_keyLastEndpoint(account)) == endpoint) { + if (_storage.getString(account.id) == endpoint) { debugPrint('Endpoint not changed'); return; } @@ -85,7 +83,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, proxyServer: '$endpoint#', // This is a hack to make the Nextcloud server directly push to the endpoint ); - await _storage.setString(_keyLastEndpoint(account), endpoint); + await _storage.setString(account.id, endpoint); debugPrint( 'Account $instance registered for push notifications ${json.encode(subscription.body.ocs.data.toJson())}', @@ -117,7 +115,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, try { await account.client.notifications.push.removeDevice(); await UnifiedPush.unregister(account.id); - await _storage.remove(_keyLastEndpoint(account)); + await _storage.remove(account.id); } catch (e) { debugPrint('Failed to unregister device: $e'); } diff --git a/packages/neon/neon/lib/src/blocs/user_statuses.dart b/packages/neon/neon/lib/src/blocs/user_statuses.dart index 7d29cfd1..8796532b 100644 --- a/packages/neon/neon/lib/src/blocs/user_statuses.dart +++ b/packages/neon/neon/lib/src/blocs/user_statuses.dart @@ -66,7 +66,7 @@ class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents var isAway = false; if (NeonPlatform.instance.canUseWindowManager) { final focused = await windowManager.isFocused(); - final visible = await windowManager.isFocused(); + final visible = await windowManager.isVisible(); isAway = !focused || !visible; } try { diff --git a/packages/neon/neon/lib/src/models/app_implementation.dart b/packages/neon/neon/lib/src/models/app_implementation.dart index 6679a187..6f2452c5 100644 --- a/packages/neon/neon/lib/src/models/app_implementation.dart +++ b/packages/neon/neon/lib/src/models/app_implementation.dart @@ -21,11 +21,9 @@ import 'package:vector_graphics/vector_graphics.dart'; @immutable abstract class AppImplementation implements Disposable { - AppImplementation(); - String get id; LocalizationsDelegate get localizationsDelegate; - List get supportedLocales; + Iterable get supportedLocales; String nameFromLocalization(final AppLocalizations localizations) => localizations.appImplementationName(id); String name(final BuildContext context) => nameFromLocalization(AppLocalizations.of(context)); diff --git a/packages/neon/neon/lib/src/settings/models/storage.dart b/packages/neon/neon/lib/src/settings/models/storage.dart index 7e49d297..9dcd4af1 100644 --- a/packages/neon/neon/lib/src/settings/models/storage.dart +++ b/packages/neon/neon/lib/src/settings/models/storage.dart @@ -102,23 +102,23 @@ final class SingleValueStorage { @internal final class AppStorage implements SettingsStorage { const AppStorage( - this.key, [ + this.groupKey, [ this.suffix, ]); - final StorageKeys key; + final StorageKeys groupKey; final String? suffix; - String get id => suffix ?? key.value; + String get id => suffix ?? groupKey.value; @visibleForTesting String formatKey(final String key) { if (suffix != null) { - return '${this.key.value}-$suffix-$key'; + return '${groupKey.value}-$suffix-$key'; } - return '${this.key.value}-$key'; + return '${groupKey.value}-$key'; } bool containsKey(final String key) => NeonStorage.database.containsKey(formatKey(key)); diff --git a/packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart b/packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart index 1779e1ed..8ec81116 100644 --- a/packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart +++ b/packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart @@ -8,21 +8,19 @@ import 'package:neon/src/widgets/account_tile.dart'; class AccountSettingsTile extends SettingsTile { const AccountSettingsTile({ required this.account, - this.color, this.trailing, this.onTap, super.key, }); final Account account; - final Color? color; + final Widget? trailing; final GestureTapCallback? onTap; @override Widget build(final BuildContext context) => NeonAccountTile( account: account, - color: color, trailing: trailing, onTap: onTap, ); diff --git a/packages/neon/neon/lib/src/utils/account_options.dart b/packages/neon/neon/lib/src/utils/account_options.dart index 6e71534f..fe337f57 100644 --- a/packages/neon/neon/lib/src/utils/account_options.dart +++ b/packages/neon/neon/lib/src/utils/account_options.dart @@ -19,10 +19,7 @@ class AccountSpecificOptions extends OptionsCollection { initialApp.values = { null: (final context) => AppLocalizations.of(context).accountOptionsAutomatic, - for (final app in result.requireData) ...{ - app.id: app.name, - }, - }; + }..addEntries(result.requireData.map((final app) => MapEntry(app.id, app.name))); }); } diff --git a/packages/neon/neon/lib/src/utils/global_options.dart b/packages/neon/neon/lib/src/utils/global_options.dart index 203f24df..661c08c9 100644 --- a/packages/neon/neon/lib/src/utils/global_options.dart +++ b/packages/neon/neon/lib/src/utils/global_options.dart @@ -26,7 +26,7 @@ class GlobalOptions extends OptionsCollection { void _rememberLastUsedAccountListener() { initialAccount.enabled = !rememberLastUsedAccount.value; if (rememberLastUsedAccount.value) { - initialAccount.value = null; + initialAccount.reset(); } else { // Only override the initial account if there already has been a value, // which means it's not the initial emit from rememberLastUsedAccount @@ -44,7 +44,7 @@ class GlobalOptions extends OptionsCollection { pushNotificationsEnabled.value = false; } } else { - pushNotificationsDistributor.value = null; + pushNotificationsDistributor.reset(); } } @@ -91,26 +91,29 @@ class GlobalOptions extends OptionsCollection { } void updateAccounts(final List accounts) { - initialAccount.values = { - for (final account in accounts) account.id: (final context) => account.humanReadableID, - }; + initialAccount.values = Map.fromEntries( + accounts.map( + (final account) => MapEntry(account.id, (final context) => account.humanReadableID), + ), + ); - if (accounts.tryFind(initialAccount.value) == null) { + if (!initialAccount.values.containsKey(initialAccount.value)) { initialAccount.reset(); } } - Future updateDistributors(final List distributors) async { - pushNotificationsDistributor.values = { - for (final distributor in distributors) ...{ - distributor: _distributorsMap[distributor] ?? (final _) => distributor, - }, - }; + void updateDistributors(final List distributors) { + pushNotificationsDistributor.values = Map.fromEntries( + distributors.map( + (final distributor) => MapEntry(distributor, _distributorsMap[distributor] ?? (final _) => distributor), + ), + ); - final allowed = distributors.isNotEmpty; + final allowed = pushNotificationsDistributor.values.containsKey(pushNotificationsDistributor.value); pushNotificationsEnabled.enabled = allowed; if (!allowed) { - pushNotificationsEnabled.value = false; + pushNotificationsDistributor.reset(); + pushNotificationsEnabled.reset(); } } @@ -128,7 +131,7 @@ class GlobalOptions extends OptionsCollection { late final themeOLEDAsDark = ToggleOption( storage: storage, - key: GlobalOptionKeys.themeOledAsDark, + key: GlobalOptionKeys.themeOLEDAsDark, label: (final context) => AppLocalizations.of(context).globalOptionsThemeOLEDAsDark, defaultValue: false, ); @@ -209,12 +212,9 @@ class GlobalOptions extends OptionsCollection { defaultValue: Platform.isAndroid || Platform.isIOS ? NavigationMode.drawer : NavigationMode.drawerAlwaysVisible, values: { NavigationMode.drawer: (final context) => AppLocalizations.of(context).globalOptionsNavigationModeDrawer, - if (!Platform.isAndroid && !Platform.isIOS) ...{ + if (!Platform.isAndroid && !Platform.isIOS) NavigationMode.drawerAlwaysVisible: (final context) => AppLocalizations.of(context).globalOptionsNavigationModeDrawerAlwaysVisible, - }, - // ignore: deprecated_member_use_from_same_package - NavigationMode.quickBar: (final context) => AppLocalizations.of(context).globalOptionsNavigationModeQuickBar, }, ); } @@ -222,7 +222,7 @@ class GlobalOptions extends OptionsCollection { @internal enum GlobalOptionKeys implements Storable { themeMode._('theme-mode'), - themeOledAsDark._('theme-oled-as-dark'), + themeOLEDAsDark._('theme-oled-as-dark'), themeKeepOriginalAccentColor._('theme-keep-original-accent-color'), pushNotificationsEnabled._('push-notifications-enabled'), pushNotificationsDistributor._('push-notifications-distributor'), @@ -244,6 +244,4 @@ enum GlobalOptionKeys implements Storable { enum NavigationMode { drawer, drawerAlwaysVisible, - @Deprecated("The new design won't use this anymore") - quickBar, } diff --git a/packages/neon/neon/lib/src/utils/global_popups.dart b/packages/neon/neon/lib/src/utils/global_popups.dart index b4a35f67..409571db 100644 --- a/packages/neon/neon/lib/src/utils/global_popups.dart +++ b/packages/neon/neon/lib/src/utils/global_popups.dart @@ -34,6 +34,7 @@ class GlobalPopups { } _subscriptions.clear(); _registered = false; + instance = null; } void register(final BuildContext context) { @@ -42,12 +43,13 @@ class GlobalPopups { return; } + _registered = true; + final globalOptions = NeonProvider.of(context); final firstLaunchBloc = NeonProvider.of(context); final nextPushBloc = NeonProvider.of(context); - - _subscriptions.addAll([ - if (NeonPlatform.instance.canUsePushNotifications) ...[ + if (NeonPlatform.instance.canUsePushNotifications) { + _subscriptions.addAll([ firstLaunchBloc.onFirstLaunch.listen((final _) { assert(context.mounted, 'Context should be mounted'); if (!globalOptions.pushNotificationsEnabled.enabled) { @@ -98,9 +100,7 @@ class GlobalPopups { ), ); }), - ], - ]); - - _registered = true; + ]); + } } } diff --git a/packages/neon/neon/lib/src/utils/user_agent.dart b/packages/neon/neon/lib/src/utils/user_agent.dart index f8f924ae..3685dcc1 100644 --- a/packages/neon/neon/lib/src/utils/user_agent.dart +++ b/packages/neon/neon/lib/src/utils/user_agent.dart @@ -1,8 +1,11 @@ import 'package:meta/meta.dart'; import 'package:package_info_plus/package_info_plus.dart'; -late String? _userAgent; +String? _userAgent; +/// Sets the user agent. +/// +/// It can be accessed with [neonUserAgent]. @internal void buildUserAgent(final PackageInfo packageInfo) { var buildNumber = packageInfo.buildNumber; @@ -12,5 +15,13 @@ void buildUserAgent(final PackageInfo packageInfo) { _userAgent = 'Neon ${packageInfo.version}+$buildNumber'; } +/// Gets the current user agent. +/// +/// It must be set by calling [buildUserAgent] before. If not set a [StateError] will be thrown. @internal -String get neonUserAgent => _userAgent!; +String get neonUserAgent { + if (_userAgent == null) { + throw StateError('The user agent has not been set up. Please use `buildUserAgent` before.'); + } + return _userAgent!; +} diff --git a/packages/neon/neon/lib/src/widgets/account_tile.dart b/packages/neon/neon/lib/src/widgets/account_tile.dart index 365a745a..2a13d681 100644 --- a/packages/neon/neon/lib/src/widgets/account_tile.dart +++ b/packages/neon/neon/lib/src/widgets/account_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:intersperse/intersperse.dart'; import 'package:meta/meta.dart'; import 'package:neon/src/bloc/result_builder.dart'; import 'package:neon/src/blocs/accounts.dart'; @@ -13,21 +14,15 @@ import 'package:nextcloud/provisioning_api.dart' as provisioning_api; class NeonAccountTile extends StatelessWidget { const NeonAccountTile({ required this.account, - this.color, this.trailing, this.onTap, - this.textColor, - this.dense = false, this.showStatus = true, super.key, }); final Account account; - final Color? color; final Widget? trailing; final GestureTapCallback? onTap; - final Color? textColor; - final bool dense; final bool showStatus; @override @@ -35,16 +30,7 @@ class NeonAccountTile extends StatelessWidget { final userDetailsBloc = NeonProvider.of(context).getUserDetailsBlocFor(account); return ListTile( - textColor: textColor, onTap: onTap, - dense: dense, - contentPadding: dense ? EdgeInsets.zero : null, - visualDensity: dense - ? const VisualDensity( - horizontal: -4, - vertical: -4, - ) - : null, leading: NeonUserAvatar( account: account, showStatus: showStatus, @@ -54,46 +40,29 @@ class NeonAccountTile extends StatelessWidget { stream: userDetailsBloc.userDetails, builder: (final context, final userDetails) => Row( children: [ - if (userDetails.hasData) ...[ + if (userDetails.hasData) Flexible( child: Text( userDetails.requireData.displayname, - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - color: textColor, - ), overflow: TextOverflow.ellipsis, ), ), - ], - if (userDetails.isLoading) ...[ - const SizedBox( - width: 5, - ), - Expanded( - child: NeonLinearProgressIndicator( - color: textColor, - ), - ), - ], - if (userDetails.hasError) ...[ - const SizedBox( - width: 5, + if (userDetails.isLoading) + const Expanded( + child: NeonLinearProgressIndicator(), ), + if (userDetails.hasError) NeonError( userDetails.error, onlyIcon: true, iconSize: 24, onRetry: userDetailsBloc.refresh, ), - ], - ], + ].intersperse(const SizedBox(width: 5)).toList(), ), ), subtitle: Text( account.humanReadableID, - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: textColor, - ), overflow: TextOverflow.ellipsis, ), ); diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart index ecb20c85..8bd82d2b 100644 --- a/packages/neon/neon/lib/src/widgets/app_bar.dart +++ b/packages/neon/neon/lib/src/widgets/app_bar.dart @@ -251,25 +251,19 @@ class _NotificationIconButtonState extends State { final notificationsImplementationData = notificationsAppImplementation.data!; final notificationBloc = notificationsImplementationData.getBloc(_account); - return StreamBuilder( - stream: notificationsImplementationData.getUnreadCounter(notificationBloc), - builder: (final context, final unreadCounterSnapshot) { - final unreadCount = unreadCounterSnapshot.data ?? 0; - return IconButton( - key: Key('app-${notificationsImplementationData.id}'), - onPressed: () async { - await _openNotifications(notificationsImplementationData); - }, - tooltip: AppLocalizations.of(context).appImplementationName(notificationsImplementationData.id), - icon: NeonAppImplementationIcon( - appImplementation: notificationsImplementationData, - unreadCount: unreadCount, - color: unreadCount > 0 - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onBackground, - ), - ); + return IconButton( + key: Key('app-${notificationsImplementationData.id}'), + onPressed: () async { + await _openNotifications(notificationsImplementationData); }, + tooltip: AppLocalizations.of(context).appImplementationName(notificationsImplementationData.id), + icon: StreamBuilder( + stream: notificationsImplementationData.getUnreadCounter(notificationBloc), + builder: (final context, final unreadCounterSnapshot) => NeonAppImplementationIcon( + appImplementation: notificationsImplementationData, + unreadCount: unreadCounterSnapshot.data, + ), + ), ); }, ); diff --git a/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart b/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart index 6a3ba25c..f8c83072 100644 --- a/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart +++ b/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart @@ -1,4 +1,4 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; import 'package:neon/src/models/app_implementation.dart'; @@ -6,7 +6,7 @@ import 'package:neon/src/models/app_implementation.dart'; class NeonAppImplementationIcon extends StatelessWidget { const NeonAppImplementationIcon({ required this.appImplementation, - this.unreadCount = 0, + this.unreadCount, this.color, this.size, super.key, @@ -14,7 +14,7 @@ class NeonAppImplementationIcon extends StatelessWidget { final AppImplementation appImplementation; - final int unreadCount; + final int? unreadCount; final Color? color; @@ -22,6 +22,11 @@ class NeonAppImplementationIcon extends StatelessWidget { @override Widget build(final BuildContext context) { + final unreadCount = this.unreadCount ?? 0; + + final color = this.color ?? + (unreadCount > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onBackground); + final icon = Container( margin: const EdgeInsets.all(5), child: appImplementation.buildIcon( diff --git a/packages/neon/neon/lib/src/widgets/linear_progress_indicator.dart b/packages/neon/neon/lib/src/widgets/linear_progress_indicator.dart index cda19ca0..60901b9b 100644 --- a/packages/neon/neon/lib/src/widgets/linear_progress_indicator.dart +++ b/packages/neon/neon/lib/src/widgets/linear_progress_indicator.dart @@ -17,14 +17,12 @@ class NeonLinearProgressIndicator extends StatelessWidget { @override Widget build(final BuildContext context) => Container( margin: margin, - child: SizedBox( - height: 3, - child: visible - ? LinearProgressIndicator( - color: color, - backgroundColor: backgroundColor, - ) - : null, - ), + constraints: BoxConstraints.loose(const Size.fromHeight(3)), + child: visible + ? LinearProgressIndicator( + color: color, + backgroundColor: backgroundColor, + ) + : null, ); } diff --git a/packages/neon/neon/lib/src/widgets/nextcloud_logo.dart b/packages/neon/neon/lib/src/widgets/nextcloud_logo.dart index c6324ca3..f86e8a73 100644 --- a/packages/neon/neon/lib/src/widgets/nextcloud_logo.dart +++ b/packages/neon/neon/lib/src/widgets/nextcloud_logo.dart @@ -2,15 +2,25 @@ import 'package:flutter/widgets.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:vector_graphics/vector_graphics.dart'; +/// The Nextcloud logo, in widget form. +/// +/// For guidelines on using the Nextcloud logo, visit https://nextcloud.com/trademarks. class NextcloudLogo extends StatelessWidget { + /// Creates a widget that shows the Nextcloud logo. const NextcloudLogo({ + this.size = 100, super.key, }); + /// The size of the logo in logical pixels. + /// + /// The logo will be fit into a square this size. + final double size; + @override Widget build(final BuildContext context) => VectorGraphic( - width: 100, - height: 100, + width: size, + height: size, loader: const AssetBytesLoader( 'assets/logo_nextcloud.svg.vec', packageName: 'neon',