Related
I have been trying to create a raised button with a rounded corner, and gradient background but to no success. I can only implement one or the other. It's been 2 hours and I haven't found a solution myself, on how I can implement both a rounded corner, and a gradient background together.
Below are my codes of my latest attempt to implement a raised button with rounded corner, and gradient background.
Custom class of GradientButton
class RaisedGradientButton extends StatelessWidget {
final Widget child;
final Gradient gradient;
final double width;
final double height;
final Function onPressed;
const RaisedGradientButton({
Key key,
#required this.child,
this.gradient,
this.width = double.infinity,
this.height = 50.0,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: width,
height: 50.0,
decoration: BoxDecoration(
gradient: new LinearGradient(
colors: [
Colors.blue,
Colors.red,
],
begin: FractionalOffset.centerLeft,
end: FractionalOffset.centerRight,
),
),
child: Material(
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(128.0)),
// color: Colors.transparent,
child: InkWell(
onTap: onPressed,
child: Center(
child: child,
)),
),
);
}
}
How I use the above code:
RaisedGradientButton(
onPressed: navigateToNextPage,
child: Text("Select Community"),
)
How it looks like:
As you can see, the gradient is there, but when I attempt to create a rounded corner, it overlaps, and the gradient is behind.
There is a simple solution. Add the same border radius to both the button and the container inside the button. Here is a simple example.
RaisedButton(
onPressed: () {},
textColor: Colors.white,
color: Colors.transparent,
padding: EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(18.0),
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(18),
gradient: LinearGradient(
colors: <Color>[
Colors.black38,
Colors.black26,
Colors.white38,
],
),
),
padding: const EdgeInsets.fromLTRB(24, 12, 24, 12),
child: const Text('Sign In', style: TextStyle(fontSize: 20)),
),
),
I suggest you put a Container with a gradient below the button in a Stack and cut its corners with ClipRRect while leaving the button's color transparent. This way you keep touch feedback and pressed button shadow, as well as accessibility support.
class RaisedGradientButton extends StatelessWidget {
final Widget child;
final Gradient gradient;
final double width;
final double height;
final Function onPressed;
final borderRadius = BorderRadius.circular(128.0);
RaisedGradientButton({
Key key,
#required this.child,
Gradient gradient,
this.width = double.infinity,
this.height = 50.0,
this.onPressed,
}) : this.gradient = gradient ??
LinearGradient(
colors: [
Colors.blue,
Colors.red,
],
begin: FractionalOffset.centerLeft,
end: FractionalOffset.centerRight,
),
super(key: key);
#override
Widget build(BuildContext context) => Stack(
children: [
Positioned.fill(
child: ClipRRect(
borderRadius: borderRadius,
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
gradient: gradient,
),
),
),
),
Container(
width: width,
height: height,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
),
padding: EdgeInsets.zero,
child: Center(child: child),
onPressed: onPressed,
color: Colors.transparent,
),
),
],
);
}
If anyone ever encounters the same issue. Here is my code on how I got it solved.
class GradientButton extends StatelessWidget {
final Widget child;
// final Gradient gradient;
final double width;
final double height;
final bool isEnabled;
final Function onPressed;
const GradientButton({
Key key,
#required this.child,
// this.gradient,
this.isEnabled,
this.width,
this.height,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Color _statusColor;
if (isEnabled != null) {
// Show gradient color by making material widget transparent
if (isEnabled == true) {
_statusColor = Colors.transparent;
} else {
// Show grey color if isEnabled is false
_statusColor = Colors.grey;
}
} else {
// Show grey color if isEnabled is null
_statusColor = Colors.transparent;
}
return Container(
width: width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
colors: [
Color(0xFF3186E3),
Color(0xFF1D36C4),
],
begin: FractionalOffset.centerLeft,
end: FractionalOffset.centerRight,
),
),
child: Material(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(4)),
color: _statusColor,
child: InkWell(
borderRadius: BorderRadius.circular(32),
onTap: onPressed,
child: Padding(
padding: EdgeInsets.fromLTRB(24, 16, 24, 16),
child: Center(
child: child,
),
))),
);
}
}
Use ElevatedButton (Recommended as of Flutter 2.0)
#override
Widget build(BuildContext context) {
final radius = BorderRadius.circular(20);
return Scaffold(
body: Center(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.cyanAccent, Colors.red]),
borderRadius: radius,
),
child: ElevatedButton(
onPressed: () {},
child: Text('Elevated Button'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: radius),
),
),
),
),
);
}
I don't have much experience in flutter yet, and I curious of how can I achieved custom AppBar that can be animated.
I just want to apply a simple animation to the AppBar which will only change the height of the AppBar. As I understand that the AppBar must be a PreferredSizeWidget and I want to animate it to change the height, there are couple articles that I read but mostly it uses SilverAppBar.
Thanks.
class CustomAppBarRounded extends StatelessWidget implements PreferredSizeWidget{
final String _appBarTitle;
CustomAppBarRounded(this._appBarTitle);
#override
Widget build(BuildContext context) {
return new Container(
child: new LayoutBuilder(builder: (context, constraint) {
final width = constraint.maxWidth * 8;
return new ClipRect(
child: Stack(
children: <Widget>[
new OverflowBox(
maxHeight: double.infinity,
maxWidth: double.infinity,
child: new SizedBox(
width: width,
height: width,
child: new Padding(
padding: new EdgeInsets.only(
bottom: width / 2 - preferredSize.height / 3
),
child: new DecoratedBox(
decoration: new BoxDecoration(
color: Colors.indigo,
shape: BoxShape.circle,
boxShadow: [
new BoxShadow(color: Colors.black54, blurRadius: 10.0)
],
),
),
),
),
),
new Center(
child: new Text("${this._appBarTitle}",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [
Shadow(color: Colors.black54, blurRadius: 10.0)
]
),
)
),
],
)
);
}),
);
}
#override
Size get preferredSize => const Size.fromHeight(100.0);
}
I've figured out how to achieve what I wanted. So I returned the PreferredSizeWidget
class CustomRoundedAppBar extends StatelessWidget{
double height = 100;
final String title;
CustomRoundedAppBar(
this.height,
this.title);
#override
Widget build(BuildContext context) {
return PreferredSize(
preferredSize: Size(this.height, this.height),
child: AnimatedContainer(
duration: Duration(seconds: 1),
height: this.height,
child: new LayoutBuilder(builder: (context, constraint){
final width =constraint.maxWidth * 8;
return new ClipRect(
child: Stack(
children: <Widget>[
new OverflowBox(
maxHeight: double.infinity,
maxWidth: double.infinity,
child: new SizedBox(
width: width,
height: width,
child: new Padding(
padding: new EdgeInsets.only(
bottom: width / 2 - this.height / 3
),
child: new DecoratedBox(
decoration: new BoxDecoration(
color: Colors.indigo,
shape: BoxShape.circle,
boxShadow: [
new BoxShadow(color: Colors.black54, blurRadius: 10.0)
],
),
),
),
),
),
new Center(
child: new Text("${this.title}",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [
Shadow(color: Colors.black54, blurRadius: 10.0)
]
),
)
),
],
)
);
})
),
);
}
}
And on the Scaffold I have an action when button is pressed it will change the height, which must be on the setState()
onPressed: (){
setState(() {
this.height = 200;
this. _appBarTitle = "TEST";
});
},
Please how to create Side Radial Menu in flutter like a picture and make rolling when user tap in it
Any help would be appreciated.
This can be achieved by using a GestureDetector, Transform, trigonometry and some clipping with ClipRect.
Using GestureDetector, it is possible to see the drag distance that a user inputs. This can be used to determine how much to rotate the widgets.
Using Transform, it is possible to move widgets to specific locations.
Trigonometry is used to determine the position of the widgets to the centre of the circle.
Using ClipRect, it is possible to clip out the left side of the widgets.
It is possible to reverse scroll direction by instead taking the distance of the drag be turned negative.
Here is the code to make a rotating menu that uses a custom widget that I have recently created for answering this question(Add more Widgets to the Widget list if you want):
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body:CircularScrollView(//wrap this with align if you want it to be aligned to the right of the screen
[//add more widgets or remove as you'd like
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('a')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('b')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('c')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('d')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
GestureDetector(
onTap: (){},//insert function when icon is tapped
child: Container(
child: Center(child: Text('e')),
height: 20,
width: 20,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
),
),
],
radius: 100,
padding: 0,//add double the radius entered to clip out the right side
itemMaxHeight: 20,//effects clipping border height
itemMaxWidth: 20,//effects clipping border width
),
),
);
}
}
class CircularScrollView extends StatefulWidget {
final List<Widget> items;
final double radius;
final double itemMaxHeight;
final double itemMaxWidth;
final double padding;
final bool reverse;
CircularScrollView(this.items, {Key key, this.radius=10, this.itemMaxHeight=0, this.itemMaxWidth=0, this.padding=0, this.reverse=false}) : super(key: key);
#override
_CircularScrollViewState createState() => _CircularScrollViewState();
}
class _CircularScrollViewState extends State<CircularScrollView> {
double lastPosition;
List<Widget> transformItems= [];
double degreesRotated = 0;
#override
void initState() {
setState(() {
_calculateTransformItems();
});
super.initState();
}
void _calculateTransformItems(){
transformItems= [];
for(int i = 0; i<widget.items.length; i++){
double startAngle = (i/widget.items.length)*2*math.pi;
double currentAngle = degreesRotated+startAngle;
transformItems.add(
Transform(
transform: Matrix4.identity()..translate(
(widget.radius)*math.cos(currentAngle),
(widget.radius)*math.sin(currentAngle),
),
child: widget.items[i],
),
);
}
}
void _calculateScroll(DragUpdateDetails details){
if (lastPosition == null){
lastPosition = details.localPosition.dy;
return;
}
double distance = details.localPosition.dy - lastPosition;
double distanceWithReversal = widget.reverse?-distance:distance;
lastPosition =details.localPosition.dy;
degreesRotated += distanceWithReversal/(widget.radius);
_calculateTransformItems();
}
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: Container(
height: widget.radius*2+widget.itemMaxHeight,
width: widget.radius*2 + widget.itemMaxWidth,
child: GestureDetector(
onVerticalDragUpdate: (details)=>setState((){_calculateScroll(details);}),
onVerticalDragEnd: (details){lastPosition=null;},
child: Container(
height: double.infinity,
width: double.infinity,
color: Colors.transparent,
child: ClipRect(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(left: widget.padding),
child: Stack(
children: transformItems,
),
),
),
),
),
),
),
);
}
}
When using this code, do not modify the insides of the custom widget unless you know exactly what that section of the code does. When aligning the widget, please instead wrap the custom widget from the outside.
You can try using this package, circle_wheel_scroll, move around this widget inside Stack, place with Positioned with negative left position if necessary.
CircleListScrollView(
physics: CircleFixedExtentScrollPhysics(),
axis: Axis.horizontal,
itemExtent: 80,
children: List.generate(20, _buildItem),
radius: MediaQuery.of(context).size.width * 0.6,
),
or this listwheelscrollview
ListWheelScrollView(
itemExtent: 100,
// diameterRatio: 1.6,
// offAxisFraction: -0.4,
// squeeze: 0.8,
clipToSize: true,
children: <Widget>[
RaisedButton(onPressed:null ,
child: Text("Item 1",textAlign:TextAlign.start,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 2",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 3",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 4",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 5",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 6",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 7",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
RaisedButton(onPressed:null ,
child: Text("Item 8",textAlign:TextAlign.center,
style:TextStyle(color:Colors.black,fontWeight: FontWeight.bold,fontSize: 25),),) ,
],
),
I want to make this kind of design with these white circles as a raised button.
Try This!
I have added 5 circles you can add more. And instead of RaisedButton use InkResponse.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new ExampleWidget()));
}
class ExampleWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Widget bigCircle = new Container(
width: 300.0,
height: 300.0,
decoration: new BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
);
return new Material(
color: Colors.black,
child: new Center(
child: new Stack(
children: <Widget>[
bigCircle,
new Positioned(
child: new CircleButton(onTap: () => print("Cool"), iconData: Icons.favorite_border),
top: 10.0,
left: 130.0,
),
new Positioned(
child: new CircleButton(onTap: () => print("Cool"), iconData: Icons.timer),
top: 120.0,
left: 10.0,
),
new Positioned(
child: new CircleButton(onTap: () => print("Cool"), iconData: Icons.place),
top: 120.0,
right: 10.0,
),
new Positioned(
child: new CircleButton(onTap: () => print("Cool"), iconData: Icons.local_pizza),
top: 240.0,
left: 130.0,
),
new Positioned(
child: new CircleButton(onTap: () => print("Cool"), iconData: Icons.satellite),
top: 120.0,
left: 130.0,
),
],
),
),
);
}
}
class CircleButton extends StatelessWidget {
final GestureTapCallback onTap;
final IconData iconData;
const CircleButton({Key key, this.onTap, this.iconData}) : super(key: key);
#override
Widget build(BuildContext context) {
double size = 50.0;
return new InkResponse(
onTap: onTap,
child: new Container(
width: size,
height: size,
decoration: new BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: new Icon(
iconData,
color: Colors.black,
),
),
);
}
}
you can use decoration like this :
Container(
width: 60,
height: 60,
child: Icon(CustomIcons.option, size: 20,),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFFe0f2f1)),
)
Now you have circle shape and Icon on it.
More efficient way
I recommend drawing a circle with CustomPainter. It's very easy and way more efficient than creating a bunch of widgets/masks:
/// Draws a circle if placed into a square widget.
/// https://stackoverflow.com/a/61246388/1321917
class CirclePainter extends CustomPainter {
final _paint = Paint()
..color = Colors.red
..strokeWidth = 2
// Use [PaintingStyle.fill] if you want the circle to be filled.
..style = PaintingStyle.stroke;
#override
void paint(Canvas canvas, Size size) {
canvas.drawOval(
Rect.fromLTWH(0, 0, size.width, size.height),
_paint,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Usage:
Widget _buildCircle(BuildContext context) {
return CustomPaint(
size: Size(20, 20),
painter: CirclePainter(),
);
}
I would use a https://docs.flutter.io/flutter/widgets/Stack-class.html to be able to freely position widgets.
To create circles
new BoxDecoration(
color: effectiveBackgroundColor,
image: backgroundImage != null
? new DecorationImage(image: backgroundImage, fit: BoxFit.cover)
: null,
shape: BoxShape.circle,
),
and https://docs.flutter.io/flutter/widgets/Transform/Transform.rotate.html to position the white dots.
I want to create a widget like a CircleAvatar that clips its child when it overflows (CircleAvatar only clip the image, not its child). Can I force a BoxDecoration to clip its child (like overflow: hidden in css)?
Consider I have these:
new Container(
height: 50.0,
alignment: Alignment.bottomCenter,
decoration: new BoxDecoration(
color: Colors.blue,
border: new Border.all(),
borderRadius: new BorderRadius.circular(25.0),
),
child: new Container(
color: Colors.orange,
height: 20.0,
),
)
I want orange box to be contained in blue circle.
There is a ClipOval class that can be used like this:
class ClipExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.blueAccent,
body: new Center(
child: new CircleAvatar(
backgroundColor: Colors.amberAccent,
child: new ClipOval(
clipper:new MyClipper(),
child: new Container(
color: Colors.red,
height: 10.0,
),
),
),
),
);
}
}
class MyClipper extends CustomClipper<Rect>{
#override
Rect getClip(Size size) {
return new Rect.fromCircle(center: new Offset(0.0,0.0),
radius: 50.0
);
}
#override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}