'How to update UI on the same page according to DropDownItem selected from Custom DropDown?
I have created a custom dropdown on a page which have a dropdown button, an overlay entry containing dropdown items. Now I want to change the other part of page excluding drop down button to change according to dropdown item clicked. e.g. If I click on Dashboard Item then overlay entry should be closed and Dashboard() widget should be displayed below dropdown button, if I click Orders, Orders() widget should be displayed.
Following is my code:
import 'package:flutter/material.dart';
import 'package:suture_house/User/UI/Screens/AccountOptions/account_details.dart';
import 'package:suture_house/User/UI/Screens/AccountOptions/addresses.dart';
import 'package:suture_house/User/UI/Screens/AccountOptions/dashboard.dart';
import 'package:suture_house/User/UI/Screens/AccountOptions/downloads.dart';
import 'AccountOptions/orders.dart';
class AccountPage extends StatefulWidget {
final String text;
const AccountPage({
Key? key,
required this.text,
}) : super(key: key);
@override
_AccountPageState createState() => _AccountPageState();
}
class _AccountPageState extends State<AccountPage> {
GlobalKey? _actionKey;
double height = 0.0, width = 0.0, xPosition = 0.0, yPosition = 0.0;
bool isDropdownOpened = false;
OverlayEntry? floatingDropDown;
int _selectedIndex = 0;
//List of pages to display as one of the children of ListView according to selected dropdown item
final options = [
Dashboard(),
Orders(),
Downloads(),
Addresses(),
AccountDetails()
];
// void _onItemTapped(int index) {
// setState(() {
// _selectedIndex = index;
// });
// }
@override
void initState() {
// TODO: implement initState
_actionKey = LabeledGlobalKey(widget.text);
super.initState();
}
@override
void dispose() {
// TODO: implement dispose
floatingDropDown!.remove();
super.dispose();
}
//To get the position of Custom DropDown Button
void findDropDownData() {
RenderBox renderBox =
_actionKey!.currentContext!.findRenderObject() as RenderBox;
height = renderBox.size.height;
width = renderBox.size.width;
Offset offset = renderBox.localToGlobal(Offset.zero);
xPosition = offset.dx;
yPosition = offset.dy;
}
//Overlay Entry of DropDown Items
OverlayEntry _createFloatingDropDown() {
return OverlayEntry(builder: (context) {
return Positioned(
left: xPosition,
width: width,
top: yPosition + height,
height: 6 * height + 60,
child: DropDown(
itemHeight: height,
),
);
});
}
@override
Widget build(BuildContext context) {
return ListView(
children: [
//Custom Drop down button
Padding(
padding: EdgeInsets.all(20),
child: GestureDetector(
key: _actionKey,
onTap: () {
setState(() {
if (isDropdownOpened) {
floatingDropDown!.remove();
} else {
findDropDownData();
floatingDropDown = _createFloatingDropDown();
Overlay.of(context)!.insert(floatingDropDown!);
}
isDropdownOpened = !isDropdownOpened;
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.indigo),
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: <Widget>[
Text(
widget.text,
style: TextStyle(color: Colors.white, fontSize: 20),
),
Spacer(),
Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
],
),
),
),
),
//Here I want to display different widgets according to DropDown Item clicked
GestureDetector(
onTap: () {
if (isDropdownOpened) {
floatingDropDown!.remove();
isDropdownOpened = false;
}
},
child: Container(
height: 1000,
)
)
],
);
}
}
//This DropDown will be displayed on Overlay Entry
class DropDown extends StatelessWidget {
final double itemHeight;
//final void Function(int) onOptionTap;
const DropDown({Key? key, required this.itemHeight,}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(
height: 5,
),
Align(
alignment: Alignment(-0.85, 0),
child: ClipPath(
clipper: ArrowClipper(),
child: Container(
height: 20,
width: 30,
decoration: BoxDecoration(
color: Colors.indigo,
),
),
),
),
Material(
elevation: 20,
shape: ArrowShape(),
child: Container(
height: 6 * itemHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
DropDownItem.first(
text: 'Dashboard',
iconData: Icons.speed_outlined,
index: 0,
isSelected: true),
DropDownItem(
text: 'Orders',
iconData: Icons.shopping_bag_outlined,
index: 1,
isSelected: false),
DropDownItem(
text: 'Downloads',
iconData: Icons.insert_drive_file_outlined,
index: 2,
isSelected: false),
DropDownItem(
text: 'Addresses',
iconData: Icons.edit_location,
index: 3,
isSelected: false),
DropDownItem(
text: 'Account Details',
iconData: Icons.person_outline,
index: 4,
isSelected: false),
DropDownItem.last(
text: 'Logout',
iconData: Icons.exit_to_app_outlined,
index: 5,
isSelected: false),
],
),
),
)
],
);
}
}
//DropDown Items
class DropDownItem extends StatelessWidget {
final String text;
final IconData iconData;
bool isSelected;
final bool isFirstItem;
final bool isLastItem;
final int index;
DropDownItem({
Key? key,
required this.text,
required this.iconData,
required this.isSelected,
this.isFirstItem = false,
this.isLastItem = false,
required this.index,
}) : super(key: key);
factory DropDownItem.first(
{required String text,
required IconData iconData,
required bool isSelected,
required int index}) {
return DropDownItem(
text: text,
iconData: iconData,
isFirstItem: true,
isSelected: isSelected,
index: index,
);
}
factory DropDownItem.last(
{required String text,
required IconData iconData,
required bool isSelected,
required int index}) {
return DropDownItem(
text: text,
iconData: iconData,
isLastItem: true,
isSelected: isSelected,
index: index,
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(
top: isFirstItem ? Radius.circular(8) : Radius.zero,
bottom: isLastItem ? Radius.circular(8) : Radius.zero),
color: isSelected ? Colors.orangeAccent : Colors.indigo),
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
children: <Widget>[
Text(
text,
style: TextStyle(color: Colors.white, fontSize: 20),
),
Spacer(),
Icon(
iconData,
color: Colors.white,
),
],
),
),
);
}
}
class ArrowClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
Path path = Path();
path.moveTo(0, size.height);
path.lineTo(size.width / 2, 0);
path.lineTo(size.width, size.height);
return path;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
class ArrowShape extends ShapeBorder {
@override
// TODO: implement dimensions
EdgeInsetsGeometry get dimensions => throw UnimplementedError();
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
// TODO: implement getInnerPath
throw UnimplementedError();
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
// TODO: implement getOuterPath
return getClip(rect.size);
//throw UnimplementedError();
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
// TODO: implement paint
}
@override
ShapeBorder scale(double t) {
// TODO: implement scale
throw UnimplementedError();
}
Path getClip(Size size) {
Path path = Path();
path.moveTo(0, size.height);
path.lineTo(size.width / 2, 0);
path.lineTo(size.width, size.height);
return path;
}
}
This is the UI of application. I want to change the UI below "View Options" according to item selected.
Solution 1:[1]
use option[_selectedIndex] wherever you want to show the widget.
Solution 2:[2]
You can use conditions to update your UI based on the option chosen, so when you make the change you're doing using options[_selectedIndex] then you have the option that you want to display and then you can do something like this
options[_selectedIndex]==dashboard? dashboardWidget():
options[_selectedIndex]==settings? settingsWidget():defaultWidget(),
and I recommend You using Enums that'll help reduce your code and make it more flexible for upcoming changes or maintainance of the code
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Lakshydeep Vikram Sah |
| Solution 2 | Mohamed El-Marakby |

