Why Flutter IconButton Animation Shows Under the Row - flutter

In my app, I setup a IconButton to render over a Row with a color background. Unfortunately, the ripple animation on button press renders under the Row (as shown in the screencast). How do i make the ripple show over the Row?
class Card extends StatelessWidget {
final Issue issue;
Color _bgColor;
Card({this.issue}) {
_bgColor = colorSwatch[issue.hashCode % colorSwatch.length];
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 12, left: 18, right: 18),
padding: EdgeInsets.only(top: 8, bottom: 8),
decoration: new BoxDecoration(
color: _bgColor,
border: new Border.all(color: _bgColor),
borderRadius: BorderRadius.all(
Radius.circular(10.0)
),
),
child: Row(children: [
IconButton(
padding: EdgeInsets.only(right: 16),
icon: Icon(Icons.play_arrow, color: Colors.white, size: 48),
tooltip: 'Start ${issue.issueName}',
onPressed: () {},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
issue.title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
softWrap: true,
),
),
Text(
issue.issueName,
style: TextStyle(
color: Colors.white,
),
),
],
),
),
]));
}
}

The ripple is part of the Material effect. Wrap your IconButton with Material:
Material(
color: _bgColor,
child: IconButton(
padding: EdgeInsets.only(right: 16),
icon: Icon(Icons.play_arrow, color: Colors.white, size: 48),
tooltip: 'Start ${issue.issueName}',
onPressed: () {},
),
),
UPDATE
To be more specific to your goal, you can replace your Container with Material.
return Material(
color: _bgColor,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
child: Container(
margin: EdgeInsets.only(top: 12, left: 18, right: 18),
padding: EdgeInsets.only(top: 8, bottom: 8),
backgroundBlendMode: BlendMode.multiply,
child: Row(
...
),
);

It looks like a bug in the Flutter Framework.
This occurs only with IconButton, no problem with FlatButton.
Possible workaround is to set BlendMode to multiply BlendMode.multiply.
try this:
Container(
decoration: BoxDecoration(
color: Colors.greenAccent[400],
backgroundBlendMode: BlendMode.multiply),
child: ListTile(
leading: IconButton(icon: Icon(Icons.play_arrow), onPressed: () {}),
title: Text("issue title"),
subtitle: Text("description"),
),
),
Update
issue on github

You can wrap your Row inside a InkWell, this will give you the touch ripples.
I have removed the background color of the container, had issues with it showing the ripple, as well as changed the text color to black.
#override
Widget build(BuildContext context) {
return Container(
height: 100,
margin: EdgeInsets.only(top: 12, left: 18, right: 18),
padding: EdgeInsets.only(top: 8, bottom: 8),
decoration: new BoxDecoration(
color: Colors.transparent,
border: new Border.all(color: Colors.orange),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: InkWell(
// added here
onTap: () {},
child: Row(children: [
IconButton(
padding: EdgeInsets.only(right: 16),
icon: Icon(Icons.play_arrow, color: Colors.black, size: 48),
tooltip: 'Start issue',
onPressed: () {},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
'issue.title',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
softWrap: true,
),
),
Text(
'issue.issueName',
style: TextStyle(
color: Colors.black,
),
),
],
),
),
]),
));
}
References
Add ripples

Ripple always takes on top of the material widgets. So if there is anything on top of the material widget you will see ripples under your widget.
Just wrap your IconButton with a Material widget with the material set to use the "transparency" type and it should work.
You can use the below generic widget for adding ripples to your widgets.
class AddRipple extends StatelessWidget {
final Widget child;
final Function onTap;
AddRipple({#required this.child, #required this.onTap});
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
child,
Positioned.fill(
child: new Material(
color: Colors.transparent,
child: new InkWell(
onTap: onTap,
))),
],
);
}
}
You can also take in a color in the constructor and user 'splashColor' property of the InkWell widget to set the splash color.

Related

Flutter - How to make Container grow with a dynamic ListView?

I have a Container that contains a ListView, I want the Container to grow as big as the ListView. When I remove the height on the Container, I get an error saying (Vertical viewport was given unbounded height). How can I achieve this with flutter?
The ListView can contain multiple card items.
order_stack.dart file:
import 'package:flutter/material.dart';
class OrderStack extends StatelessWidget {
const OrderStack({Key? key, required this.id, required this.tableNumber}) : super(key: key);
final int id;
final int tableNumber;
#override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
color: Color.fromRGBO(33, 49, 55, 1),
),
clipBehavior: Clip.antiAlias,
child: Column(
children: [
Container(
height: 80,
width: 400,
color: const Color.fromRGBO(93, 194, 188, 1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.only(left: 30),
child: Text(
'Table $tableNumber',
style: const TextStyle(
color: Colors.black,
fontSize: 25,
fontWeight: FontWeight.w900,
),
),
),
Container(
padding: const EdgeInsets.only(right: 30),
child: Text(
'#$id',
style: const TextStyle(
color: Colors.black,
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
Container(
height: 70,
width: 400,
child: ListView(
physics: const NeverScrollableScrollPhysics(),
children: <Widget>[
Card(
elevation: 0,
margin: EdgeInsets.zero,
child: ListTile(
tileColor: Colors.yellow[200],
leading: const Text(
'3X',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.w800
),
),
title: const Text(
'Good Burger',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w800
),
),
subtitle: const Text('Here is a second line'),
trailing: IconButton(
iconSize: 30,
icon: const Icon(Icons.expand_more_outlined),
color: Colors.black,
onPressed: () {},
),
onTap: () => print('Good Burger'),
),
),
],
),
),
],
),
);
}
}
The easiest way would be to wrap your ListView() widget with a Column() and this column given a height of the user's screen with media query remember you should also use shrink wrap to true in the list and wrap your body with SingleChildScrollView()like so:
...
body: SingleChildScrollView(
...
Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
///Your listview
ListView(
shrinkWrap: true,
)
]
)
)
...
)
Replace the Container with the Expanded widget. It will expand the ListView to fit the available space in the Column.
Expanded(
child: ListView(
...
)

crossAxisAlignment: CrossAxisAlignment.end doesn't work

I am pretty new to programming and also to Flutter, so please excuse me if my question is silly. I am trying to push Skip button to the bottom left corner of the screen. But the crossAxisAlignment: CrossAxisAlignment.end code doesn't work. How could I do this the right way so it can adapt to all screen formats properly? The problematic part is at the bottom of the entire code. Thank you in advance!
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(left: 15, top: 30),
width: 230,
decoration: BoxDecoration(),
child: Image.asset('images/logo1.png'),
),
Container(
margin: EdgeInsets.only(left: 15, top: 30),
child: Text(
'Login',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
),
Container(
margin: EdgeInsets.only(left: 15, right: 15, top: 30),
height: 50,
width: double.infinity,
child: Text(
'E-mail',
style: TextStyle(fontSize: 20, color: Colors.black54),
),
padding: EdgeInsets.only(top: 10, left: 15, bottom: 10),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.black54,
),
borderRadius: BorderRadius.all(
Radius.circular(7),
),
),
),
Container(
margin: EdgeInsets.only(left: 15, right: 15, top: 15),
height: 50,
width: double.infinity,
child: Text(
'Password',
style: TextStyle(fontSize: 20, color: Colors.black54),
),
padding: EdgeInsets.only(top: 10, left: 15, bottom: 10),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.black54,
),
borderRadius: BorderRadius.all(
Radius.circular(7),
),
),
),
Container(
margin: EdgeInsets.only(left: 15, right: 15, top: 15),
child: MaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
height: 50.0,
minWidth: double.infinity,
color: Colors.orangeAccent,
child: Text(
"Login",
style: TextStyle(
fontSize: 20.0,
color: Colors.white,
),
),
onPressed: () => {},
splashColor: Colors.redAccent,
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Center(
child: TextButton(
style: TextButton.styleFrom(
textStyle: TextStyle(fontSize: 17),
),
onPressed: () {},
child: Text(
'Forgot your password?',
style: TextStyle(color: Colors.black54),
),
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
margin: EdgeInsets.only(top: 5),
child: TextButton(
style: TextButton.styleFrom(
textStyle: TextStyle(fontSize: 17),
),
onPressed: () {},
child: Text(
'Skip',
style: TextStyle(color: Colors.black54),
),
),
),
],
),
],
),
);
}
}
Mobile Display
Remove Row(). Add alignment: Alignment.centerRight, to your Container.
Container(
margin: EdgeInsets.only(top: 5),
alignment: Alignment.centerRight,
child: TextButton(
style: TextButton.styleFrom(
textStyle: TextStyle(fontSize: 17),
),
onPressed: () {},
child: Text(
'Skip',
style: TextStyle(color: Colors.black54),
),
),
),
To change the position for Row children horizontally
you need to use mainAxisAlignment property
mainAxisAlignment property:
How the children should be placed along the main axis.
For example, MainAxisAlignment.start, the default, places the children at the start (i.e., the left for a Row or the top for a Column) of the main axis.
source flutter https://api.flutter.dev/flutter/widgets/Flex/mainAxisAlignment.html
You can use Spacer widget before Row. By default, row children will start from left.
///others widgets
const Spacer(), /// just add this
Row(....
More about Spacer widget.

Flutter showModalBottomSheet does not allow transparent background container

Am I using some widget here incorrectly or is this an issue with showModalBottomSheet?
As you can see here in the screenshots, the test code I've included displays a button in a 70% transparent container. This looks correct in main body. However, when you tap the Open Bottom Sheet button to display the showModalBottomSheet() function you'll see that that exact same button in a container with 70% transparent bg color is now 100% opaque.
Why is the opacity of the container being ignored in the showModalBottomSheet widget tree?
I've tried using Opacity widget and a Stack, but transparency does not seem to be allowed in a showModalBottomSheet?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: BottomSheet(),
);
}
}
class BottomSheet extends StatelessWidget {
const BottomSheet({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
void _showBottomSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: MediaQuery.of(context).size.height * 0.75,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 27.0, bottom: 27.0),
child: Text(
'Select',
style: TextStyle(
fontSize: 20.0,
color: Colors.blue,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 24.0, right: 24.0),
child: GridView.count(
crossAxisCount: 3,
mainAxisSpacing: 25.0,
crossAxisSpacing: 25.0,
padding: EdgeInsets.only(
bottom: 100.0,
),
children: [
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
Placeholder(),
],
),
),
),
Container(
width: double.infinity,
height: 161.0,
color: Colors.white.withOpacity(0.3),
child: Padding(
padding: const EdgeInsets.only(
left: 24.0,
right: 24.0,
),
child: Center(
child: OutlinedButton(
child: Text(
'Button in container with 70% transparent bg ignored',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
textAlign: TextAlign.center,
),
onPressed: () {},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.blue,
minimumSize: Size(double.infinity, 52.0),
),
),
),
),
),
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(32.0),
topRight: const Radius.circular(32.0),
),
),
);
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(32.0),
topRight: const Radius.circular(32.0),
),
),
barrierColor: Color.fromRGBO(0, 0, 0, 0.9),
isScrollControlled: true,
);
}
return Scaffold(
backgroundColor: Colors.indigo,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text(
'Open bottom sheet',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
onPressed: _showBottomSheet,
),
SizedBox(
height: 30.0,
),
Container(
width: double.infinity,
height: 161.0,
color: Colors.white.withOpacity(0.3),
child: Padding(
padding: const EdgeInsets.only(
left: 24.0,
right: 24.0,
),
child: Center(
child: OutlinedButton(
child: Text(
'Button in container with 70% transparent bg',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
textAlign: TextAlign.center,
),
onPressed: () {},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.blue,
minimumSize: Size(double.infinity, 52.0),
),
),
),
),
),
],
),
),
);
}
}
As you can see here, it appears to be a bug in Flutter's showModalBottomSheet().
To overcome this, I built my own bottom sheet in a Stack to get the transparency affect I needed on the bottom sheet button.

How to fix widget overflowing rounded border container flutter

I have a container with topLeft border as rounded, in its child I have a column with a SingleChildScrollView having the widgets for each full name, mobile etc.
When I'm scrolling up these children come over the container and overflow the rounded border.
I want to fix this.
I want this to go behind the container and not come in front of it.
class SetupAccountScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BaseWidget(
mainTitle: "Setup Account",
description:
"One last step to an awesome shopping experience, we’d like to know you more",
content: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 50,
),
FullNameInputWidget(),
MobileNumberInputWidget(),
AddressInputWidget(),
],
),
),
);
}
}
class BaseWidget extends StatelessWidget {
final String mainTitle;
final String description;
final Widget content;
BaseWidget({this.mainTitle, this.description, this.content});
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: Text(
"Back",
style: GoogleFonts.playfairDisplay(fontSize: 15),
),
leading: IconButton(
icon: Icon(
Icons.chevron_left,
size: 40,
),
onPressed: () {}),
backgroundColor: Colors.transparent,
),
body: Stack(
children: [
Positioned(
bottom: 0,
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.only(topLeft: Radius.circular(100))),
child: content),
),
Positioned.fill(
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(mainTitle,
style: GoogleFonts.playfairDisplay(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 25)),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(description,
style: GoogleFonts.playfairDisplay(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: 15)),
),
],
),
)))
],
),
);
}
}
Instead of decoration for the container, use ClipRRect to make the round corner, like so.
Positioned(
bottom: 0,
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(100)),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: content,
),
),
),

trigger flutter IconButton ripple animation programmtically?

Is there a way to programmatically trigger the IconButton ripple animation? The following is some code:
#override
Widget build(BuildContext context) {
return Material(
child: Container(
margin: EdgeInsets.only(top: 12, left: 18, right: 18),
padding: EdgeInsets.only(top: 8, bottom: 8),
decoration: BoxDecoration(
color: _bgColor,
border: new Border.all(color: _bgColor),
borderRadius: BorderRadius.all(Radius.circular(
10.0) // <--- border radius here
),
),
child: Row(children: [
IconButton(
padding: EdgeInsets.only(right: 16),
icon: Icon(Icons.play_arrow, color: Colors.white, size: 48),
tooltip: 'Start ${issue.issueName}',
onPressed: () {},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
issue.title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
softWrap: true,
),
),
Text(
issue.issueName,
style: TextStyle(
color: Colors.white,
),
),
],
),
),
])));
}