Skip to content

Commit 5e52dc6

Browse files
Custom Drop down
1 parent 64ac345 commit 5e52dc6

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed

custom_dropdown.dart

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
import 'package:flutter/material.dart';
2+
3+
class CustomDropdown extends StatefulWidget {
4+
final String text;
5+
6+
const CustomDropdown({Key key, @required this.text}) : super(key: key);
7+
8+
@override
9+
_CustomDropdownState createState() => _CustomDropdownState();
10+
}
11+
12+
class _CustomDropdownState extends State<CustomDropdown> {
13+
GlobalKey actionKey;
14+
double height, width, xPosition, yPosition;
15+
bool isDropdownOpened = false;
16+
OverlayEntry floatingDropdown;
17+
18+
@override
19+
void initState() {
20+
actionKey = LabeledGlobalKey(widget.text);
21+
super.initState();
22+
}
23+
24+
void findDropdownData() {
25+
RenderBox renderBox = actionKey.currentContext.findRenderObject();
26+
height = renderBox.size.height;
27+
width = renderBox.size.width;
28+
Offset offset = renderBox.localToGlobal(Offset.zero);
29+
xPosition = offset.dx;
30+
yPosition = offset.dy;
31+
print(height);
32+
print(width);
33+
print(xPosition);
34+
print(yPosition);
35+
}
36+
37+
OverlayEntry _createFloatingDropdown() {
38+
return OverlayEntry(builder: (context) {
39+
return Positioned(
40+
left: xPosition,
41+
width: width,
42+
top: yPosition + height,
43+
height: 4 * height + 40,
44+
child: DropDown(
45+
itemHeight: height,
46+
),
47+
);
48+
});
49+
}
50+
51+
@override
52+
Widget build(BuildContext context) {
53+
return GestureDetector(
54+
key: actionKey,
55+
onTap: () {
56+
setState(() {
57+
if (isDropdownOpened) {
58+
floatingDropdown.remove();
59+
} else {
60+
findDropdownData();
61+
floatingDropdown = _createFloatingDropdown();
62+
Overlay.of(context).insert(floatingDropdown);
63+
}
64+
65+
isDropdownOpened = !isDropdownOpened;
66+
});
67+
},
68+
child: Container(
69+
decoration: BoxDecoration(
70+
borderRadius: BorderRadius.circular(8),
71+
color: Colors.red.shade600,
72+
),
73+
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
74+
child: Row(
75+
children: <Widget>[
76+
Text(
77+
widget.text.toUpperCase(),
78+
style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600),
79+
),
80+
Spacer(),
81+
Icon(
82+
Icons.arrow_drop_down,
83+
color: Colors.white,
84+
),
85+
],
86+
),
87+
),
88+
);
89+
}
90+
}
91+
92+
class DropDown extends StatelessWidget {
93+
final double itemHeight;
94+
95+
const DropDown({Key key, this.itemHeight}) : super(key: key);
96+
97+
@override
98+
Widget build(BuildContext context) {
99+
return Column(
100+
children: <Widget>[
101+
SizedBox(
102+
height: 5,
103+
),
104+
Align(
105+
alignment: Alignment(-0.85, 0),
106+
child: ClipPath(
107+
clipper: ArrowClipper(),
108+
child: Container(
109+
height: 20,
110+
width: 30,
111+
decoration: BoxDecoration(
112+
color: Colors.red.shade600,
113+
),
114+
),
115+
),
116+
),
117+
Material(
118+
elevation: 20,
119+
shape: ArrowShape(),
120+
child: Container(
121+
height: 4 * itemHeight,
122+
decoration: BoxDecoration(
123+
borderRadius: BorderRadius.circular(8),
124+
),
125+
child: Column(
126+
children: <Widget>[
127+
DropDownItem.first(
128+
text: "Add new",
129+
iconData: Icons.add_circle_outline,
130+
isSelected: false,
131+
),
132+
DropDownItem(
133+
text: "View Profile",
134+
iconData: Icons.person_outline,
135+
isSelected: false,
136+
),
137+
DropDownItem(
138+
text: "Settings",
139+
iconData: Icons.settings,
140+
isSelected: false,
141+
),
142+
DropDownItem.last(
143+
text: "Logout",
144+
iconData: Icons.exit_to_app,
145+
isSelected: true,
146+
),
147+
],
148+
),
149+
),
150+
),
151+
],
152+
);
153+
}
154+
}
155+
156+
class DropDownItem extends StatelessWidget {
157+
final String text;
158+
final IconData iconData;
159+
final bool isSelected;
160+
final bool isFirstItem;
161+
final bool isLastItem;
162+
163+
const DropDownItem({Key key, this.text, this.iconData, this.isSelected = false, this.isFirstItem = false, this.isLastItem = false})
164+
: super(key: key);
165+
166+
factory DropDownItem.first({String text, IconData iconData, bool isSelected}) {
167+
return DropDownItem(
168+
text: text,
169+
iconData: iconData,
170+
isSelected: isSelected,
171+
isFirstItem: true,
172+
);
173+
}
174+
175+
factory DropDownItem.last({String text, IconData iconData, bool isSelected}) {
176+
return DropDownItem(
177+
text: text,
178+
iconData: iconData,
179+
isSelected: isSelected,
180+
isLastItem: true,
181+
);
182+
}
183+
184+
@override
185+
Widget build(BuildContext context) {
186+
return Container(
187+
decoration: BoxDecoration(
188+
borderRadius: BorderRadius.vertical(
189+
top: isFirstItem ? Radius.circular(8) : Radius.zero,
190+
bottom: isLastItem ? Radius.circular(8) : Radius.zero,
191+
),
192+
color: isSelected ? Colors.red.shade900 : Colors.red.shade600,
193+
),
194+
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
195+
child: Row(
196+
children: <Widget>[
197+
Text(
198+
text.toUpperCase(),
199+
style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600),
200+
),
201+
Spacer(),
202+
Icon(
203+
iconData,
204+
color: Colors.white,
205+
),
206+
],
207+
),
208+
);
209+
}
210+
}
211+
212+
class ArrowClipper extends CustomClipper<Path> {
213+
@override
214+
Path getClip(Size size) {
215+
Path path = Path();
216+
217+
path.moveTo(0, size.height);
218+
path.lineTo(size.width / 2, 0);
219+
path.lineTo(size.width, size.height);
220+
221+
return path;
222+
}
223+
224+
@override
225+
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
226+
}
227+
228+
class ArrowShape extends ShapeBorder {
229+
@override
230+
// TODO: implement dimensions
231+
EdgeInsetsGeometry get dimensions => throw UnimplementedError();
232+
233+
@override
234+
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
235+
// TODO: implement getInnerPath
236+
throw UnimplementedError();
237+
}
238+
239+
@override
240+
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
241+
// TODO: implement getOuterPath
242+
return getClip(rect.size);
243+
}
244+
245+
@override
246+
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
247+
// TODO: implement paint
248+
}
249+
250+
@override
251+
ShapeBorder scale(double t) {
252+
// TODO: implement scale
253+
throw UnimplementedError();
254+
}
255+
256+
Path getClip(Size size) {
257+
Path path = Path();
258+
259+
path.moveTo(0, size.height);
260+
path.lineTo(size.width / 2, 0);
261+
path.lineTo(size.width, size.height);
262+
263+
return path;
264+
}
265+
}

0 commit comments

Comments
 (0)