mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
## Summary - align iOS back controls and affected titles across channel details, settings, and pairing - make channel star and mute actions reflect their state immediately - animate locally sent channel, thread, and DM messages from behind the composer with a 300ms ease-out ## Testing - `just mobile-check` - full mobile test suite (1,573 tests) --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
73 lines
2.2 KiB
Dart
73 lines
2.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
|
|
/// Duration of a local message's entrance transition.
|
|
const localMessageSendTransitionDuration = Duration(milliseconds: 300);
|
|
|
|
/// Default vertical offset, in child heights, for a message entrance.
|
|
const localMessageSendTransitionStartOffset = 1.5;
|
|
|
|
/// Vertical offset, in child heights, used for an avatar entrance.
|
|
const localMessageSendTransitionAvatarStartOffset = 2.5;
|
|
|
|
/// Slides a newly sent local message upward from beneath the composer.
|
|
class LocalMessageSendTransition extends HookWidget {
|
|
/// Creates a local send transition around [child].
|
|
const LocalMessageSendTransition({
|
|
super.key,
|
|
required this.animate,
|
|
this.startOffsetFactor = localMessageSendTransitionStartOffset,
|
|
required this.child,
|
|
});
|
|
|
|
/// Whether to play the entrance transition.
|
|
final bool animate;
|
|
|
|
/// Initial vertical offset expressed as a multiple of the child's height.
|
|
final double startOffsetFactor;
|
|
|
|
/// Message content translated by the transition.
|
|
final Widget child;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final reduceMotion = MediaQuery.disableAnimationsOf(context);
|
|
final controller = useAnimationController(
|
|
duration: localMessageSendTransitionDuration,
|
|
initialValue: animate && !reduceMotion ? 0 : 1,
|
|
);
|
|
final easedAnimation = useMemoized(
|
|
() => CurvedAnimation(parent: controller, curve: Curves.easeOutCubic),
|
|
[controller],
|
|
);
|
|
useEffect(() => easedAnimation.dispose, [easedAnimation]);
|
|
|
|
useEffect(() {
|
|
if (animate && !reduceMotion) {
|
|
unawaited(
|
|
controller.animateTo(
|
|
1,
|
|
duration: localMessageSendTransitionDuration,
|
|
curve: Curves.linear,
|
|
),
|
|
);
|
|
} else {
|
|
controller.value = 1;
|
|
}
|
|
return null;
|
|
}, [animate, controller, reduceMotion]);
|
|
|
|
return AnimatedBuilder(
|
|
animation: easedAnimation,
|
|
child: child,
|
|
builder: (context, child) => FractionalTranslation(
|
|
translation: Offset(0, startOffsetFactor * (1 - easedAnimation.value)),
|
|
transformHitTests: false,
|
|
child: child,
|
|
),
|
|
);
|
|
}
|
|
}
|