Transformation distorts the border radius of container in flutter - flutter

Align(
alignment: Alignment.bottomCenter,
child: Container(
//padding: EdgeInsets.all(20.0),
height: 50,
width: 320,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0))),
child: Stack(children: <Widget>[
Align(
alignment: Alignment.center,
child: Transform(
alignment: Alignment.topRight,
transform: Matrix4.diagonal3Values(4.0, 1.0, 1.0),
child: Container(
height: 45,
width: 45,
decoration: BoxDecoration(
//color: Color(0xffff8d27),
color: Colors.grey,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(10.0))
)),
),
),
Row(
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Reservation',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
Align(
alignment: Alignment.center,
child: Icon(Icons.attach_money)),
Expanded(
child: Center(
child: Text(
'Now',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
)
],
)
])))
This is the current result
I have used a stack with the bottom layer being a container with grey color and 15.0 border radius aligned in the center, and the top layer containing text in two expandables and an icon in centre. I am trying to implement a two way slider button. I have not implemented the sliding part yet but that won't be an issue. When the user slides on a particular side the grey colored container in the bottom of stack streches on the respective side of the slide in turn depicting a selection. I am using the transform widget to scale the container in x dimension or simply speaking stretching it. The problem is that this transformation tends to distort the border radius of the container which is not producing the desired effect.
How can I stretch or transform the container so that it does not affect the border radius?

final _transformRight = Matrix4.identity()..translate(130.0);
final _transformLeft = Matrix4.identity(); //It means zero.
bool _shouldTransformRight = false;
#override
Widget build(BuildContext context) {
...
body: GestureDetector(
child: Container(
...
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
),
child: Stack(
children: <Widget>[
AnimatedContainer(
...
duration: Duration(milliseconds: 200),
decoration: BoxDecoration(
color: Colors.grey,
//You can create a variable to unify the
//borderRadius for all containers.
borderRadius: BorderRadius.circular(15.0),
),
transform: _shouldTransformRight
? _transformRight
: _transformLeft,
),
Row(...),
],
),
),
onHorizontalDragEnd: (details) {
setState(() {
_shouldTransformRight = !_shouldTransformRight;
});
}
),
...
}
Summary of the edits that I've made to your code:
No need to provide a shape and a corner radius for the container at the same time. Using one of them alone is sufficient.
In the corner radius of the container I used BorderRadius.circular(...) instead of BorderRadius.all(...), because it's shorter.
I've replaced the first child of the Stack with AnimatedContainer and used it's transform argument to position it accordingly. The Matrix4.diagonal3Values(4.0, 1.0, 1.0) it was the cause for the distorted border radius.
Lastly, I wrapped the whole widget tree in a GestureDetector, so that I can get the user input, which based on it I'm gonna translate the AnimatedContainer. You can configure which callBack of GestureDetector to use, here I've used onHorizontalDragEnd.

Related

How can I apply notch curve to Widget?

I have a design as you can see below and the question is how can I apply this notch effect on Arrow ? Is there any easy way to do it just like in Floating Action Button ? Thanks in advance.
I have created Dartpad, please look into this and do let me know if you need any help.
Dartpad Link : https://dartpad.dev/flutter?9bd55396e067e71a839851e18905f478
Code:
Padding(
padding: const EdgeInsets.all(16.0),
child: SizedBox(
height: 70,
child: Stack(alignment: Alignment.centerRight, children: [
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: Container(
height: 70,
decoration: BoxDecoration(
color: Colors.cyan, borderRadius: BorderRadius.circular(8)),
),
),
Container(
width: 40,
height: 65,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 5)),
child: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.cyan,
child: const Icon(Icons.chevron_right),
),
)
]),
),
);
Output:
Hey you can achieve it in 2 way
1st one and simple - Create a box decoration widget and a circle shape widget with white border and put these together with Stack and fit as per your requirements.
2nd use Custom Clipper or Custom Paint and draw your shape.

How to center ElevatedButton in flutter?

I imported [sliding up panel][2] from pub.dev and everything is good except for one issue. I'm looking for a way to center the 'blue circle' vertically. (in between 'red appbar' and 'buttom slider') I tried wraping the code with 'center' or using mainaxisalignment but it doesn't work. Other things I tried which I found on google just gave me an errors. Please help.
Thanks in advance
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(200),
child: AppBar(
backgroundColor: Colors.pinkAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(bottom: Radius.circular(100))
),
flexibleSpace: Container(
alignment: Alignment.center,
child: Text(
'Red AppBar',
style: TextStyle(
color: Colors.white,
fontSize: 50.0,
fontWeight: FontWeight.bold
),
),
),
),
),
body: SlidingUpPanel(
renderPanelSheet: false,
panel: _floatingPanel(),
collapsed: _floatingCollapsed(),
body: ElevatedButton(
onPressed: () {},
child: Text('Blue Circle'),
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(20),
),
)
),
);
Widget _floatingCollapsed() {
return Container(
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
),
margin: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
child: Center(
child: Text(
"buttom slider",
style: TextStyle(color: Colors.white),
),
),
);
}
Widget _floatingPanel() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(24.0)),
boxShadow: [
BoxShadow(
blurRadius: 20.0,
color: Colors.grey,
),
]
),
margin: const EdgeInsets.all(24.0),
child: Center(
child: Text("LEVEL INFO"),
),
);
}
Also, what could be the best way to give margin to the 'blue circle' so it won't touch the far left and far right side of the screen?
The SlidingUpPanel's body is using Stack, also getting full screen size. You can check
#override
Widget build(BuildContext context) {
return Stack(
alignment: widget.slideDirection == SlideDirection.UP
? Alignment.bottomCenter
: Alignment.topCenter,
children: <Widget>[
//make the back widget take up the entire back side
widget.body != null
? AnimatedBuilder(
animation: _ac,
builder: (context, child) {
return Positioned(
top: widget.parallaxEnabled ? _getParallax() : 0.0,
child: child ?? SizedBox(),
);
},
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: widget.body,
),
)
You can play with Align on body and wrapping with SizedBox. You can use LayoutBuilder or MediaQuery to provide size.
body: SlidingUpPanel(
renderPanelSheet: false,
panel: _floatingPanel(),
collapsed: _floatingCollapsed(),
body: Align(
alignment: Alignment(0.0, -.3), //play with it
child: SizedBox(
width: 200,
height: 200,
child: ElevatedButton(
As per the documentation of sliding_up_panel, the body represents the widget that lies underneath the sliding panel and it automatically sizes itself to fill the screen. So it occupies full screen. Since you expanded AppBar to 200 unit, the body Widget was pushed down, hence you are seeing the centre point also pushed down by 200 unit. If you change the body height to occupy full screen from the start, by setting the Scaffold property extendBodyBehindAppBar with true, you will place the widget in the correct centre point.
if you add the below line immediately after the Scaffold. You will get desired result.
return Scaffold(
extendBodyBehindAppBar:true,

How to stack two icons one on top of the other?

I have two icons in a stack. I want one to hide the other, but instead it's semi-transparent and the other is shown behind it.
This is the code:
Stack(
children: [
Container(child: Icon(Icons.account_circle_rounded), padding: EdgeInsets.only(left: 10),),
Container(child: Icon(Icons.account_circle_rounded), padding: EdgeInsets.only(left: 20),),
],
)
This is how it looks:
Icons are more like SVG, use vector to draw shape. There are some empty spaces inside the icon to draw the path, and we can see the background though this. Also, it contains padding around it, You can use your assets to draw the UI.
We can wrap with another ColoredBox to hide the background icon, but it will get extra padding from IconData.
This snippet shows basic level structure.
SizedBox(
height: 24,
width: 24 * 1.7,
child: Stack(
alignment: Alignment.center,
children: [
Align(
alignment: Alignment.centerLeft,
child: Container(
child: const Icon(
Icons.account_circle_rounded,
),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
child: const Icon(
Icons.account_circle_sharp,
),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
),
),
],
),
),
Widget renders bottom to top.

Flutter and BoxShapeBorder

I'm trying to add the "Protection Icon" on top of the "Box Shape" border and make sure that when it moves, it's within Its borderlines on each screen size:
Code:
return Scaffold(
body: Container(
decoration: BoxDecoration(
color: const Color(0xff182341),
),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
border: Border.all(color: const Color(0xff5F687E)),
shape: BoxShape.circle,
),
),
Stack( // Using stack here because of other elements that will be added.
children: [
AnimatedPositioned(
top: 280,
left: 130,
duration: Duration(milliseconds: 500),
child: Container(
child: Image(image: AssetImage('images/g8.png')),
),
),
]
),
]),
),
);
Result:
However, I only got the iPhone 13 Pro's screen right, the other's aren't where they should be
Don't use Positioned to specify an absolute offset, instead try to use widgets that allow you to set a relative offset (like a percentage), so they will scale well with different screen sizes.
For example, you can replace your Positioned for Align, like this:
Align(
alignment: Alignment(-0.75, 0.48), // play around with these values
child: ...
)
The two arguments for Alignment class are used to set a percentage offset on x and y axis, each ranging from -1.0 to +1.0, with 0.0 being the center.

Flutter multiple items in Row with Expanded/Flexible

I want to have more(in this example 4) items in Row. I tried use Expanded or Flexible or mainAxisAlignment: MainAxisAlignment.spaceEvenly to fill up all width of screen. But I want to heve items lets say specific width and height 50 pixels, and GestureDetector which is around item to be 1/4 of screen(4 items = full width). So everywhere I tap in the row something will be selected. My problem is that every my solution deforms items(circles) or circle must be very huge to width = height to not be deformed.
I also tried wrapping whole _ratingEmoji in Expanded and giving ratingItem.ratingIndicator constraints to max width but with same result.
Row of items:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var ratingItem in editGroceryRatingProvider.foodRatings)
_ratingEmoji(editGroceryRatingProvider, ratingItem, context),
],
),
Item:
Widget _ratingEmoji(EditGroceryRatingProvider editGroceryRatingProvider,
SelectedFoodRating ratingItem, BuildContext context) {
return GestureDetector(
child: ratingItem.selected
? Container(
width: MediaQuery.of(context).size.width / 8,
height: MediaQuery.of(context).size.width / 8,
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
border: Border.all(
color: Color(0xFF312ADB),
width: 3.0,
),
borderRadius: BorderRadius.circular(32.0)),
child: Center(
child: ratingItem.ratingIndicator,
),
)
: ratingItem.ratingIndicator,
onTap: () => editGroceryRatingProvider.updateSelectedRating(ratingItem),
);
}
RatingIndicator which is "circle" item to be on the screen:
Container(
decoration: BoxDecoration(
border: Border.all(
color: color,
width: 1.5,
),
borderRadius: BorderRadius.circular(64.0),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
width: 3.0,
),
borderRadius: BorderRadius.circular(64.0),
),
child: ClipOval(
child: Container(
width: width ?? MediaQuery.of(context).size.width / 7,
height: height ?? MediaQuery.of(context).size.width / 7,
color: color,
),
),
),
);
Thanks a lot.
Update:
Items should stay as they are on screenshot, but "whole" row should be clickable(means even if you click between 2 items, one closer to click should be selected)
Check, please solution.
The whole area is clickable. You just need to add your item instead of mine.
#override
Widget build(BuildContext context) {
final colors = [Colors.red, Colors.blue, Colors.brown, Colors.cyan];
return Scaffold(
body: Container(
alignment: Alignment.center,
color: const Color(0xffFAFAFA),
child: Row(
children: List.generate(
colors.length,
(index) => Expanded(
child: GestureDetector(
onTap: () => print('Tapped:$index'),
child: Container(
height: 50,
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: colors[index],
),
),
)),
)),
),
),
);