refactor: animation dots
This commit is contained in:
@@ -741,8 +741,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 4, bottom: 4),
|
padding: const EdgeInsets.only(left: 4, bottom: 4),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 18,
|
height: 22,
|
||||||
child: _buildTypingDot(),
|
child: Platform.isIOS
|
||||||
|
? _buildTypingPillBubble()
|
||||||
|
: _buildTypingEllipsis(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -751,32 +753,114 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTypingDot() {
|
Widget _buildTypingEllipsis() {
|
||||||
final min = AnimationValues.typingIndicatorScale;
|
final min = AnimationValues.typingIndicatorScale;
|
||||||
return Container(
|
final dotColor = context.conduitTheme.textSecondary.withValues(alpha: 0.75);
|
||||||
width: 14,
|
|
||||||
height: 14,
|
const double dotSize = 6.0;
|
||||||
decoration: BoxDecoration(
|
const double gap = Spacing.xs; // 4.0
|
||||||
color: context.conduitTheme.textSecondary.withValues(alpha: 0.6),
|
final d = AnimationDelay.typingDelay;
|
||||||
shape: BoxShape.circle,
|
final d2 = Duration(milliseconds: d.inMilliseconds * 2);
|
||||||
),
|
|
||||||
)
|
Widget dot(Duration delay) {
|
||||||
.animate(onPlay: (controller) => controller.repeat())
|
return Container(
|
||||||
.scale(
|
width: dotSize,
|
||||||
duration: AnimationDuration.typingIndicator,
|
height: dotSize,
|
||||||
curve: AnimationCurves.typingIndicator,
|
decoration: BoxDecoration(
|
||||||
begin: Offset(min, min),
|
color: dotColor,
|
||||||
end: const Offset(1, 1),
|
shape: BoxShape.circle,
|
||||||
)
|
),
|
||||||
.then(delay: AnimationDelay.typingDelay)
|
)
|
||||||
.scale(
|
.animate(onPlay: (controller) => controller.repeat())
|
||||||
duration: AnimationDuration.typingIndicator,
|
.then(delay: delay)
|
||||||
curve: AnimationCurves.typingIndicator,
|
.scale(
|
||||||
begin: const Offset(1, 1),
|
duration: AnimationDuration.typingIndicator,
|
||||||
end: Offset(min, min),
|
curve: AnimationCurves.typingIndicator,
|
||||||
);
|
begin: Offset(min, min),
|
||||||
|
end: const Offset(1, 1),
|
||||||
|
)
|
||||||
|
.then(delay: AnimationDelay.typingDelay)
|
||||||
|
.scale(
|
||||||
|
duration: AnimationDuration.typingIndicator,
|
||||||
|
curve: AnimationCurves.typingIndicator,
|
||||||
|
begin: const Offset(1, 1),
|
||||||
|
end: Offset(min, min),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
dot(Duration.zero),
|
||||||
|
const SizedBox(width: gap),
|
||||||
|
dot(d),
|
||||||
|
const SizedBox(width: gap),
|
||||||
|
dot(d2),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTypingPillBubble() {
|
||||||
|
final min = AnimationValues.typingIndicatorScale;
|
||||||
|
|
||||||
|
final bubbleColor = context.conduitTheme.surfaceContainerHighest;
|
||||||
|
final dotColor = context.conduitTheme.textSecondary.withValues(alpha: 0.75);
|
||||||
|
|
||||||
|
const double dotSize = 6.0;
|
||||||
|
const double gap = Spacing.xs; // 4.0
|
||||||
|
const double padV = 6.0;
|
||||||
|
const double padH = 10.0;
|
||||||
|
|
||||||
|
final d = AnimationDelay.typingDelay;
|
||||||
|
final d2 = Duration(milliseconds: d.inMilliseconds * 2);
|
||||||
|
|
||||||
|
Widget dot(Duration delay) {
|
||||||
|
return Container(
|
||||||
|
width: dotSize,
|
||||||
|
height: dotSize,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: dotColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.animate(onPlay: (controller) => controller.repeat())
|
||||||
|
.then(delay: delay)
|
||||||
|
.scale(
|
||||||
|
duration: AnimationDuration.typingIndicator,
|
||||||
|
curve: AnimationCurves.typingIndicator,
|
||||||
|
begin: Offset(min, min),
|
||||||
|
end: const Offset(1, 1),
|
||||||
|
)
|
||||||
|
.then(delay: AnimationDelay.typingDelay)
|
||||||
|
.scale(
|
||||||
|
duration: AnimationDuration.typingIndicator,
|
||||||
|
curve: AnimationCurves.typingIndicator,
|
||||||
|
begin: const Offset(1, 1),
|
||||||
|
end: Offset(min, min),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: padH, vertical: padV),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bubbleColor,
|
||||||
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
dot(Duration.zero),
|
||||||
|
const SizedBox(width: gap),
|
||||||
|
dot(d),
|
||||||
|
const SizedBox(width: gap),
|
||||||
|
dot(d2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildActionButtons() {
|
Widget _buildActionButtons() {
|
||||||
final isErrorMessage =
|
final isErrorMessage =
|
||||||
widget.message.content.contains('⚠️') ||
|
widget.message.content.contains('⚠️') ||
|
||||||
|
|||||||
@@ -613,11 +613,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
'Ce dossier et ses associations seront supprimés.';
|
'Ce dossier et ses associations seront supprimés.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get failedToDeleteFolder =>
|
String get failedToDeleteFolder => 'Échec de la suppression du dossier';
|
||||||
'Échec de la suppression du dossier';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'À propos de l\'application';
|
String get aboutApp => 'À propos de l’application';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutAppSubtitle => 'Informations et liens Conduit';
|
String get aboutAppSubtitle => 'Informations et liens Conduit';
|
||||||
|
|||||||
@@ -606,11 +606,10 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
'Questa cartella e le sue associazioni verranno rimosse.';
|
'Questa cartella e le sue associazioni verranno rimosse.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get failedToDeleteFolder =>
|
String get failedToDeleteFolder => 'Impossibile eliminare la cartella';
|
||||||
'Impossibile eliminare la cartella';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutApp => 'Informazioni sull\'app';
|
String get aboutApp => 'Informazioni sull’app';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutAppSubtitle => 'Informazioni e link di Conduit';
|
String get aboutAppSubtitle => 'Informazioni e link di Conduit';
|
||||||
|
|||||||
Reference in New Issue
Block a user