30 changed files with 423 additions and 235 deletions
			
			
		@ -1,4 +1,3 @@ | 
				
			|||||||
export 'package:neon/src/models/account.dart'; | 
					export 'package:neon/src/models/account.dart'; | 
				
			||||||
export 'package:neon/src/models/app_implementation.dart'; | 
					export 'package:neon/src/models/app_implementation.dart'; | 
				
			||||||
export 'package:neon/src/models/branding.dart'; | 
					 | 
				
			||||||
export 'package:neon/src/models/notifications_interface.dart'; | 
					export 'package:neon/src/models/notifications_interface.dart'; | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +0,0 @@ | 
				
			|||||||
import 'package:flutter/widgets.dart'; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@immutable | 
					 | 
				
			||||||
class Branding { | 
					 | 
				
			||||||
  const Branding({ | 
					 | 
				
			||||||
    required this.name, | 
					 | 
				
			||||||
    required this.logo, | 
					 | 
				
			||||||
    this.legalese, | 
					 | 
				
			||||||
  }); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  final String name; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  final Widget logo; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  final String? legalese; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
@ -0,0 +1,66 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/neon.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Custom app branding | 
				
			||||||
 | 
					/// | 
				
			||||||
 | 
					/// Descendant widgets obtain the current [Branding] object using | 
				
			||||||
 | 
					/// `Branding.of(context)`. Instances of [Branding] can be customized with | 
				
			||||||
 | 
					/// [Branding.copyWith]. | 
				
			||||||
 | 
					@immutable | 
				
			||||||
 | 
					class Branding { | 
				
			||||||
 | 
					  /// Creates a custom branding | 
				
			||||||
 | 
					  const Branding({ | 
				
			||||||
 | 
					    required this.name, | 
				
			||||||
 | 
					    required this.logo, | 
				
			||||||
 | 
					    this.legalese, | 
				
			||||||
 | 
					    this.showLoginWithNextcloud = false, | 
				
			||||||
 | 
					  }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// App name | 
				
			||||||
 | 
					  final String name; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Logo of the app shown on various places in the app. | 
				
			||||||
 | 
					  final Widget logo; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// A string to show in small print. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// Typically this is a copyright notice shown as the [AboutDialog.applicationLegalese]. | 
				
			||||||
 | 
					  final String? legalese; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Whether to show the Nextcloud logo on the LoginPage | 
				
			||||||
 | 
					  final bool showLoginWithNextcloud; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Creates a copy of this object but with the given fields replaced with the | 
				
			||||||
 | 
					  /// new values. | 
				
			||||||
 | 
					  Branding copyWith({ | 
				
			||||||
 | 
					    final String? name, | 
				
			||||||
 | 
					    final Widget? logo, | 
				
			||||||
 | 
					    final String? legalese, | 
				
			||||||
 | 
					  }) => | 
				
			||||||
 | 
					      Branding( | 
				
			||||||
 | 
					        name: name ?? this.name, | 
				
			||||||
 | 
					        logo: logo ?? this.logo, | 
				
			||||||
 | 
					        legalese: legalese ?? this.legalese, | 
				
			||||||
 | 
					      ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The data from the closest [Branding] instance given the build context. | 
				
			||||||
 | 
					  static Branding of(final BuildContext context) => Theme.of(context).extension<NeonTheme>()!.branding; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  int get hashCode => Object.hashAll([ | 
				
			||||||
 | 
					        name, | 
				
			||||||
 | 
					        logo, | 
				
			||||||
 | 
					        legalese, | 
				
			||||||
 | 
					      ]); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  bool operator ==(final Object other) { | 
				
			||||||
 | 
					    if (identical(this, other)) { | 
				
			||||||
 | 
					      return true; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    if (other.runtimeType != runtimeType) { | 
				
			||||||
 | 
					      return false; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return other is Branding && name == other.name && logo == other.logo && legalese == other.legalese; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,58 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/colors.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/neon.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A ColorScheme used in the [NeonTheme]. | 
				
			||||||
 | 
					@immutable | 
				
			||||||
 | 
					class NeonColorScheme { | 
				
			||||||
 | 
					  const NeonColorScheme({ | 
				
			||||||
 | 
					    this.primary = NcColors.primary, | 
				
			||||||
 | 
					  }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Primary color used throughout the app. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// See [ColorScheme.primary] | 
				
			||||||
 | 
					  final Color primary; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Creates a copy of this object but with the given fields replaced with the | 
				
			||||||
 | 
					  /// new values. | 
				
			||||||
 | 
					  NeonColorScheme copyWith({ | 
				
			||||||
 | 
					    final Color? primary, | 
				
			||||||
 | 
					    final Color? oledBackground, | 
				
			||||||
 | 
					  }) => | 
				
			||||||
 | 
					      NeonColorScheme( | 
				
			||||||
 | 
					        primary: primary ?? this.primary, | 
				
			||||||
 | 
					      ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The data from the closest [NeonColorScheme] instance given the build context. | 
				
			||||||
 | 
					  static NeonColorScheme of(final BuildContext context) => Theme.of(context).extension<NeonTheme>()!.colorScheme; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Linearly interpolate between two [NeonColorScheme]s. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// {@macro dart.ui.shadow.lerp} | 
				
			||||||
 | 
					  // ignore: prefer_constructors_over_static_methods | 
				
			||||||
 | 
					  static NeonColorScheme lerp(final NeonColorScheme a, final NeonColorScheme b, final double t) { | 
				
			||||||
 | 
					    if (identical(a, b)) { | 
				
			||||||
 | 
					      return a; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return NeonColorScheme( | 
				
			||||||
 | 
					      primary: Color.lerp(a.primary, b.primary, t)!, | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  int get hashCode => Object.hashAll([ | 
				
			||||||
 | 
					        primary, | 
				
			||||||
 | 
					      ]); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  bool operator ==(final Object other) { | 
				
			||||||
 | 
					    if (identical(this, other)) { | 
				
			||||||
 | 
					      return true; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    if (other.runtimeType != runtimeType) { | 
				
			||||||
 | 
					      return false; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return other is NeonColorScheme && other.primary == primary; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,41 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:nextcloud/nextcloud.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// [Color] constants which represent Nextcloud's | 
				
			||||||
 | 
					/// [color palette](https://docs.nextcloud.com/server/latest/developer_manual/design/foundations.html#color). | 
				
			||||||
 | 
					abstract final class NcColors { | 
				
			||||||
 | 
					  /// Nextcloud blue. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// The default primary clolor as specified by the | 
				
			||||||
 | 
					  /// [design guidlines](https://docs.nextcloud.com/server/latest/developer_manual/design/foundations.html#primary-color). | 
				
			||||||
 | 
					  static const Color primary = Color(0xFF0082C9); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The [ColorScheme.background] color used on OLED devices. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// This color is only used at the users discretion. | 
				
			||||||
 | 
					  static const Color oledBackground = Colors.black; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Color of a starred item. | 
				
			||||||
 | 
					  static const Color starredColor = Colors.yellow; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Color used to emphasise declining actions. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// Usually used in conjunction with [NcColors.accept]. | 
				
			||||||
 | 
					  static const Color decline = Colors.red; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Color used to emphasise accepting actions. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// Usually used in conjunction with [NcColors.decline]. | 
				
			||||||
 | 
					  static const Color accept = Colors.green; | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// [UserStatusType] color mapping. | 
				
			||||||
 | 
					extension UserStatusTypeColors on UserStatusType { | 
				
			||||||
 | 
					  /// The color for the user status. | 
				
			||||||
 | 
					  Color? get color => switch (this) { | 
				
			||||||
 | 
					        UserStatusType.online => const Color(0xFF49B382), | 
				
			||||||
 | 
					        UserStatusType.away => const Color(0xFFF4A331), | 
				
			||||||
 | 
					        UserStatusType.dnd => const Color(0xFFED484C), | 
				
			||||||
 | 
					        _ => null, | 
				
			||||||
 | 
					      }; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,64 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/neon.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/widgets/dialog.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Defines a theme for [NeonDialog] widgets. | 
				
			||||||
 | 
					/// | 
				
			||||||
 | 
					/// Descendant widgets obtain the current [NeonDialogTheme] object using | 
				
			||||||
 | 
					/// `NeonDialogTheme.of(context)`. Instances of [NeonDialogTheme] can be customized with | 
				
			||||||
 | 
					/// [NeonDialogTheme.copyWith]. | 
				
			||||||
 | 
					@immutable | 
				
			||||||
 | 
					class NeonDialogTheme { | 
				
			||||||
 | 
					  /// Creates a dialog theme that can be used for [NeonTheme.dialogTheme]. | 
				
			||||||
 | 
					  const NeonDialogTheme({ | 
				
			||||||
 | 
					    this.constraints = const BoxConstraints( | 
				
			||||||
 | 
					      minWidth: 280, | 
				
			||||||
 | 
					      maxWidth: 560, | 
				
			||||||
 | 
					    ), | 
				
			||||||
 | 
					  }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Used to configure the [BoxConstraints] for the [NeonDialog] widget. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// This value should also be used on [Dialog.fullscreen] and other similar pages. | 
				
			||||||
 | 
					  /// By default it follows the default [m3 dialog specification](https://m3.material.io/components/dialogs/specs). | 
				
			||||||
 | 
					  final BoxConstraints constraints; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Creates a copy of this object but with the given fields replaced with the | 
				
			||||||
 | 
					  /// new values. | 
				
			||||||
 | 
					  NeonDialogTheme copyWith({ | 
				
			||||||
 | 
					    final BoxConstraints? constraints, | 
				
			||||||
 | 
					  }) => | 
				
			||||||
 | 
					      NeonDialogTheme( | 
				
			||||||
 | 
					        constraints: constraints ?? this.constraints, | 
				
			||||||
 | 
					      ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The data from the closest [NeonDialogTheme] instance given the build context. | 
				
			||||||
 | 
					  static NeonDialogTheme of(final BuildContext context) => Theme.of(context).extension<NeonTheme>()!.dialogTheme; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Linearly interpolate between two [NeonDialogTheme]s. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// {@macro dart.ui.shadow.lerp} | 
				
			||||||
 | 
					  // ignore: prefer_constructors_over_static_methods | 
				
			||||||
 | 
					  static NeonDialogTheme lerp(final NeonDialogTheme a, final NeonDialogTheme b, final double t) { | 
				
			||||||
 | 
					    if (identical(a, b)) { | 
				
			||||||
 | 
					      return a; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return NeonDialogTheme( | 
				
			||||||
 | 
					      constraints: BoxConstraints.lerp(a.constraints, b.constraints, t)!, | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  int get hashCode => constraints.hashCode; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  bool operator ==(final Object other) { | 
				
			||||||
 | 
					    if (identical(this, other)) { | 
				
			||||||
 | 
					      return true; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    if (other.runtimeType != runtimeType) { | 
				
			||||||
 | 
					      return false; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return other is NeonDialogTheme && other.constraints == constraints; | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,58 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/branding.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/color_scheme.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/dialog.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/widgets/dialog.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Defines the configuration of the overall visual [Theme] for the app | 
				
			||||||
 | 
					/// or a widget subtree within the app. | 
				
			||||||
 | 
					/// | 
				
			||||||
 | 
					/// It is typically only needed to provide a [branding]. | 
				
			||||||
 | 
					@immutable | 
				
			||||||
 | 
					class NeonTheme extends ThemeExtension<NeonTheme> { | 
				
			||||||
 | 
					  /// Create a [NeonTheme] that's used to configure a [Theme]. | 
				
			||||||
 | 
					  const NeonTheme({ | 
				
			||||||
 | 
					    required this.branding, | 
				
			||||||
 | 
					    this.colorScheme = const NeonColorScheme(), | 
				
			||||||
 | 
					    this.dialogTheme = const NeonDialogTheme(), | 
				
			||||||
 | 
					  }); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// A theme for customizing the Branding of the app. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// This is the value returned from [Branding.of]. | 
				
			||||||
 | 
					  final Branding branding; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// A color scheme for customizing the default appearance of the app. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// This is the value returned from [NeonColorScheme.of]. | 
				
			||||||
 | 
					  final NeonColorScheme colorScheme; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// A theme for customizing the visual properties of [NeonDialog]s. | 
				
			||||||
 | 
					  /// | 
				
			||||||
 | 
					  /// This is the value returned from [NeonDialogTheme.of]. | 
				
			||||||
 | 
					  final NeonDialogTheme dialogTheme; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  NeonTheme copyWith({ | 
				
			||||||
 | 
					    final Branding? branding, | 
				
			||||||
 | 
					    final NeonColorScheme? colorScheme, | 
				
			||||||
 | 
					    final NeonDialogTheme? dialogTheme, | 
				
			||||||
 | 
					  }) => | 
				
			||||||
 | 
					      NeonTheme( | 
				
			||||||
 | 
					        branding: branding ?? this.branding, | 
				
			||||||
 | 
					        colorScheme: colorScheme ?? this.colorScheme, | 
				
			||||||
 | 
					        dialogTheme: dialogTheme ?? this.dialogTheme, | 
				
			||||||
 | 
					      ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override | 
				
			||||||
 | 
					  NeonTheme lerp(final NeonTheme? other, final double t) { | 
				
			||||||
 | 
					    if (other is! NeonTheme) { | 
				
			||||||
 | 
					      return this; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    return NeonTheme( | 
				
			||||||
 | 
					      branding: branding, | 
				
			||||||
 | 
					      colorScheme: NeonColorScheme.lerp(colorScheme, other.colorScheme, t), | 
				
			||||||
 | 
					      dialogTheme: NeonDialogTheme.lerp(dialogTheme, other.dialogTheme, t), | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,73 @@ | 
				
			|||||||
 | 
					import 'package:flutter/material.dart'; | 
				
			||||||
 | 
					import 'package:meta/meta.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/colors.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/theme/neon.dart'; | 
				
			||||||
 | 
					import 'package:neon/src/utils/hex_color.dart'; | 
				
			||||||
 | 
					import 'package:nextcloud/nextcloud.dart'; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@internal | 
				
			||||||
 | 
					@immutable | 
				
			||||||
 | 
					class AppTheme { | 
				
			||||||
 | 
					  const AppTheme( | 
				
			||||||
 | 
					    this.nextcloudTheme, { | 
				
			||||||
 | 
					    required this.neonTheme, | 
				
			||||||
 | 
					    final bool keepOriginalAccentColor = false, | 
				
			||||||
 | 
					    this.oledAsDark = false, | 
				
			||||||
 | 
					    this.appThemes, | 
				
			||||||
 | 
					  }) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final CoreServerCapabilities_Ocs_Data_Capabilities_Theming? nextcloudTheme; | 
				
			||||||
 | 
					  final bool keepOriginalAccentColor; | 
				
			||||||
 | 
					  final bool oledAsDark; | 
				
			||||||
 | 
					  final Iterable<ThemeExtension>? appThemes; | 
				
			||||||
 | 
					  final NeonTheme neonTheme; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ColorScheme _buildColorScheme(final Brightness brightness) { | 
				
			||||||
 | 
					    final primary = nextcloudTheme?.color != null ? HexColor(nextcloudTheme!.color!) : neonTheme.colorScheme.primary; | 
				
			||||||
 | 
					    final keepOriginalAccentColorOverride = keepOriginalAccentColor ? primary : null; | 
				
			||||||
 | 
					    final oledBackgroundOverride = oledAsDark && brightness == Brightness.dark ? NcColors.oledBackground : null; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ColorScheme.fromSeed( | 
				
			||||||
 | 
					      seedColor: primary, | 
				
			||||||
 | 
					      brightness: brightness, | 
				
			||||||
 | 
					    ).copyWith( | 
				
			||||||
 | 
					      background: oledBackgroundOverride, | 
				
			||||||
 | 
					      primary: keepOriginalAccentColorOverride, | 
				
			||||||
 | 
					      secondary: keepOriginalAccentColorOverride, | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ThemeData _getTheme(final Brightness brightness) { | 
				
			||||||
 | 
					    final colorScheme = _buildColorScheme(brightness); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ThemeData( | 
				
			||||||
 | 
					      useMaterial3: true, | 
				
			||||||
 | 
					      colorScheme: colorScheme, | 
				
			||||||
 | 
					      scaffoldBackgroundColor: colorScheme.background, | 
				
			||||||
 | 
					      cardColor: colorScheme.background, // For LicensePage | 
				
			||||||
 | 
					      snackBarTheme: _snackBarTheme, | 
				
			||||||
 | 
					      dividerTheme: _dividerTheme, | 
				
			||||||
 | 
					      scrollbarTheme: _scrollbarTheme, | 
				
			||||||
 | 
					      extensions: [ | 
				
			||||||
 | 
					        neonTheme, | 
				
			||||||
 | 
					        ...?appThemes, | 
				
			||||||
 | 
					      ], | 
				
			||||||
 | 
					    ); | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ThemeData get lightTheme => _getTheme(Brightness.light); | 
				
			||||||
 | 
					  ThemeData get darkTheme => _getTheme(Brightness.dark); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static const _snackBarTheme = SnackBarThemeData( | 
				
			||||||
 | 
					    behavior: SnackBarBehavior.floating, | 
				
			||||||
 | 
					  ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static const _dividerTheme = DividerThemeData( | 
				
			||||||
 | 
					    thickness: 1.5, | 
				
			||||||
 | 
					    space: 30, | 
				
			||||||
 | 
					  ); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static const _scrollbarTheme = ScrollbarThemeData( | 
				
			||||||
 | 
					    interactive: true, | 
				
			||||||
 | 
					  ); | 
				
			||||||
 | 
					} | 
				
			||||||
@ -1,164 +0,0 @@ | 
				
			|||||||
import 'package:flutter/material.dart'; | 
					 | 
				
			||||||
import 'package:meta/meta.dart'; | 
					 | 
				
			||||||
import 'package:neon/src/utils/hex_color.dart'; | 
					 | 
				
			||||||
import 'package:neon/src/widgets/dialog.dart'; | 
					 | 
				
			||||||
import 'package:nextcloud/nextcloud.dart'; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@internal | 
					 | 
				
			||||||
const themePrimaryColor = Color(0xFFF37736); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@internal | 
					 | 
				
			||||||
@immutable | 
					 | 
				
			||||||
class AppTheme { | 
					 | 
				
			||||||
  AppTheme( | 
					 | 
				
			||||||
    this.nextcloudTheme, { | 
					 | 
				
			||||||
    final bool keepOriginalAccentColor = false, | 
					 | 
				
			||||||
    this.oledAsDark = false, | 
					 | 
				
			||||||
    this.appThemes, | 
					 | 
				
			||||||
  }) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  final CoreServerCapabilities_Ocs_Data_Capabilities_Theming? nextcloudTheme; | 
					 | 
				
			||||||
  final bool keepOriginalAccentColor; | 
					 | 
				
			||||||
  final bool oledAsDark; | 
					 | 
				
			||||||
  final Iterable<ThemeExtension>? appThemes; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  late final _primaryColor = nextcloudTheme?.color != null ? HexColor(nextcloudTheme!.color!) : themePrimaryColor; | 
					 | 
				
			||||||
  late final _keepOriginalAccentColorOverride = keepOriginalAccentColor ? _primaryColor : null; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ColorScheme _buildColorScheme(final Brightness brightness) { | 
					 | 
				
			||||||
    final oledBackgroundOverride = oledAsDark && brightness == Brightness.dark ? Colors.black : null; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ColorScheme.fromSeed( | 
					 | 
				
			||||||
      seedColor: _primaryColor, | 
					 | 
				
			||||||
      brightness: brightness, | 
					 | 
				
			||||||
    ).copyWith( | 
					 | 
				
			||||||
      background: oledBackgroundOverride, | 
					 | 
				
			||||||
      primary: _keepOriginalAccentColorOverride, | 
					 | 
				
			||||||
      secondary: _keepOriginalAccentColorOverride, | 
					 | 
				
			||||||
    ); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ThemeData _getTheme(final Brightness brightness) { | 
					 | 
				
			||||||
    final colorScheme = _buildColorScheme(brightness); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ThemeData( | 
					 | 
				
			||||||
      useMaterial3: true, | 
					 | 
				
			||||||
      colorScheme: colorScheme, | 
					 | 
				
			||||||
      scaffoldBackgroundColor: colorScheme.background, | 
					 | 
				
			||||||
      cardColor: colorScheme.background, // For LicensePage | 
					 | 
				
			||||||
      snackBarTheme: _snackBarTheme, | 
					 | 
				
			||||||
      dividerTheme: _dividerTheme, | 
					 | 
				
			||||||
      extensions: [ | 
					 | 
				
			||||||
        const NeonTheme(), | 
					 | 
				
			||||||
        ...?appThemes, | 
					 | 
				
			||||||
      ], | 
					 | 
				
			||||||
    ); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ThemeData get lightTheme => _getTheme(Brightness.light); | 
					 | 
				
			||||||
  ThemeData get darkTheme => _getTheme(Brightness.dark); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static const _snackBarTheme = SnackBarThemeData( | 
					 | 
				
			||||||
    behavior: SnackBarBehavior.floating, | 
					 | 
				
			||||||
  ); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static const _dividerTheme = DividerThemeData( | 
					 | 
				
			||||||
    thickness: 1.5, | 
					 | 
				
			||||||
    space: 30, | 
					 | 
				
			||||||
  ); | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Defines the configuration of the overall visual [Theme] for a NeonApp | 
					 | 
				
			||||||
/// or a widget subtree within the app. | 
					 | 
				
			||||||
@internal | 
					 | 
				
			||||||
@immutable | 
					 | 
				
			||||||
class NeonTheme extends ThemeExtension<NeonTheme> { | 
					 | 
				
			||||||
  /// Create a [NeonTheme] that's used to configure a [Theme]. | 
					 | 
				
			||||||
  const NeonTheme({ | 
					 | 
				
			||||||
    this.dialogTheme = const NeonDialogTheme(), | 
					 | 
				
			||||||
  }); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// A theme for customizing the visual properties of [NeonDialog]s. | 
					 | 
				
			||||||
  /// | 
					 | 
				
			||||||
  /// This is the value returned from [NeonDialogTheme.of]. | 
					 | 
				
			||||||
  final NeonDialogTheme dialogTheme; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override | 
					 | 
				
			||||||
  NeonTheme copyWith({ | 
					 | 
				
			||||||
    final NeonDialogTheme? dialogTheme, | 
					 | 
				
			||||||
  }) => | 
					 | 
				
			||||||
      NeonTheme( | 
					 | 
				
			||||||
        dialogTheme: dialogTheme ?? this.dialogTheme, | 
					 | 
				
			||||||
      ); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override | 
					 | 
				
			||||||
  NeonTheme lerp(final NeonTheme? other, final double t) { | 
					 | 
				
			||||||
    if (other is! NeonTheme) { | 
					 | 
				
			||||||
      return this; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    return NeonTheme( | 
					 | 
				
			||||||
      dialogTheme: NeonDialogTheme.lerp(dialogTheme, other.dialogTheme, t), | 
					 | 
				
			||||||
    ); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Defines a theme for [NeonDialog] widgets. | 
					 | 
				
			||||||
/// | 
					 | 
				
			||||||
/// Descendant widgets obtain the current [NeonDialogTheme] object using | 
					 | 
				
			||||||
/// `NeonDialogTheme.of(context)`. Instances of [NeonDialogTheme] can be customized with | 
					 | 
				
			||||||
/// [NeonDialogTheme.copyWith]. | 
					 | 
				
			||||||
@immutable | 
					 | 
				
			||||||
class NeonDialogTheme { | 
					 | 
				
			||||||
  /// Creates a dialog theme that can be used for [NeonTheme.dialogTheme]. | 
					 | 
				
			||||||
  const NeonDialogTheme({ | 
					 | 
				
			||||||
    this.constraints = const BoxConstraints( | 
					 | 
				
			||||||
      minWidth: 280, | 
					 | 
				
			||||||
      maxWidth: 560, | 
					 | 
				
			||||||
    ), | 
					 | 
				
			||||||
  }); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Used to configure the [BoxConstraints] for the [NeonDialog] widget. | 
					 | 
				
			||||||
  /// | 
					 | 
				
			||||||
  /// This value should also be used on [Dialog.fullscreen] and other similar pages. | 
					 | 
				
			||||||
  /// By default it follows the default [m3 dialog specification](https://m3.material.io/components/dialogs/specs). | 
					 | 
				
			||||||
  final BoxConstraints constraints; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Creates a copy of this object but with the given fields replaced with the | 
					 | 
				
			||||||
  /// new values. | 
					 | 
				
			||||||
  NeonDialogTheme copyWith({ | 
					 | 
				
			||||||
    final BoxConstraints? constraints, | 
					 | 
				
			||||||
  }) => | 
					 | 
				
			||||||
      NeonDialogTheme( | 
					 | 
				
			||||||
        constraints: constraints ?? this.constraints, | 
					 | 
				
			||||||
      ); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// The data from the closest [NeonDialogTheme] instance given the build context. | 
					 | 
				
			||||||
  static NeonDialogTheme of(final BuildContext context) => Theme.of(context).extension<NeonTheme>()!.dialogTheme; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Linearly interpolate between two neon dialog themes. | 
					 | 
				
			||||||
  /// | 
					 | 
				
			||||||
  /// {@macro dart.ui.shadow.lerp} | 
					 | 
				
			||||||
  // ignore: prefer_constructors_over_static_methods | 
					 | 
				
			||||||
  static NeonDialogTheme lerp(final NeonDialogTheme a, final NeonDialogTheme b, final double t) { | 
					 | 
				
			||||||
    if (identical(a, b)) { | 
					 | 
				
			||||||
      return a; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    return NeonDialogTheme( | 
					 | 
				
			||||||
      constraints: BoxConstraints.lerp(a.constraints, b.constraints, t)!, | 
					 | 
				
			||||||
    ); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override | 
					 | 
				
			||||||
  int get hashCode => constraints.hashCode; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override | 
					 | 
				
			||||||
  bool operator ==(final Object other) { | 
					 | 
				
			||||||
    if (identical(this, other)) { | 
					 | 
				
			||||||
      return true; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    if (other.runtimeType != runtimeType) { | 
					 | 
				
			||||||
      return false; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    return other is NeonDialogTheme && other.constraints == constraints; | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
@ -0,0 +1,5 @@ | 
				
			|||||||
 | 
					export 'src/theme/branding.dart'; | 
				
			||||||
 | 
					export 'src/theme/color_scheme.dart'; | 
				
			||||||
 | 
					export 'src/theme/colors.dart'; | 
				
			||||||
 | 
					export 'src/theme/dialog.dart'; | 
				
			||||||
 | 
					export 'src/theme/neon.dart'; | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue