【问题标题】:How to add a margin or padding to a Snackbar?如何为 Snackbar 添加边距或填充?
【发布时间】:2019-08-13 13:17:16
【问题描述】:

我一直在 Snackbar 上工作,并在我的项目中取得了成功。但是,我想将这个小东西添加到 Snackbar 中,那就是 margins。我在这个链接中看到了:Snackbars - Material Design

我真的希望我的 Snackbar 变成这样:

我现在得到的是这个:

我的代码是:

final snackBar = SnackBar(
    content: Text("Feild(s) are empty!"),
    duration: new Duration(seconds: 1),
    backgroundColor: Theme.of(context).primaryColor,
  );
  Scaffold.of(context).showSnackBar(snackBar);
}

【问题讨论】:

  • 这是应该在颤振中完成的事情。 Flutter 似乎使用的是旧版的小吃店规格

标签: flutter material-design snackbar


【解决方案1】:

Flutter 团队更新了快餐栏以匹配 PR 中的材料设计。您可以通过设置简单地获得新行为

行为:SnackBarBehavior.floating

这是一个示例代码

final snackBar = SnackBar(
  elevation: 6.0,
  backgroundColor: Configs.current.COLORS_PRIMARY,
  behavior: SnackBarBehavior.floating,
  content: Text(
    "Snack bar test",
    style: TextStyle(color: Colors.white),
  ),
);

结果会是这样的

【讨论】:

    【解决方案2】:

    不确定边距。圆角 SnackBar 可以这样创建:

    Scaffold
        .of(context)
        .showSnackBar(
            SnackBar(
                content: Text(message),
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(Radius.circular(20)))));
    

    在上面使用所需的边框半径。

    更新: 您可以使用浮动SnackBar 添加默认边距。将下面传递给SnackBar 构造函数:

    Scaffold
    .of(context)
    .showSnackBar(
        SnackBar(
            content: Text(message),
            behavior: SnackBarBehavior.floating,
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(20)))));
    

    【讨论】:

    • 这很棒,非常适合我。例如。 imgur.com/a/O9Nrcdq
    • 我想在snackbaraction 标签之外添加边框。有什么建议吗?谢谢。
    • 我们如何在snackbar上添加2像素边框?请建议。谢谢
    【解决方案3】:

    截图:


    最小代码:

    var snackbar = SnackBar(
      content: Text('Hello World!'),
      margin: EdgeInsets.all(20),
      behavior: SnackBarBehavior.floating,
    );
    Scaffold.of(context).showSnackBar(snackbar);
    

    完整代码:

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: Center(
          child: Builder(builder: (context) {
            return RaisedButton(
              onPressed: () {
                var snackbar = SnackBar(
                  content: Text('Hello World!'),
                  margin: EdgeInsets.all(20),
                  behavior: SnackBarBehavior.floating,
                );
                Scaffold.of(context).showSnackBar(snackbar);
              },
              child: Text('Show SnackBar'),
            );
          }),
        ),
      );
    }
    

    【讨论】:

    • 非常感谢 ;)
    • 我们如何在snackbar上添加2像素边框?请建议。谢谢。
    • @Kamlesh 从今天起不可能,除非您在源代码中进行更改或自己使用 Overlay 或其他小部件创建一些东西。
    【解决方案4】:

    免责声明:此答案已过时,仅应出于历史原因查看。从上面选择一个合适的解决方案?


    遗憾的是,我还没有找到 Flutter 提供的任何选项或可能性来实现一轮 SnackBar。

    如果您真的需要/想要圆角和间距,您可以复制SnackBar 的源代码并对副本进行调整。请记住将implements SnackBar 添加到您的类定义中。

    我已经开始实施并添加了圆角。您只需为 iPhone X 等手机添加底部填充,它们的底部有元素,会遮挡视图。 (要获得底部间距,您可以使用MediaQuery.of(context).viewInsets.bottom。)


    您可以复制的独立示例:

    ma​​in.dart

    import 'package:flutter/material.dart';
    import 'package:your_app_name/my_snack.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: MyBody()
          ),
        );
      }
    }
    
    class MyBody extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
            child: RaisedButton(onPressed: () {
              Scaffold.of(context).showSnackBar(MySnack(content: Text('MySnack!')));
              }),
        );
      }
    }
    

    my_snack.dart

    // Copyright 2015 The Chromium Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    import 'package:flutter/rendering.dart';
    import 'package:flutter/widgets.dart';
    import 'package:flutter/material.dart';
    
    const double _kSnackBarPadding = 24.0;
    const double _kSingleLineVerticalPadding = 14.0;
    const Color _kSnackBackground = Color(0xFF323232);
    
    // TODO(ianh): We should check if the given text and actions are going to fit on
    // one line or not, and if they are, use the single-line layout, and if not, use
    // the multiline layout. See link above.
    
    // TODO(ianh): Implement the Tablet version of snackbar if we're "on a tablet".
    
    const Duration _kSnackBarTransitionDuration = Duration(milliseconds: 250);
    const Duration _kSnackBarDisplayDuration = Duration(milliseconds: 4000);
    const Curve _snackBarHeightCurve = Curves.fastOutSlowIn;
    const Curve _snackBarFadeCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlowIn);
    
    /// Specify how a [SnackBar] was closed.
    ///
    /// The [ScaffoldState.showSnackBar] function returns a
    /// [ScaffoldFeatureController]. The value of the controller's closed property
    /// is a Future that resolves to a SnackBarClosedReason. Applications that need
    /// to know how a snackbar was closed can use this value.
    ///
    /// Example:
    ///
    /// ```dart
    /// Scaffold.of(context).showSnackBar(
    ///   SnackBar( ... )
    /// ).closed.then((SnackBarClosedReason reason) {
    ///    ...
    /// });
    /// ```
    
    /// A button for a [SnackBar], known as an "action".
    ///
    /// Snack bar actions are always enabled. If you want to disable a snack bar
    /// action, simply don't include it in the snack bar.
    ///
    /// Snack bar actions can only be pressed once. Subsequent presses are ignored.
    ///
    /// See also:
    ///
    ///  * [SnackBar]
    ///  * <https://material.io/design/components/snackbars.html>
    
    class _SnackBarActionState extends State<SnackBarAction> {
      bool _haveTriggeredAction = false;
    
      void _handlePressed() {
        if (_haveTriggeredAction)
          return;
        setState(() {
          _haveTriggeredAction = true;
        });
        widget.onPressed();
        Scaffold.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.action);
      }
    
      @override
      Widget build(BuildContext context) {
        return FlatButton(
          onPressed: _haveTriggeredAction ? null : _handlePressed,
          child: Text(widget.label),
          textColor: widget.textColor,
          disabledTextColor: widget.disabledTextColor,
        );
      }
    }
    
    /// A lightweight message with an optional action which briefly displays at the
    /// bottom of the screen.
    ///
    /// To display a snack bar, call `Scaffold.of(context).showSnackBar()`, passing
    /// an instance of [SnackBar] that describes the message.
    ///
    /// To control how long the [SnackBar] remains visible, specify a [duration].
    ///
    /// A SnackBar with an action will not time out when TalkBack or VoiceOver are
    /// enabled. This is controlled by [AccessibilityFeatures.accessibleNavigation].
    ///
    /// See also:
    ///
    ///  * [Scaffold.of], to obtain the current [ScaffoldState], which manages the
    ///    display and animation of snack bars.
    ///  * [ScaffoldState.showSnackBar], which displays a [SnackBar].
    ///  * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the currently
    ///    displayed snack bar, if any, and allows the next to be displayed.
    ///  * [SnackBarAction], which is used to specify an [action] button to show
    ///    on the snack bar.
    ///  * <https://material.io/design/components/snackbars.html>
    class MySnack extends StatelessWidget implements SnackBar {
      /// Creates a snack bar.
      ///
      /// The [content] argument must be non-null.
      const MySnack({
        Key key,
        @required this.content,
        this.backgroundColor,
        this.action,
        this.duration = _kSnackBarDisplayDuration,
        this.animation,
      }) : assert(content != null),
            assert(duration != null),
            super(key: key);
    
      /// The primary content of the snack bar.
      ///
      /// Typically a [Text] widget.
      final Widget content;
    
      /// The Snackbar's background color. By default the color is dark grey.
      final Color backgroundColor;
    
      /// (optional) An action that the user can take based on the snack bar.
      ///
      /// For example, the snack bar might let the user undo the operation that
      /// prompted the snackbar. Snack bars can have at most one action.
      ///
      /// The action should not be "dismiss" or "cancel".
      final SnackBarAction action;
    
      /// The amount of time the snack bar should be displayed.
      ///
      /// Defaults to 4.0s.
      ///
      /// See also:
      ///
      ///  * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the
      ///    currently displayed snack bar, if any, and allows the next to be
      ///    displayed.
      ///  * <https://material.io/design/components/snackbars.html>
      final Duration duration;
    
      /// The animation driving the entrance and exit of the snack bar.
      final Animation<double> animation;
    
      @override
      Widget build(BuildContext context) {
        final MediaQueryData mediaQueryData = MediaQuery.of(context);
        assert(animation != null);
        final ThemeData theme = Theme.of(context);
        final ThemeData darkTheme = ThemeData(
          brightness: Brightness.dark,
          accentColor: theme.accentColor,
          accentColorBrightness: theme.accentColorBrightness,
        );
        final List<Widget> children = <Widget>[
          const SizedBox(width: _kSnackBarPadding),
          Expanded(
            child: Container(
              padding: const EdgeInsets.symmetric(vertical: _kSingleLineVerticalPadding),
              child: DefaultTextStyle(
                style: darkTheme.textTheme.subhead,
                child: content,
              ),
            ),
          ),
        ];
        if (action != null) {
          children.add(ButtonTheme.bar(
            padding: const EdgeInsets.symmetric(horizontal: _kSnackBarPadding),
            textTheme: ButtonTextTheme.accent,
            child: action,
          ));
        } else {
          children.add(const SizedBox(width: _kSnackBarPadding));
        }
        final CurvedAnimation heightAnimation = CurvedAnimation(parent: animation, curve: _snackBarHeightCurve);
        final CurvedAnimation fadeAnimation = CurvedAnimation(parent: animation, curve: _snackBarFadeCurve, reverseCurve: const Threshold(0.0));
        Widget snackbar = SafeArea(
          bottom: false,
          top: false,
          child: Row(
            children: children,
            crossAxisAlignment: CrossAxisAlignment.center,
          ),
        );
        snackbar = Semantics(
          container: true,
          liveRegion: true,
          onDismiss: () {
            Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
          },
          child: Dismissible(
            key: const Key('dismissible'),
            direction: DismissDirection.down,
            resizeDuration: null,
            onDismissed: (DismissDirection direction) {
              Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
            },
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Container(
                decoration: BoxDecoration(
                  color: backgroundColor ?? _kSnackBackground,
                  borderRadius: BorderRadius.circular(8.0)
                ),
                child: Material(
                  elevation: 6.0,
                  color: Colors.transparent,
                  child: Theme(
                    data: darkTheme,
                    child: mediaQueryData.accessibleNavigation ? snackbar : FadeTransition(
                      opacity: fadeAnimation,
                      child: snackbar,
                    ),
                  ),
                ),
              ),
            ),
          ),
        );
        return ClipRect(
          child: mediaQueryData.accessibleNavigation ? snackbar : AnimatedBuilder(
            animation: heightAnimation,
            builder: (BuildContext context, Widget child) {
              return Align(
                alignment: AlignmentDirectional.topStart,
                heightFactor: heightAnimation.value,
                child: child,
              );
            },
            child: snackbar,
          ),
        );
      }
    
      // API for Scaffold.addSnackBar():
    
      /// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
      static AnimationController createAnimationController({ @required TickerProvider vsync }) {
        return AnimationController(
          duration: _kSnackBarTransitionDuration,
          debugLabel: 'SnackBar',
          vsync: vsync,
        );
      }
    
      /// Creates a copy of this snack bar but with the animation replaced with the given animation.
      ///
      /// If the original snack bar lacks a key, the newly created snack bar will
      /// use the given fallback key.
      SnackBar withAnimation(Animation<double> newAnimation, { Key fallbackKey }) {
        return MySnack(
          key: key ?? fallbackKey,
          content: content,
          backgroundColor: backgroundColor,
          action: action,
          duration: duration,
          animation: newAnimation,
        );
      }
    }
    

    对 SnackBar 所做的更改:

    • 将SnackBar重命名为MySnack
    • 将implements SnackBar 添加到MySnack 类
    • 在MySnack构建方法中的SafeArea中设置bottom: false
    • 在Material 周围添加了Container,这是SnackBar 的背景
    • 将color 声明从Material 移动到创建的Container
    • 将所需的borderRadius(连同color)作为BoxDecoration 添加到Container

    【讨论】:

    • 感谢 Niklas 的输入,但您的答案似乎更难接近。但是,我发现第一个答案是最好的。您还应该为将来的参考使用相同的方法。谢谢:)
    • 第一个答案肯定是要走的路:)不知道框架。
    • 没问题 Niklas,但无论如何感谢您的输入。对此,我真的非常感激。 :)
    • 有没有办法从屏幕顶部而不是底部制作零食
    • 我们如何在snackbar上添加2像素边框?请建议。谢谢。
    猜你喜欢
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 2015-10-09
    • 1970-01-01
    相关资源
    最近更新 更多