Making a resizable dropdown widget with touch in flutter - flutter

I want to create a drop down menu like the image below, Which is opened by touching and dragging and closing by touching the outside.
before dragging
after dragging
Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false
),
body: Stack(
children: <Widget>[
Container(
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(bottom: Radius.circular(20))
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 40),
child: Divider(
color: Colors.blueGrey[500],
height: 10,
indent: 5,
),
),
Icon(FontAwesomeIcons.angleDoubleDown,size: 15,color: Colors.blueGrey[500],)
],
),
)
],
),
),
Center(child: Text('List View'),)
],
)
)
I want to change the height, but I encounter overflow error!
What is the best way to make this widget?
Can I do this within the AppBar?

You can do this in some ways, but one that came up in mind immediately was to use a CustomPaint widget with your own CustomPainter at the top of a Stack so you can actually keep your other widgets below the scrolled bar.
I've tried to replicate what you've shown on the images but feel free to tweak it to your needs.
const kMinScrollBarHeight = 20.0;
class MyScreen extends StatefulWidget {
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
double _scrollBarOffset = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromRGBO(13, 23, 35, 1.0),
appBar: AppBar(
backgroundColor: const Color.fromRGBO(255, 72, 18, 1.0),
),
body: Stack(children: <Widget>[
GestureDetector(
onVerticalDragUpdate: (tapDetails) => setState(() => _scrollBarOffset = tapDetails.globalPosition.dy),
child: Stack(
children: <Widget>[
Center(
child: Text(
'My screen widgets',
style: TextStyle(color: Colors.white),
),
),
Stack(
children: <Widget>[
Positioned(
bottom: MediaQuery.of(context).size.height -
max(_scrollBarOffset,
MediaQuery.of(context).padding.top + kToolbarHeight + kMinScrollBarHeight),
child: CustomPaint(
painter: MyDraggable(),
child: Container(
height: MediaQuery.of(context).size.height,
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlutterLogo(
size: 100.0,
),
Text('Flutter is awesome'),
],
),
),
),
),
],
),
],
),
),
]),
);
}
}
class MyDraggable extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()..color = Colors.white;
final Radius cornerRadius = Radius.circular(20.0);
final double lineMargin = 30.0;
// Draw slider
canvas.drawRRect(
RRect.fromLTRBAndCorners(0.0, 0.0, size.width, size.height,
bottomLeft: cornerRadius, bottomRight: cornerRadius),
paint);
paint.color = Colors.black.withAlpha(64);
paint.strokeWidth = 1.5;
// Draw triangle
canvas.drawPoints(
PointMode.polygon,
[
Offset((size.width / 2) - 5.0, size.height - 10.0),
Offset((size.width / 2) + 5.0, size.height - 10.0),
Offset((size.width / 2), size.height - 5.0),
Offset((size.width / 2) - 5.0, size.height - 10.0),
],
paint);
// Draw line
canvas.drawLine(Offset(lineMargin, size.height - kMinScrollBarHeight),
Offset(size.width - lineMargin, size.height - kMinScrollBarHeight), paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

Related

How can I add some widgets to the position of siblings in the row at Flutter

I'm trying to make a discrete progress indicator similar to percent indicator but in a discrete manner.
And I wanna add some guide widget or arrow at the position of the current progress position something like images. (It is not actually implemented, it is a view of widget inspector, but I wanna get the result of that image, top or bottom of the row.)
But, the problem is, how can I get the position of the child widget of the row and add some stacked widget to that?
class LinearDiscreteIndicator extends StatelessWidget {
final int numTotal;
final int numCurrent;
final double? width;
final double lineHeight;
final double margin;
final Color fillColor;
final Color emptyColor;
final Radius barRadious;
final MainAxisAlignment alignment;
final Widget? progressWidget;
const LinearDiscreteIndicator({
Key? key,
required this.numTotal,
required this.numCurrent,
this.width,
required this.lineHeight,
this.margin = 2,
this.fillColor = Colors.blue,
this.emptyColor = Colors.grey,
this.barRadious = const Radius.circular(2),
this.alignment = MainAxisAlignment.start,
this.progressWidget,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: [
SizedBox(
height: lineHeight,
width: width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: alignment,
children: List.generate(numTotal, (index) => index)
.map(
(e) => Expanded(
child: Padding(
padding: EdgeInsets.only(
right: e == (numTotal - 1) ? 0 : margin,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(barRadious),
color: e > numCurrent ? emptyColor : fillColor,
),
),
),
),
)
.toList(),
),
),
],
);
}
}
This is my current widget class code, but I'm not sure how can I enable a tooltip-like widget at the position of the current progress.
var numCurrent = 10;
var numTotal = 30;
var itemWidth = (MediaQuery.of(context).size.width / numTotal);
double containerWidth = 80;
var positionLeft = ((itemWidth) * (numCurrent + 1)) - (containerWidth / 2);
return Scaffold(
appBar: AppBar(),
backgroundColor: theme.colorScheme.background,
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: 50,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
left: positionLeft,
top: 10,
child: Container(
child: Stack(
children: const [
Align(alignment: Alignment.topCenter, child: Text("Container")),
Align(
alignment: Alignment.bottomCenter,
child: Icon(
Icons.arrow_drop_down_sharp,
size: 24,
)),
],
),
width: containerWidth,
height: 30,
color: Colors.red,
),
),
Positioned(
bottom: 0,
child: LinearDiscreteIndicator(
numCurrent: numCurrent,
numTotal: numTotal,
lineHeight: 4,
width: MediaQuery.of(context).size.width,
),
),
],
),
),
],
),
),
);

How can i make a shape like this in Flutter?

I was wondering how can I achieve something like this, this kind of shape?
Here:
class Draw extends StatelessWidget {
const Draw({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 300,
height: 170,
child: Stack(
clipBehavior: Clip.none,
children: [
Center(
child: ClipPath(
clipper: MyClipper(),
child: Container(
width: 300,
height: 170,
color: Colors.black,
),
),
),
Positioned(
left: -20,
child: SizedBox(
height: 170,
child: Center(
child: Material(
elevation: 3,
color: Colors.black,
shadowColor: Colors.white,
borderRadius: BorderRadius.circular(10),
child: const SizedBox(width: 80, height: 80),
),
),
),
)
],
),
),
),
);
}
}
class MyClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final p = Path();
double facotr = 30;
p.moveTo(10, 0);
p.lineTo(10, size.height - 20);
p.quadraticBezierTo(10, size.height, 20, size.height);
p.lineTo(size.width - facotr, size.height - facotr);
p.lineTo(size.width, size.height / 2);
p.lineTo(size.width - facotr, facotr);
p.lineTo(20, 0);
p.quadraticBezierTo(10, 0, 10, 10);
return p;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
There is the Paint widget, but also someone made a tool for that wich makes it convienent.
Shape Maker

Flutter Custom Card Widget How to draw half circular line

There is a card widget. As seen in the picture, I want lines on this card widget. But I don't know how to do this. I would be glad if you could help me with this.
Problem Solved!
This Card Widget Code
Container(
height: 190.sp, //
width: 360.sp,
padding: EdgeInsets.symmetric(horizontal: 5.sp),
child: Card(
color: Colors.orange,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
child: Stack(
children: [
Align(
alignment: Alignment.bottomRight,
child: Transform.rotate(
angle: -pi / 2,
child: CustomPaint(
painter: MyPainter(),
child: SizedBox(width: 65, height: 65),
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Transform.rotate(
angle: -pi / 2,
child: Transform.translate(
offset: Offset(25, 0),
child: CustomPaint(
painter: MyPainter(),
child: SizedBox(width: 30, height: 30),
),
),
),
),
],
),
));
THIS CUSTOM PAINTER
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final redCircle = Paint()
..color = Colors.white
..style = PaintingStyle.stroke;
final arcRect = Rect.fromCircle(
center: size.bottomCenter(Offset.zero), radius: size.shortestSide);
canvas.drawArc(arcRect, 0, -pi, false, redCircle);
}
#override
bool shouldRepaint(MyPainter oldDelegate) => false;
}

Asking how to achieve the design problem in flutter

Hello Guys I would like to ask is it possible to achieve this kind of design using code? I really got stuck, here is design I would like to achieve
but the real problem is I only achieve this
this is my sample code:
in my main:
return Scaffold(
backgroundColor: Color(0xff012a60),
body: Stack(children: <Widget>[
/*_AnimatedCircle(
outerController: outerController,
innerController: innerController,
),*/
_OuterCircle(circleColor: Colors.blueGrey[900].withOpacity(.8)),
Column(children: <Widget>[
const Spacer(flex: 50),
Expanded(flex: 30, child: Container(color: Colors.white)),
const Spacer(flex: 20),
]),
Row(children: <Widget>[
const Spacer(flex: 80),
Transform.scale(
scale: 2.5,
child: Container(
width: MediaQuery.of(context).size.width * 0.2,
decoration: const BoxDecoration(
color: Colors.red, shape: BoxShape.circle)),
),
]),
// _OuterCircle(circleColor: Colors.blueAccent.withOpacity(.25)),
Row(children: <Widget>[
const Spacer(flex: 80),
Column(children: [
Spacer(flex: 60),
Transform.scale(
scale: 2.5,
child: Container(
width: MediaQuery.of(context).size.width * 0.2,
height: MediaQuery.of(context).size.width * 0.05,
decoration: const BoxDecoration(color: Colors.green)),
),
Spacer(flex: 24),
]),
]),
]),
);
and this is my other class
class _OuterCircle extends StatelessWidget {
const _OuterCircle({Key key, this.circleColor}) : super(key: key);
final Color circleColor;
#override
Widget build(BuildContext context) {
return Transform.scale(
scale: 2,
child: Container(
width: MediaQuery.of(context).size.width * 0.47,
decoration: BoxDecoration(shape: BoxShape.circle, color: circleColor),
));
}
}
Please help I really stuck how to achieve this kind of design in dart code
I think it is easier to break down the geometry problem from the largest area to small area.
The key point of achieve this is using Clip (ClipOval, ClipRect...)
The upper part can be draw by using right circle to clip left circle
The lower part can be draw by using right circle to clip inner rectangle
Scale the stack area
Draw large left circle (area 1)
Draw middle white rectangle (area 2)
Draw right circle with rectangle inside (area 3)
Draw the middle intersection part (area 4)
Widgets in used: ClipOval, FractionallySizedBox, AspectRatio,
The left Big Circle
class BigCircle extends StatelessWidget {
const BigCircle({this.color,Key key}):super(key: key);
final Color color;
#override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1,
child: Container(
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
);
}
}
The custom oval clipper (clip right white circle area)
class CustomClipOval extends CustomClipper<Rect> {
#override
Rect getClip(Size size) {
final width = size.width * 0.42;
return Rect.fromLTRB(
size.width - width,
(size.height - width) * 0.5,
size.width,
(size.height + width) * 0.5,
);
}
#override
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
return false;
}
}
Finally the stack
return Scaffold(
backgroundColor: const Color(0xff004471),
body: Transform.scale(
scale: 1.19,
child: Stack(children: <Widget>[
// Area 1
const Align(
alignment: Alignment.centerLeft,
child: BigCircle(
color: Color(0xff022c66),
),
),
// Area 2
Align(
alignment: const Alignment(0, 0.4),
child: FractionallySizedBox(
heightFactor: 0.23,
child: Container(
color: Colors.white,
),
),
),
// Area 3
ClipOval(
clipper: CustomClipOval(),
child: Container(
alignment: const Alignment(0, 0.46),
color: Colors.white,
child: FractionallySizedBox(
heightFactor: 0.14,
child: Container(
color: const Color(0xff335f86),
),
),
),
),
// Area 4
ClipOval(
clipper: CustomClipOval(),
child: const Align(
alignment: Alignment.centerLeft,
child: BigCircle(color: Color(0xff647294),),
),
),
]),
),
);

Flutter - Overlay card widget on a container

In flutter, is it possible to place a part of a card on another container? In CSS, we would set margin-top to a negative value or use translate property. In flutter as we cannot set negative values to margin-top, is there an alternative to that?
Yes, you can acheive it with a Stack widget. You can stack a card over the background and provide a top or bottom padding.
A simple example would look like:
class StackDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
// The containers in the background
new Column(
children: <Widget>[
new Container(
height: MediaQuery.of(context).size.height * .65,
color: Colors.blue,
),
new Container(
height: MediaQuery.of(context).size.height * .35,
color: Colors.white,
)
],
),
// The card widget with top padding,
// incase if you wanted bottom padding to work,
// set the `alignment` of container to Alignment.bottomCenter
new Container(
alignment: Alignment.topCenter,
padding: new EdgeInsets.only(
top: MediaQuery.of(context).size.height * .58,
right: 20.0,
left: 20.0),
child: new Container(
height: 200.0,
width: MediaQuery.of(context).size.width,
child: new Card(
color: Colors.white,
elevation: 4.0,
),
),
)
],
);
}
}
The output of the above code would look something like:
Hope this helps!
Screenshot:
Instead of hardcoding Positioned or Container, you should use Align.
Code:
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
body: Stack(
children: [
Column(
children: [
Expanded(flex: 2, child: Container(color: Colors.indigo)),
Expanded(child: Container(color: Colors.white)),
],
),
Align(
alignment: Alignment(0, 0.5),
child: Container(
width: size.width * 0.9,
height: size.height * 0.4,
child: Card(
elevation: 12,
child: Center(child: Text('CARD', style: Theme.of(context).textTheme.headline2)),
),
),
),
],
),
);
}
Here is running example with overlay:
class _MyHomePageState extends State<MyHomePage> {
double _width = 0.0;
double _height = 0.0;
#override
Widget build(BuildContext context) {
_width = MediaQuery.of(context).size.width;
_height = MediaQuery.of(context).size.height;
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: <Widget>[
// The containers in the background and scrollable
getScrollableBody(),
// This container will work as Overlay
getOverlayWidget()
],
),
);
}
Widget getOverlayWidget() {
return new Container(
alignment: Alignment.bottomCenter,
child: new Container(
height: 100.0,
width: _width,
color: Colors.cyan.withOpacity(0.4),
),
);
}
Widget getScrollableBody() {
return SingleChildScrollView(
child: new Column(
children: <Widget>[
new Container(
height: _height * .65,
color: Colors.yellow,
),
new Container(
height: _height * .65,
color: Colors.brown,
),
new Container(
margin: EdgeInsets.only(bottom: 100.0),
height: _height * .65,
color: Colors.orange,
),
],
),
);
}
}
Here is Result of code:
Scrollable Body under customised Bottom Bar