Profile decoration

This commit is contained in:
2025-12-10 23:11:46 +08:00
parent d7746d14e4
commit d8485954fa
4 changed files with 577 additions and 174 deletions

View File

@@ -16,6 +16,7 @@ import 'package:island/widgets/account/leveling_progress.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/profile_decoration.dart';
import 'package:island/widgets/debug_sheet.dart'; import 'package:island/widgets/debug_sheet.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@@ -98,6 +99,10 @@ class AccountScreen extends HookConsumerWidget {
child: ProfilePictureWidget( child: ProfilePictureWidget(
file: user.value?.profile.picture, file: user.value?.profile.picture,
radius: 32, radius: 32,
decoration: ProfileDecoration(
text: '#OpenToWork',
color: Colors.green,
),
), ),
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
@@ -331,23 +336,21 @@ class AccountScreen extends HookConsumerWidget {
if (availableWidth > totalMin) { if (availableWidth > totalMin) {
return Row( return Row(
spacing: 8, spacing: 8,
children: children: children
children .map((child) => Expanded(child: child))
.map((child) => Expanded(child: child)) .toList(),
.toList(),
).padding(horizontal: 12).height(48); ).padding(horizontal: 12).height(48);
} else { } else {
return SingleChildScrollView( return SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
child: Row( child: Row(
spacing: 8, spacing: 8,
children: children: children
children .map(
.map( (child) =>
(child) => SizedBox(width: minWidth, child: child),
SizedBox(width: minWidth, child: child), )
) .toList(),
.toList(),
).padding(horizontal: 12), ).padding(horizontal: 12),
).height(48); ).height(48);
} }
@@ -495,97 +498,96 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
appBar: AppBar(title: const Text('account').tr()), appBar: AppBar(title: const Text('account').tr()),
body: body: ConstrainedBox(
ConstrainedBox( constraints: const BoxConstraints(maxWidth: 360),
constraints: const BoxConstraints(maxWidth: 360), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Card(
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
onTap: () {
context.pushNamed('createAccount');
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(Symbols.person_add, size: 48),
const SizedBox(height: 8),
Text('createAccount').tr().bold(),
Text('createAccountDescription').tr(),
],
),
),
),
),
),
const Gap(8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Card(
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
onTap: () {
context.pushNamed('login');
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(Symbols.login, size: 48),
const SizedBox(height: 8),
Text('login').tr().bold(),
Text('loginDescription').tr(),
],
),
),
),
),
),
const Gap(8),
Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: [
Padding( IconButton(
padding: const EdgeInsets.symmetric(horizontal: 24), onPressed: () {
child: Card( context.pushNamed('about');
child: InkWell( },
borderRadius: const BorderRadius.all(Radius.circular(8)), iconSize: 18,
onTap: () { color: Theme.of(context).colorScheme.secondary,
context.pushNamed('createAccount'); icon: const Icon(Icons.info, fill: 1),
}, tooltip: 'about'.tr(),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(Symbols.person_add, size: 48),
const SizedBox(height: 8),
Text('createAccount').tr().bold(),
Text('createAccountDescription').tr(),
],
),
),
),
),
), ),
const Gap(8), IconButton(
Padding( icon: const Icon(Icons.bug_report, fill: 1),
padding: const EdgeInsets.symmetric(horizontal: 24), onPressed: () {
child: Card( showModalBottomSheet(
child: InkWell( context: context,
borderRadius: const BorderRadius.all(Radius.circular(8)), builder: (context) => DebugSheet(),
onTap: () { );
context.pushNamed('login'); },
}, iconSize: 18,
child: Padding( color: Theme.of(context).colorScheme.secondary,
padding: const EdgeInsets.all(16), tooltip: 'debugOptions'.tr(),
child: Column(
children: [
Icon(Symbols.login, size: 48),
const SizedBox(height: 8),
Text('login').tr().bold(),
Text('loginDescription').tr(),
],
),
),
),
),
), ),
const Gap(8), IconButton(
Row( onPressed: () {
mainAxisAlignment: MainAxisAlignment.center, context.pushNamed('settings');
children: [ },
IconButton( icon: const Icon(Icons.settings, fill: 1),
onPressed: () { iconSize: 18,
context.pushNamed('about'); color: Theme.of(context).colorScheme.secondary,
}, tooltip: 'appSettings'.tr(),
iconSize: 18,
color: Theme.of(context).colorScheme.secondary,
icon: const Icon(Icons.info, fill: 1),
tooltip: 'about'.tr(),
),
IconButton(
icon: const Icon(Icons.bug_report, fill: 1),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => DebugSheet(),
);
},
iconSize: 18,
color: Theme.of(context).colorScheme.secondary,
tooltip: 'debugOptions'.tr(),
),
IconButton(
onPressed: () {
context.pushNamed('settings');
},
icon: const Icon(Icons.settings, fill: 1),
iconSize: 18,
color: Theme.of(context).colorScheme.secondary,
tooltip: 'appSettings'.tr(),
),
],
), ),
], ],
), ),
).center(), ],
),
).center(),
); );
} }
} }

View File

@@ -9,8 +9,11 @@ import 'package:island/models/file.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/services/time.dart'; import 'package:island/services/time.dart';
import 'package:island/utils/format.dart'; import 'package:island/utils/format.dart';
import 'package:island/widgets/content/profile_decoration.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:island/widgets/data_saving_gate.dart'; import 'package:island/widgets/data_saving_gate.dart';
import 'file_viewer_contents.dart'; import 'file_viewer_contents.dart';
@@ -258,17 +261,15 @@ class CloudFileWidget extends HookConsumerWidget {
var content = switch (item.mimeType?.split('/').firstOrNull) { var content = switch (item.mimeType?.split('/').firstOrNull) {
'image' => AspectRatio( 'image' => AspectRatio(
aspectRatio: ratio, aspectRatio: ratio,
child: child: (useInternalGate && dataSaving && !unlocked.value)
(useInternalGate && dataSaving && !unlocked.value) ? dataPlaceHolder(Symbols.image)
? dataPlaceHolder(Symbols.image) : cloudImage(),
: cloudImage(),
), ),
'video' => AspectRatio( 'video' => AspectRatio(
aspectRatio: ratio, aspectRatio: ratio,
child: child: (useInternalGate && dataSaving && !unlocked.value)
(useInternalGate && dataSaving && !unlocked.value) ? dataPlaceHolder(Symbols.play_arrow)
? dataPlaceHolder(Symbols.play_arrow) : cloudVideo(),
: cloudVideo(),
), ),
'audio' => AudioFileContent(item: item, uri: uri), 'audio' => AudioFileContent(item: item, uri: uri),
_ => Builder( _ => Builder(
@@ -383,10 +384,9 @@ class CloudVideoWidget extends HookConsumerWidget {
final serverUrl = ref.watch(serverUrlProvider); final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/drive/files/${item.id}'; final uri = '$serverUrl/drive/files/${item.id}';
var ratio = var ratio = item.fileMeta?['ratio'] is num
item.fileMeta?['ratio'] is num ? item.fileMeta!['ratio'].toDouble()
? item.fileMeta!['ratio'].toDouble() : 1.0;
: 1.0;
if (ratio == 0) ratio = 1.0; if (ratio == 0) ratio = 1.0;
if (open.value) { if (open.value) {
@@ -533,10 +533,9 @@ class CloudImageWidget extends ConsumerWidget {
return AspectRatio( return AspectRatio(
aspectRatio: aspectRatio, aspectRatio: aspectRatio,
child: child: file != null
file != null ? CloudFileWidget(item: file!, fit: fit)
? CloudFileWidget(item: file!, fit: fit) : UniversalImage(uri: uri, blurHash: blurHash, fit: fit),
: UniversalImage(uri: uri, blurHash: blurHash, fit: fit),
); );
} }
@@ -545,10 +544,9 @@ class CloudImageWidget extends ConsumerWidget {
required String serverUrl, required String serverUrl,
bool original = false, bool original = false,
}) { }) {
final uri = final uri = original
original ? '$serverUrl/drive/files/$fileId?original=true'
? '$serverUrl/drive/files/$fileId?original=true' : '$serverUrl/drive/files/$fileId';
: '$serverUrl/drive/files/$fileId';
return CachedNetworkImageProvider(uri); return CachedNetworkImageProvider(uri);
} }
} }
@@ -560,6 +558,7 @@ class ProfilePictureWidget extends ConsumerWidget {
final double? borderRadius; final double? borderRadius;
final IconData? fallbackIcon; final IconData? fallbackIcon;
final Color? fallbackColor; final Color? fallbackColor;
final ProfileDecoration? decoration;
const ProfilePictureWidget({ const ProfilePictureWidget({
super.key, super.key,
this.fileId, this.fileId,
@@ -568,6 +567,7 @@ class ProfilePictureWidget extends ConsumerWidget {
this.borderRadius, this.borderRadius,
this.fallbackIcon, this.fallbackIcon,
this.fallbackColor, this.fallbackColor,
this.decoration,
}); });
@override @override
@@ -575,36 +575,49 @@ class ProfilePictureWidget extends ConsumerWidget {
final serverUrl = ref.watch(serverUrlProvider); final serverUrl = ref.watch(serverUrlProvider);
final String? id = file?.id ?? fileId; final String? id = file?.id ?? fileId;
final fallback = final fallback = Icon(
Icon( fallbackIcon ?? Symbols.account_circle,
fallbackIcon ?? Symbols.account_circle, size: radius,
size: radius, color: fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer,
color: ).center();
fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer,
).center(); final image = id == null
? fallback
: DataSavingGate(
bypass: true,
placeholder: fallback,
content: () => UniversalImage(
uri: '$serverUrl/drive/files/$id',
fit: BoxFit.cover,
),
);
Widget content = Container(
width: radius * 2,
height: radius * 2,
color: Theme.of(context).colorScheme.primaryContainer,
child: decoration != null
? Stack(
fit: StackFit.expand,
children: [
image,
CustomPaint(
painter: _ProfileDecorationPainter(
text: decoration!.text,
color: decoration!.color,
textColor: decoration!.textColor ?? Colors.white,
),
),
],
)
: image,
);
return ClipRRect( return ClipRRect(
borderRadius: borderRadius: borderRadius == null
borderRadius == null ? BorderRadius.all(Radius.circular(radius))
? BorderRadius.all(Radius.circular(radius)) : BorderRadius.all(Radius.circular(borderRadius!)),
: BorderRadius.all(Radius.circular(borderRadius!)), child: content,
child: Container(
width: radius * 2,
height: radius * 2,
color: Theme.of(context).colorScheme.primaryContainer,
child:
id == null
? fallback
: DataSavingGate(
bypass: true,
placeholder: fallback,
content:
() => UniversalImage(
uri: '$serverUrl/drive/files/$id',
fit: BoxFit.cover,
),
),
),
); );
} }
} }
@@ -716,32 +729,29 @@ class SplitAvatarWidget extends ConsumerWidget {
), ),
), ),
Expanded( Expanded(
child: child: filesId.length > 4
filesId.length > 4 ? Container(
? Container( color: Theme.of(
color: context,
Theme.of( ).colorScheme.primaryContainer,
child: Center(
child: Text(
'+${filesId.length - 3}',
style: TextStyle(
fontSize: radius * 0.4,
color: Theme.of(
context, context,
).colorScheme.primaryContainer, ).colorScheme.onPrimaryContainer,
child: Center(
child: Text(
'+${filesId.length - 3}',
style: TextStyle(
fontSize: radius * 0.4,
color:
Theme.of(
context,
).colorScheme.onPrimaryContainer,
),
), ),
), ),
)
: _buildQuadrant(
context,
filesId[3],
ref,
radius,
), ),
)
: _buildQuadrant(
context,
filesId[3],
ref,
radius,
),
), ),
], ],
), ),
@@ -765,14 +775,12 @@ class SplitAvatarWidget extends ConsumerWidget {
width: radius, width: radius,
height: radius, height: radius,
color: Theme.of(context).colorScheme.primaryContainer, color: Theme.of(context).colorScheme.primaryContainer,
child: child: Icon(
Icon( fallbackIcon,
fallbackIcon, size: radius * 0.6,
size: radius * 0.6, color:
color: fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer,
fallbackColor ?? ).center(),
Theme.of(context).colorScheme.onPrimaryContainer,
).center(),
); );
} }
@@ -786,3 +794,106 @@ class SplitAvatarWidget extends ConsumerWidget {
); );
} }
} }
class _ProfileDecorationPainter extends CustomPainter {
final String text;
final Color color;
final Color textColor;
_ProfileDecorationPainter({
required this.text,
required this.color,
required this.textColor,
});
@override
void paint(Canvas canvas, Size size) {
if (text.isEmpty) return;
final radius = size.width / 2;
final center = Offset(size.width / 2, size.height / 2);
final strokeWidth = radius * 0.4; // Increased thickness
final centerAngle = 3 * math.pi / 4;
final sweepAngle = math.pi / 1;
final startAngle = centerAngle - (sweepAngle / 2);
final arcRadius = radius - (strokeWidth / 2);
final rect = Rect.fromCircle(center: center, radius: arcRadius);
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth
..shader = SweepGradient(
startAngle: startAngle,
endAngle: startAngle + sweepAngle,
colors: [color.withOpacity(0), color, color, color.withOpacity(0)],
stops: const [0.0, 0.25, 0.75, 1.0],
).createShader(rect);
canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
_drawTextOnArc(canvas, center, arcRadius, text, centerAngle);
}
void _drawTextOnArc(
Canvas canvas,
Offset center,
double radius,
String text,
double centerAngle,
) {
final textStyle = TextStyle(
color: textColor,
fontSize: radius * 0.28,
fontWeight: FontWeight.bold,
);
double totalAngle = 0;
List<double> charAngles = [];
// Calculate total angle occupied by text
for (int i = 0; i < text.length; i++) {
final char = text[i];
final span = TextSpan(text: char, style: textStyle);
final tp = TextPainter(text: span, textDirection: ui.TextDirection.ltr);
tp.layout();
final charWidth = tp.width;
final angle = charWidth / radius;
charAngles.add(angle);
totalAngle += angle;
}
// Start from "Left" of the center (High angle)
// We want to traverse from centerAngle + total/2 to centerAngle - total/2
double currentAngle = centerAngle + (totalAngle / 2);
for (int i = 0; i < text.length; i++) {
final char = text[i];
final span = TextSpan(text: char, style: textStyle);
final tp = TextPainter(text: span, textDirection: ui.TextDirection.ltr);
tp.layout();
final charAngle = charAngles[i];
final midCharAngle = currentAngle - charAngle / 2;
final x = center.dx + radius * math.cos(midCharAngle);
final y = center.dy + radius * math.sin(midCharAngle);
canvas.save();
canvas.translate(x, y);
canvas.rotate(midCharAngle - math.pi / 2);
tp.paint(canvas, Offset(-tp.width / 2, -tp.height / 2));
canvas.restore();
currentAngle -= charAngle;
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'profile_decoration.freezed.dart';
@freezed
sealed class ProfileDecoration with _$ProfileDecoration {
const factory ProfileDecoration({
required String text,
required Color color,
Color? textColor,
}) = _ProfileDecoration;
}

View File

@@ -0,0 +1,277 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'profile_decoration.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ProfileDecoration {
String get text; Color get color; Color? get textColor;
/// Create a copy of ProfileDecoration
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ProfileDecorationCopyWith<ProfileDecoration> get copyWith => _$ProfileDecorationCopyWithImpl<ProfileDecoration>(this as ProfileDecoration, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfileDecoration&&(identical(other.text, text) || other.text == text)&&(identical(other.color, color) || other.color == color)&&(identical(other.textColor, textColor) || other.textColor == textColor));
}
@override
int get hashCode => Object.hash(runtimeType,text,color,textColor);
@override
String toString() {
return 'ProfileDecoration(text: $text, color: $color, textColor: $textColor)';
}
}
/// @nodoc
abstract mixin class $ProfileDecorationCopyWith<$Res> {
factory $ProfileDecorationCopyWith(ProfileDecoration value, $Res Function(ProfileDecoration) _then) = _$ProfileDecorationCopyWithImpl;
@useResult
$Res call({
String text, Color color, Color? textColor
});
}
/// @nodoc
class _$ProfileDecorationCopyWithImpl<$Res>
implements $ProfileDecorationCopyWith<$Res> {
_$ProfileDecorationCopyWithImpl(this._self, this._then);
final ProfileDecoration _self;
final $Res Function(ProfileDecoration) _then;
/// Create a copy of ProfileDecoration
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? text = null,Object? color = null,Object? textColor = freezed,}) {
return _then(_self.copyWith(
text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
as String,color: null == color ? _self.color : color // ignore: cast_nullable_to_non_nullable
as Color,textColor: freezed == textColor ? _self.textColor : textColor // ignore: cast_nullable_to_non_nullable
as Color?,
));
}
}
/// Adds pattern-matching-related methods to [ProfileDecoration].
extension ProfileDecorationPatterns on ProfileDecoration {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProfileDecoration value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ProfileDecoration() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProfileDecoration value) $default,){
final _that = this;
switch (_that) {
case _ProfileDecoration():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProfileDecoration value)? $default,){
final _that = this;
switch (_that) {
case _ProfileDecoration() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String text, Color color, Color? textColor)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ProfileDecoration() when $default != null:
return $default(_that.text,_that.color,_that.textColor);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String text, Color color, Color? textColor) $default,) {final _that = this;
switch (_that) {
case _ProfileDecoration():
return $default(_that.text,_that.color,_that.textColor);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String text, Color color, Color? textColor)? $default,) {final _that = this;
switch (_that) {
case _ProfileDecoration() when $default != null:
return $default(_that.text,_that.color,_that.textColor);case _:
return null;
}
}
}
/// @nodoc
class _ProfileDecoration implements ProfileDecoration {
const _ProfileDecoration({required this.text, required this.color, this.textColor});
@override final String text;
@override final Color color;
@override final Color? textColor;
/// Create a copy of ProfileDecoration
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ProfileDecorationCopyWith<_ProfileDecoration> get copyWith => __$ProfileDecorationCopyWithImpl<_ProfileDecoration>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfileDecoration&&(identical(other.text, text) || other.text == text)&&(identical(other.color, color) || other.color == color)&&(identical(other.textColor, textColor) || other.textColor == textColor));
}
@override
int get hashCode => Object.hash(runtimeType,text,color,textColor);
@override
String toString() {
return 'ProfileDecoration(text: $text, color: $color, textColor: $textColor)';
}
}
/// @nodoc
abstract mixin class _$ProfileDecorationCopyWith<$Res> implements $ProfileDecorationCopyWith<$Res> {
factory _$ProfileDecorationCopyWith(_ProfileDecoration value, $Res Function(_ProfileDecoration) _then) = __$ProfileDecorationCopyWithImpl;
@override @useResult
$Res call({
String text, Color color, Color? textColor
});
}
/// @nodoc
class __$ProfileDecorationCopyWithImpl<$Res>
implements _$ProfileDecorationCopyWith<$Res> {
__$ProfileDecorationCopyWithImpl(this._self, this._then);
final _ProfileDecoration _self;
final $Res Function(_ProfileDecoration) _then;
/// Create a copy of ProfileDecoration
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? text = null,Object? color = null,Object? textColor = freezed,}) {
return _then(_ProfileDecoration(
text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable
as String,color: null == color ? _self.color : color // ignore: cast_nullable_to_non_nullable
as Color,textColor: freezed == textColor ? _self.textColor : textColor // ignore: cast_nullable_to_non_nullable
as Color?,
));
}
}
// dart format on