|
|
|
|
|
|
using Unity.UIWidgets.painting; |
|
|
|
using Unity.UIWidgets.rendering; |
|
|
|
using Unity.UIWidgets.scheduler2; |
|
|
|
using Unity.UIWidgets.service; |
|
|
|
using TextStyle = Unity.UIWidgets.painting.TextStyle; |
|
|
|
public const float _kSnackBarPadding = 24.0f; |
|
|
|
public const float _kSingleLineVerticalPadding = 14.0f; |
|
|
|
public static readonly Color _kSnackBackground = new Color(0xFF323232); |
|
|
|
public const float _singleLineVerticalPadding = 14.0f; |
|
|
|
public static readonly TimeSpan _kSnackBarTransitionDuration = new TimeSpan(0, 0, 0, 0, 250); |
|
|
|
public static readonly TimeSpan _kSnackBarDisplayDuration = new TimeSpan(0, 0, 0, 0, 4000); |
|
|
|
public static readonly TimeSpan _snackBarTransitionDuration = new TimeSpan(0, 0, 0, 0, 250); |
|
|
|
public static readonly TimeSpan _snackBarDisplayDuration = new TimeSpan(0, 0, 0, 0, 4000); |
|
|
|
public static readonly Curve _snackBarFadeCurve = new Interval(0.72f, 1.0f, curve: Curves.fastOutSlowIn); |
|
|
|
public static readonly Curve _snackBarFadeInCurve = new Interval(0.45f, 1.0f, curve: Curves.fastOutSlowIn); |
|
|
|
public static readonly Curve _snackBarFadeOutCurve = new Interval(0.72f, 1.0f, curve: Curves.fastOutSlowIn); |
|
|
|
} |
|
|
|
|
|
|
|
public enum SnackBarClosedReason { |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
SnackBarThemeData snackBarTheme = Theme.of(context).snackBarTheme; |
|
|
|
Color textColor = widget.textColor ?? snackBarTheme.actionTextColor; |
|
|
|
Color disabledTextColor = widget.disabledTextColor ?? snackBarTheme.disabledActionTextColor; |
|
|
|
|
|
|
|
textColor: widget.textColor, |
|
|
|
disabledTextColor: widget.disabledTextColor |
|
|
|
textColor: textColor, |
|
|
|
disabledTextColor: disabledTextColor |
|
|
|
public class SnackBar : StatelessWidget { |
|
|
|
public class SnackBar : StatefulWidget { |
|
|
|
float? elevation = null, |
|
|
|
ShapeBorder shape = null, |
|
|
|
SnackBarBehavior? behavior = null, |
|
|
|
Animation<float> animation = null |
|
|
|
Animation<float> animation = null, |
|
|
|
VoidCallback onVisible = null |
|
|
|
duration = duration ?? SnackBarUtils._kSnackBarDisplayDuration; |
|
|
|
duration = duration ?? SnackBarUtils._snackBarDisplayDuration; |
|
|
|
D.assert(elevation == null || elevation >= 0.0); |
|
|
|
this.elevation = elevation; |
|
|
|
this.shape = shape; |
|
|
|
this.behavior = behavior; |
|
|
|
this.onVisible = onVisible; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Widget content; |
|
|
|
|
|
|
public readonly float? elevation; |
|
|
|
|
|
|
|
public readonly ShapeBorder shape; |
|
|
|
|
|
|
|
public readonly SnackBarBehavior? behavior; |
|
|
|
|
|
|
|
public readonly SnackBarAction action; |
|
|
|
|
|
|
|
public readonly TimeSpan duration; |
|
|
|
|
|
|
public readonly VoidCallback onVisible; |
|
|
|
|
|
|
|
static AnimationController createAnimationController(TickerProvider vsync) { |
|
|
|
return new AnimationController( |
|
|
|
duration: SnackBarUtils._snackBarTransitionDuration, |
|
|
|
debugLabel: "SnackBar", |
|
|
|
vsync: vsync |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
SnackBar withAnimation(Animation<float> newAnimation, Key fallbackKey = null) { |
|
|
|
return new SnackBar( |
|
|
|
key: key ?? fallbackKey, |
|
|
|
content: content, |
|
|
|
backgroundColor: backgroundColor, |
|
|
|
elevation: elevation, |
|
|
|
shape: shape, |
|
|
|
behavior: behavior, |
|
|
|
action: action, |
|
|
|
duration: duration, |
|
|
|
animation: newAnimation, |
|
|
|
onVisible: onVisible |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
public override State createState() { |
|
|
|
return new _SnackBarState(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _SnackBarState : State<SnackBar> { |
|
|
|
bool _wasVisible = false; |
|
|
|
|
|
|
|
public override void initState() { |
|
|
|
base.initState(); |
|
|
|
widget.animation.addStatusListener(_onAnimationStatusChanged); |
|
|
|
} |
|
|
|
|
|
|
|
public override void didUpdateWidget(StatefulWidget oldWidget) { |
|
|
|
var _oldWidget = (SnackBar) oldWidget; |
|
|
|
if (widget.animation != _oldWidget.animation) { |
|
|
|
_oldWidget.animation.removeStatusListener(_onAnimationStatusChanged); |
|
|
|
widget.animation.addStatusListener(_onAnimationStatusChanged); |
|
|
|
} |
|
|
|
|
|
|
|
base.didUpdateWidget(oldWidget); |
|
|
|
} |
|
|
|
|
|
|
|
public override void dispose() { |
|
|
|
widget.animation.removeStatusListener(_onAnimationStatusChanged); |
|
|
|
base.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
void _onAnimationStatusChanged(AnimationStatus animationStatus) { |
|
|
|
switch (animationStatus) { |
|
|
|
case AnimationStatus.dismissed: |
|
|
|
case AnimationStatus.forward: |
|
|
|
case AnimationStatus.reverse: |
|
|
|
break; |
|
|
|
case AnimationStatus.completed: |
|
|
|
if (widget.onVisible != null && !_wasVisible) { |
|
|
|
widget.onVisible(); |
|
|
|
} |
|
|
|
|
|
|
|
_wasVisible = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
D.assert(animation != null); |
|
|
|
D.assert(widget.animation != null); |
|
|
|
ThemeData theme = Theme.of(context); |
|
|
|
ColorScheme colorScheme = theme.colorScheme; |
|
|
|
SnackBarThemeData snackBarTheme = theme.snackBarTheme; |
|
|
|
bool isThemeDark = theme.brightness == Brightness.dark; |
|
|
|
ThemeData theme = Theme.of(context); |
|
|
|
ThemeData darkTheme = new ThemeData( |
|
|
|
brightness: Brightness.dark, |
|
|
|
accentColor: theme.accentColor, |
|
|
|
accentColorBrightness: theme.accentColorBrightness |
|
|
|
Brightness brightness = isThemeDark ? Brightness.light : Brightness.dark; |
|
|
|
Color themeBackgroundColor = isThemeDark |
|
|
|
? colorScheme.onSurface |
|
|
|
: Color.alphaBlend(colorScheme.onSurface.withOpacity(0.80f), colorScheme.surface); |
|
|
|
ThemeData inverseTheme = new ThemeData( |
|
|
|
brightness: brightness, |
|
|
|
backgroundColor: themeBackgroundColor, |
|
|
|
colorScheme: new ColorScheme( |
|
|
|
primary: colorScheme.onPrimary, |
|
|
|
primaryVariant: colorScheme.onPrimary, |
|
|
|
secondary: isThemeDark ? colorScheme.primaryVariant : colorScheme.secondary, |
|
|
|
secondaryVariant: colorScheme.onSecondary, |
|
|
|
surface: colorScheme.onSurface, |
|
|
|
background: themeBackgroundColor, |
|
|
|
error: colorScheme.onError, |
|
|
|
onPrimary: colorScheme.primary, |
|
|
|
onSecondary: colorScheme.secondary, |
|
|
|
onSurface: colorScheme.surface, |
|
|
|
onBackground: colorScheme.background, |
|
|
|
onError: colorScheme.error, |
|
|
|
brightness: brightness |
|
|
|
), |
|
|
|
snackBarTheme: snackBarTheme |
|
|
|
); |
|
|
|
|
|
|
|
TextStyle contentTextStyle = snackBarTheme.contentTextStyle ?? inverseTheme.textTheme.subtitle1; |
|
|
|
SnackBarBehavior snackBarBehavior = widget.behavior ?? snackBarTheme.behavior ?? SnackBarBehavior.fix; |
|
|
|
bool isFloatingSnackBar = snackBarBehavior == SnackBarBehavior.floating; |
|
|
|
float snackBarPadding = isFloatingSnackBar ? 16.0f : 24.0f; |
|
|
|
|
|
|
|
CurvedAnimation heightAnimation = |
|
|
|
new CurvedAnimation(parent: widget.animation, curve: SnackBarUtils._snackBarHeightCurve); |
|
|
|
CurvedAnimation fadeInAnimation = |
|
|
|
new CurvedAnimation(parent: widget.animation, curve: SnackBarUtils._snackBarFadeInCurve); |
|
|
|
CurvedAnimation fadeOutAnimation = new CurvedAnimation( |
|
|
|
parent: widget.animation, |
|
|
|
curve: SnackBarUtils._snackBarFadeOutCurve, |
|
|
|
reverseCurve: new Threshold(0.0f) |
|
|
|
List<Widget> children = new List<Widget> { |
|
|
|
new SizedBox(width: SnackBarUtils._kSnackBarPadding), |
|
|
|
var childrenList = new List<Widget>() { |
|
|
|
new SizedBox(width: snackBarPadding), |
|
|
|
padding: EdgeInsets.symmetric(vertical: SnackBarUtils._kSingleLineVerticalPadding), |
|
|
|
padding: EdgeInsets.symmetric(vertical: SnackBarUtils._singleLineVerticalPadding), |
|
|
|
style: darkTheme.textTheme.subhead, |
|
|
|
child: content) |
|
|
|
style: contentTextStyle, |
|
|
|
child: widget.content |
|
|
|
) |
|
|
|
|
|
|
|
if (action != null) { |
|
|
|
children.Add(ButtonTheme.bar( |
|
|
|
padding: EdgeInsets.symmetric(horizontal: SnackBarUtils._kSnackBarPadding), |
|
|
|
if (widget.action != null) { |
|
|
|
childrenList.Add(new ButtonTheme( |
|
|
|
child: action |
|
|
|
minWidth: 64.0f, |
|
|
|
padding: EdgeInsets.symmetric(horizontal: snackBarPadding), |
|
|
|
child: widget.action |
|
|
|
children.Add(new SizedBox(width: SnackBarUtils._kSnackBarPadding)); |
|
|
|
childrenList.Add(new SizedBox(width: snackBarPadding)); |
|
|
|
CurvedAnimation heightAnimation = |
|
|
|
new CurvedAnimation(parent: animation, curve: SnackBarUtils._snackBarHeightCurve); |
|
|
|
CurvedAnimation fadeAnimation = new CurvedAnimation(parent: animation, |
|
|
|
curve: SnackBarUtils._snackBarFadeCurve, reverseCurve: new Threshold(0.0f)); |
|
|
|
Widget snackbar = new SafeArea( |
|
|
|
Widget snackBar = new SafeArea( |
|
|
|
bottom: !isFloatingSnackBar, |
|
|
|
children: children, |
|
|
|
crossAxisAlignment: CrossAxisAlignment.center |
|
|
|
crossAxisAlignment: CrossAxisAlignment.center, |
|
|
|
children: childrenList |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
float elevation = widget.elevation ?? snackBarTheme.elevation ?? 6.0f; |
|
|
|
Color backgroundColor = |
|
|
|
widget.backgroundColor ?? snackBarTheme.backgroundColor ?? inverseTheme.backgroundColor; |
|
|
|
ShapeBorder shape = widget.shape |
|
|
|
?? snackBarTheme.shape |
|
|
|
?? (isFloatingSnackBar |
|
|
|
? new RoundedRectangleBorder(borderRadius: BorderRadius.circular(4.0f)) |
|
|
|
: null); |
|
|
|
|
|
|
|
snackBar = new Material( |
|
|
|
shape: shape, |
|
|
|
elevation: elevation, |
|
|
|
color: backgroundColor, |
|
|
|
child: new Theme( |
|
|
|
data: inverseTheme, |
|
|
|
child: mediaQueryData.accessibleNavigation |
|
|
|
? snackBar |
|
|
|
: new FadeTransition( |
|
|
|
opacity: fadeOutAnimation, |
|
|
|
child: snackBar |
|
|
|
) |
|
|
|
snackbar = new Dismissible( |
|
|
|
if (isFloatingSnackBar) { |
|
|
|
snackBar = new Padding( |
|
|
|
padding: EdgeInsets.fromLTRB(15.0f, 5.0f, 15.0f, 10.0f), |
|
|
|
child: snackBar |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
snackBar = new Dismissible( |
|
|
|
key: Key.key("dismissible"), |
|
|
|
direction: DismissDirection.down, |
|
|
|
resizeDuration: null, |
|
|
|
|
|
|
child: new Material( |
|
|
|
elevation: 6.0f, |
|
|
|
color: backgroundColor ?? SnackBarUtils._kSnackBackground, |
|
|
|
child: new Theme( |
|
|
|
data: darkTheme, |
|
|
|
child: mediaQueryData.accessibleNavigation |
|
|
|
? snackbar |
|
|
|
: new FadeTransition( |
|
|
|
opacity: fadeAnimation, |
|
|
|
child: snackbar |
|
|
|
) |
|
|
|
) |
|
|
|
) |
|
|
|
child: snackBar |
|
|
|
return new ClipRect( |
|
|
|
child: mediaQueryData.accessibleNavigation |
|
|
|
? snackbar |
|
|
|
: new AnimatedBuilder( |
|
|
|
animation: heightAnimation, |
|
|
|
builder: (BuildContext subContext, Widget child) => { |
|
|
|
return new Align( |
|
|
|
alignment: Alignment.topLeft, |
|
|
|
heightFactor: heightAnimation.value, |
|
|
|
child: child |
|
|
|
); |
|
|
|
}, |
|
|
|
child: snackbar |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
public static AnimationController createAnimationController(TickerProvider vsync) { |
|
|
|
return new AnimationController( |
|
|
|
duration: SnackBarUtils._kSnackBarTransitionDuration, |
|
|
|
debugLabel: "SnackBar", |
|
|
|
vsync: vsync |
|
|
|
); |
|
|
|
} |
|
|
|
Widget snackBarTransition = null; |
|
|
|
if (mediaQueryData.accessibleNavigation) { |
|
|
|
snackBarTransition = snackBar; |
|
|
|
} |
|
|
|
else if (isFloatingSnackBar) { |
|
|
|
snackBarTransition = new FadeTransition( |
|
|
|
opacity: fadeInAnimation, |
|
|
|
child: snackBar |
|
|
|
); |
|
|
|
} |
|
|
|
else { |
|
|
|
snackBarTransition = new AnimatedBuilder( |
|
|
|
animation: heightAnimation, |
|
|
|
builder: (BuildContext subContext, Widget subChild) => { |
|
|
|
return new Align( |
|
|
|
alignment: AlignmentDirectional.topStart, |
|
|
|
heightFactor: heightAnimation.value, |
|
|
|
child: subChild |
|
|
|
); |
|
|
|
}, |
|
|
|
child: snackBar |
|
|
|
); |
|
|
|
} |
|
|
|
public SnackBar withAnimation(Animation<float> newAnimation, Key fallbackKey = null) { |
|
|
|
return new SnackBar( |
|
|
|
key: key ?? fallbackKey, |
|
|
|
content: content, |
|
|
|
backgroundColor: backgroundColor, |
|
|
|
action: action, |
|
|
|
duration: duration, |
|
|
|
animation: newAnimation |
|
|
|
); |
|
|
|
return new ClipRect(child: snackBarTransition); |
|
|
|
} |
|
|
|
} |
|
|
|
} |