Flutter Showing Snackbar On Top of Bottom Sheet - flutter

In my code I call a bottom sheet to display a list of tiles. These tiles contain buttons that display a snackbar. That functionality works fine, the only issue is that the snackbar is displayed behind the bottom sheet so you can only see it if you close the bottom sheet. Each of them are called with the following code:
1. Bottom Sheet:
void _settingModalBottomSheet(context, stream, scaffoldKey ) {
if (_availableRides.length == 0) {
return null;
} else {
return scaffoldKey.currentState.showBottomSheet((context) {
return Column(
children: Widgets;
});
}
}
2. Snackbar
widget.scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text("Created", textAlign:
TextAlign.center,),),
Does anyone know how I can position the snackbar in front of the bottom sheet

So I was able to solve this by just adding another Scaffold() to my Bottom sheet and passing it a new scaffold key

SnackBar has a property for this. It's called behavior, you could do this:
SnackBar(
behavior: SnackBarBehavior.floating,
...
SnackBarBehavior enum
floating → const SnackBarBehavior
This behavior will cause SnackBar to be shown above other widgets in
the Scaffold. This includes being displayed above a
BottomNavigationBar and a FloatingActionButton.
See material.io/design/components/snackbars.html for more details.

I solved by Set (padding from bottom to SnackBar) As much as the height of the (bottomSheet : height) .
In This Case I Have This bottomSheet:
bottomSheet: Container(
child: RaisedButton(...),
width: MediaQuery.of(context).size.width,
height: AppBar().preferredSize.height * 0.85,
),
And This snackBar:
_scaffoldKey.currentState.showSnackBar(SnackBar(
padding:EdgeInsetsDirectional.only(
bottom:AppBar().preferredSize.height * 0.85),
backgroundColor: Colors.red,
duration: new Duration(milliseconds: 3000),
content: Text('ETC..'),
));

You can achieve this Simply by wrapping your BottomSheet widget with a Scaffold.
eg:
void _settingModalBottomSheet(context, stream, scaffoldKey ) {
if (_availableRides.length == 0) {
return null;
} else {
return scaffoldKey.currentState.showBottomSheet((context) {
return Scaffold(
body: Column(
children: Widgets;
})
);
}
}

I arrived on pretty decent solution. I wrapped my bottom sheet in a Scaffold, but I added it as the bottomSheet parameter. While adding the Scaffold, some trailing background will be added, so I just made its background transparent.
Scaffold(
backgroundColor: Colors.transparent,
bottomSheet: ...,
)

This is a working solution according to documentation.
https://docs.flutter.dev/cookbook/design/snackbars
This example works with bottom sheets as well.
Initialize ScaffoldMessengerKey.
Wrap your component widget with Scaffold.
Wrap Scaffold with ScaffoldMessenger.
Add key scaffoldMessengerKey to ScaffoldMessenger
Call method scaffoldMessengerKey.currentState?.showSnackBar(SnackBar());
Example:
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
// Any widget with button.
// (Bottom sheet also) - root widget must be ScaffoldMessenger.
ScaffoldMessenger(
key: scaffoldMessengerKey,
child: Scaffold(
body: Container(
child: ElevatedButton(
style: raisedButtonStyle,
onPressed: () {
scaffoldMessengerKey.currentState?.showSnackBar(
//SnackBar design.
SnackBar(
backgroundColor: Colors.white,
elevation: 8,
content: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: Text(
'Simple snackbar text.',
style: FlutterFlowTheme.of(context).bodyText1
.override(fontFamily: 'Rubik',
fontWeight: FontWeight.w300,
lineHeight: 1.5,
),
),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
// Some code to undo the change.
},
),
duration: Duration(seconds: 5),
behavior: SnackBarBehavior.floating,
},
child: Text('Open snackbar over bottom sheet!'),
); //ElevatedButton
); //Container
); //Scaffold
); //ScaffoldMessenger
Note:
With this approach you don't need to pass BuildContext.
If you don't want to register ScaffoldMessengerKey.
You can show SnackBar like this: ScaffoldMessenger.of(context).showSnackBar(SnackBar());

I solved this by changing bottomSheet to bottomNavigationBar since the floating snack bar solution didn't work for me.

you can use flushbar package. I think this is the better option if need to use with bottomSheet.
context should be your page's context, not bottomsheet context
any event inside bottomSheet
CustomFlushBar().flushBar(text: 'Thank you for your payment!', context: context,duration: 2);
CustomFlushBar class
class CustomFlushBar {
void flushBar({int duration, #required String text, Color iconColor, IconData iconData, Color backgroundColor, #required BuildContext context}) async {
await dismiss();
Flushbar(
margin: EdgeInsets.all(8),
borderRadius: 8,
backgroundColor: backgroundColor ?? Palette.greenButton,
icon: Icon(iconData ?? Icons.done, color: iconColor ?? Palette.white),
flushbarStyle: FlushbarStyle.FLOATING,
message: text,
duration: Duration(seconds: duration ?? 3),
)..show(context);
}
Future<void> dismiss() async {
if (!Flushbar().isDismissed()) {
await Flushbar().dismiss();
}
}
}

Related

Custom splash screen with animation-Flutter

I am trying to show a splash screen with an animation, and I found this article: https://medium.com/#galadhruvil7/flutter-splash-screen-animation-16c50e18b9d8 I think that's very simple but wonderful, the case is I would like to change the flutter icon into another image and text. Here is the code
SplashScreenState() {
_timer = new Timer(const Duration(seconds: 1), () {
setState(() {
assetImage = Row(
children: [
Image.asset('assets/logo.png', height: 500, width: 500),
Text("trial")
],
);
});
});
}
and showing the widget like this:
return Scaffold(
backgroundColor: Colors.grey[850],
body: Center(
child: Container(
child: assetImage,
),
),
);
but by running that code, there is no animation effect. Is there a way to keep the animation like the source that I have given while the Flutter logo is changed into image and text ?
Through AnimatedSwitcher with conditional operators you can change your logo with icon and text.
Example - AnimatedSwitcherExample

Flutter: InkWell completely non-functional in Card

Pasted below is the build method for a widget based on Card that serves as a list element in a ListWheelScrollView. The TweenAnimationBuilder is simply to animate a background color change in the Card widget whenever it's the currently selected list item.
Widget build(BuildContext context) {
Color primary = Theme
.of(context)
.primaryColor;
Color secondary = Colors.white;
return new TweenAnimationBuilder(
tween: new ColorTween(
begin: secondary, end: selected ? primary : secondary),
duration: new Duration(milliseconds: 300),
builder: (BuildContext context, Color color, Widget child) {
return new Card(
color: color,
child: new InkWell(
splashColor: Colors.blue,
child: new Container(
height: 75,
width: 400,
child: new Center(
child: new Text(quiz.title)
)
),
onTap: () => print("Does nothing")
)
);
}
);}
No matter what I do, there are no visual splashes on the Card nor does the onTap handler ever execute.
I've tried every solution I've seen here on SO. Really confused on this one.
I don't know how you're doing it, try this code:
Widget build(BuildContext context) {
var primary = Theme.of(context).primaryColor;
var secondary = Colors.white;
var selected = true;
return Scaffold(
body: TweenAnimationBuilder(
tween: ColorTween(begin: secondary, end: selected ? primary : secondary),
duration: Duration(milliseconds: 300),
builder: (BuildContext context, Color color, Widget child) {
return Card(
color: color,
child: InkWell(
splashColor: Colors.blue,
onTap: () => print('Does nothing'),
child: Container(
height: 75,
width: 400,
child: Center(child: Text('Title')),
),
),
);
},
),
);
}
Unfortunately after getting a little more creative with my search queries, I discovered the answer is that this is simply how Flutter works. Children of a ListWheelScrollView cannot receive gesture input. I suppose this shouldn't be that surprising given how the widget is intended to function. To save others the frustration, please see the duplicate SO question and discussion on Google's Flutter Github linked below. Also linked is a Pub workaround package found via the Flutter Github discussion I'm looking into now.
ListTile OnTap is working when I use ListView. But when i use ListWheelScrollView it doesnt work
https://github.com/flutter/flutter/issues/38803
https://pub.dev/packages/clickable_list_wheel_view

How to display a snackBar in scaffold is body is composed of Stack?

I am trying to display a snack bar on my screen. But the snack bar is covering the whole screen instead of just being displayed at the bottom. Here is my code.
Scaffold(
key: _scaffoldKey ,
backgroundColor: Colors.pink,
body: Stack(
children: <Widget>[
Text('Display Scaffold'),
Positioned(
top: 110,
child: Container(
child: FlatButton(
onpressed: (){
final snackBar = SnackBar(duration: Duration(seconds: 4),
content: Center(child: Text('Welcome')),
) ;
_scaffoldKey.currentState.showSnackBar(snackBar);
}
),
),
),
),
);
Remove Center widget. This makes the widget as big as the parent allows.
If you need to center your text, just use textAlign: TextAlign.center property in Text widget.
Have you try this ?
Scaffold.of(context).showSnackBar(snackBar);
instead of
_scaffoldKey.currentState.showSnackBar(snackBar);
It may be the solution.
I hope it could help you !

flutter automatic testing : Tap on a button don't work in drawer

I'm trying to do some TDD with flutter and when the test is running, tapping on a button doesn't work if it's in a drawer. The button works perfectly fine with a normal user.
In the following example we press on two buttons which print a message in the console. Here's the actions :
locate and tap on a button in the scaffold: OK
open the drawer: OK
Locate button in drawer: OK
Tap on the drawer button: nothing happen
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Test that drawer is apparing and we can click on button',
(WidgetTester tester) async {
final scaffoldKey = GlobalKey<ScaffoldState>();
await tester.pumpWidget(new MaterialApp(
title: 'Where Assistant',
home: Scaffold(
key: scaffoldKey,
body: Column(
children: <Widget>[
Text('test text'),
RaisedButton(
onPressed: () {
print('OK on main screen');
},
child: Icon(Icons.access_alarm),
),
],
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the Drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app
// ...
},
),
RaisedButton(
onPressed: () {
print('OK drawer');
},
child: Icon(Icons.add),
)
],
),
),
),
));
await tester.pump();
expect(find.text('test text'), findsOneWidget);
expect(find.byIcon(Icons.access_alarm), findsOneWidget);
await tester.tap(find.byIcon(Icons.access_alarm));
expect(find.byIcon(Icons.add), findsNothing);
scaffoldKey.currentState.openDrawer();
await tester.pump(); // drawer should appear
expect(find.text('Item 2'), findsOneWidget);
expect(find.byIcon(Icons.add), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
print('end of test');
});
}
I also ran into a similar issue.
The tester was able to find the FlatButton in the drawer:
expect(find.byType(FlatButton), findsOneWidget);
tester.tap did not appear to be working:
await tester.tap(find.byType(FlatButton));
But the solution mentioned in this Flutter issue did work:
FlatButton button = find.widgetWithText(FlatButton, 'TextExample').evaluate().first.widget;
button.onPressed();
finally found a workaround : just need to add some delay before tapping in the drawer.
So I added
await tester.pump(const Duration(milliseconds: 100));
before tapping on buttons
I haven't played with WidgetTest yet, but I was able to achieve tapping on a button inside drawer, with flutter driver.
test('test button tap in drawer', () async {
final SerializableFinder drawerOpenButton = find.byTooltip('Open navigation menu');
final SerializableFinder btn = find.byValueKey('firstButton');
await driver.waitFor(drawerOpenButton);
await driver.tap(drawerOpenButton);
await driver.waitFor(btn);
await driver.tap(btn);
print('tapped');
});
In main.dart, I used key property for the RaisedButton I want to tap on.
My problem was that the onTap callback for the GestureDetector was too deep in the Widget Tree,
I ended up creating a new stateless Widget type which directly wraps up the GestureListener and passes the necessary data there.
So the find.byWidgetPredicate((widget) => widget is ActionClickableButton).first worked.
Wait for Drawer to Open Fully
Drawer opening is an animation, sliding out over time.
Until the Drawer is fully extended we shouldn't try testing taps for widgets inside it.
pumpAndSettle() or "double pump" after initiating .openDrawer() are two solutions.
pumpAndSettle()
final ScaffoldState scafState = tester.firstState(find.byType(Scaffold));
scafState.openDrawer();
await tester.pumpAndSettle();
// do taps below here
if you have other animations running during test, this won't "settle" / return until all animations are complete or 10 minutes has passed (default timeout).
...or
Double pump()
final ScaffoldState scafState = tester.firstState(find.byType(Scaffold));
scafState.openDrawer();
await tester.pump(); // 99% of Drawer still offstage
await tester.pump(const Duration(seconds: 1)); // ←- This is important
// do taps below here
scaffoldState.openDrawer();
Drawer opening has been "requested"
but nothing has changed onstage yet (nothing built/laid out/painted), Drawer is 100% offstage
Drawer widgets are offstage / cannot be tapped
pumpAndSettle()
await tester.pumpAndSettle();
pumps a single frame every 100ms while (any) animations are ongoing
in between 100ms waits, this does nothing
returns when binder.hasScheduledFrame is false (no more animation frames, from Drawer opening or other animations)
Double pump()
await tester.pump();
triggers a build/layout/paint, starting the animation
only the first frame of the Drawer opening animation here
99% of Drawer contents are still offstage & untappable
hit targets are offstage / cannot be tapped
await tester.pump(const Duration(seconds: 1);
this waits a full second before calling pump() (again) to "render" a frame of current situation. (This cannot be used alone to start & wait for animation to complete as this waits 1 second to pass then calls pump() once. That would only start the animation.)
animation started by previous pump() has had 1000ms (of mostly skipped frames) to complete
Drawer should be fully open and entirety of Drawer contents should be onstage
should be OK to do find/tap of Drawer button widgets now
a Scaffold tests and Drawer tests on the Flutter framework itself use this technique
Try this code.. this is work on drawer click..
class DrawerItem {
String title;
IconData icon;
DrawerItem(this.title, this.icon);
}
class MyHomeScreenStateFull extends StatefulWidget {
// here define drawer item
final drawerItems = [
new DrawerItem("Home", Icons.save),
new DrawerItem("Event", Icons.local_pizza)
];
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return MyHomeScreenState();
}
}
class MyHomeScreenState extends State<MyHomeScreenStateFull> {
int selectedDrawerIndex = 0;
// here define other screen.
getDrawerItemWidget(int pos) {
switch (pos) {
case 0:
return new FirstFragmentStateFull();
case 1:
return new SecondFragmentStateFull();
default:
return new Text("Error");
}
}
onSelectItem(int index) {
setState(() => selectedDrawerIndex = index);
Navigator.of(context).pop(); // close the drawer
}
#override
Widget build(BuildContext context) {
var drawerOptions = <Widget>[];
for (var i = 0; i < widget.drawerItems.length; i++) {
var d = widget.drawerItems[i];
drawerOptions.add(new ListTile(
leading: new Icon(d.icon),
title: new Text(
d.title,
style: TextStyle(fontSize: 15),
),
selected: i == selectedDrawerIndex,
onTap: () => onSelectItem(i),
));
}
return Scaffold(
appBar: AppBar(
title: new Text(widget.drawerItems[selectedDrawerIndex].title),
),
/*body: Center(
child: Text("Welcome To Home"),
),*/
drawer: Drawer(
child: Column(
children: <Widget>[
new UserAccountsDrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
accountName: Text("Vikas Pandey "),
accountEmail: null,
currentAccountPicture: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(
"https://via.placeholder.com/600/92c952"))),
),
),
new Column(
children: drawerOptions,
)
],
),
),
body: getDrawerItemWidget(selectedDrawerIndex),
);
}
}

How to add a margin or padding to a Snackbar?

I have been working on the Snackbar and achieved in my project successfully. However, there is this little thing which I want to add to Snackbar and that is the margins. I have seen in this link : Snackbars - Material Design
I really want my Snackbar to come like this :
What I'm getting is this now :
My code is :
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 team have updated the snackbar to match the material design in this PR. You can simply get the new behavior by setting
behavior: SnackBarBehavior.floating
Here is a sample code
final snackBar = SnackBar(
elevation: 6.0,
backgroundColor: Configs.current.COLORS_PRIMARY,
behavior: SnackBarBehavior.floating,
content: Text(
"Snack bar test",
style: TextStyle(color: Colors.white),
),
);
and the result will look like this
Not sure about margins. Round corner SnackBar can be created like:
Scaffold
.of(context)
.showSnackBar(
SnackBar(
content: Text(message),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)))));
Use required border radius in above.
Update:
You can use floating SnackBar to add default margins. Pass below to SnackBar constructor:
Scaffold
.of(context)
.showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)))));
Screenshot:
Minimal code:
var snackbar = SnackBar(
content: Text('Hello World!'),
margin: EdgeInsets.all(20),
behavior: SnackBarBehavior.floating,
);
Scaffold.of(context).showSnackBar(snackbar);
Full code:
#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'),
);
}),
),
);
}
Disclaimer: This answer is outdated and should only be viewed for historical reasons. Choose an appropriate solution from above 👀
Sadly I haven't found any options or possibilities, given by Flutter, to implement a round SnackBar.
If you really need/want the rounded corners and spacing you can copy the source code of the SnackBar and make your adjustments to the copy. Remember to add implements SnackBar to your class definition.
I've started the implementation and added the rounded corners. You'll just have to add the bottom padding for Phones like the iPhone X, which have elements at the bottom, obscuring the view. (To get the bottom spacing you can use MediaQuery.of(context).viewInsets.bottom.)
Standalone example you can copy:
main.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,
);
}
}
Changes made to SnackBar:
Renamed SnackBarto MySnack
Added implements SnackBar to MySnack class
Set bottom: false inside the SafeArea in MySnack build method
Added a Container around the Material which is the background of the SnackBar
Moved the color declaration from Material to the created Container
Added the desired borderRadius (together with the color) as a BoxDecoration to the Container