Flutter draw Opens without transition with list view builder - flutter

I want my drawer contain list with expandable items, a list view inside list view.
it is working but the transition stopped, its just appear suddenly.
There aren't a lot items, 30 expandable rows and in conclude 100 so i think it shouldn't effect but it is.
I don't want custom drawer because the regular transition is good enough for me.
When i change the item Count to 2 its working fine so the amount is the problem.
class AppDrawer extends StatelessWidget {
const AppDrawer({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<LeagueCountry> _countries =
Provider.of<LeaguesProvider>(context).getByCountries();
return Drawer(
child: Container(
// child: DrawerList(),
child: ListView.builder(
itemCount: _countries.length,
itemBuilder: (BuildContext context, int index) {
return ExpandableListView(
title: "Title $index",
country: _countries[index],
);
},
),
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
final LeagueCountry country;
const ExpandableListView({
Key key,
this.title,
this.country,
}) : super(key: key);
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
double rowHeight = 50.0;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue,
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
alignment: Alignment.center,
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: Center(
child: Icon(
expandFlag
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.country.name,
style: new TextStyle(
fontWeight: FontWeight.bold, color: Colors.white),
)
],
),
),
new ExpandableContainer(
expandedHeight: rowHeight * widget.country.leagues.length,
expanded: expandFlag,
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
height: rowHeight,
decoration: new BoxDecoration(
border: new Border.all(width: 1.0, color: Colors.grey),
color: Colors.black),
child: new ListTile(
title: new Text(
"${widget.country.leagues[index].name}",
style: new TextStyle(
fontWeight: FontWeight.bold, color: Colors.white),
),
leading: new Icon(
Icons.local_pizza,
color: Colors.white,
),
),
);
},
itemCount: widget.country.leagues.length,
))
],
),
);
}
}
Expandable container :
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double collapsedHeight;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.collapsedHeight = 0.0,
this.expandedHeight = 300.0,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : collapsedHeight,
child: new Container(
child: child,
decoration: new BoxDecoration(
border: new Border.all(width: 1.0, color: Colors.blue)),
),
);
}
}
league country model:
class LeagueCountry {
String name;
List<League> leagues;
LeagueCountry({
this.name,
this.leagues,
});
}
League model:
class League {
String id;
int externalId;
String name;
String countryName;
int priority;
bool showInHomepage;
League({
this.id,
this.externalId,
this.name,
this.countryName,
this.priority,
this.showInHomepage,
});

Related

My search bar is not filtering any data from listview. Can anyone tell me what's wrong with my onItemChanged() function and how to solve the issue?

The app only shows listview but doesn't filter any data.
If I type anything in searchbar the list view remains same
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => new _HomeState();
}
class _HomeState extends State<Home> {
Future<List> getData() async {
final response =
await http.get(Uri.parse("http://192.168.0.166/api/conn2.php"));
return json.decode(response.body);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("MY APP"),
),
body: Center(child: ListSearch()));
}
}
class ListSearch extends StatefulWidget {
ListSearchState createState() => ListSearchState();
}
class ListSearchState extends State<ListSearch> {
TextEditingController _textController = TextEditingController();
List newList = [];
onItemChanged(String value) {
setState(() {
newList = MyStatelessWidget(
list: [],
)
.list
.where((string) => string.toLowerCase().contains(value.toLowerCase()))
.toList(); //copying api data into newList
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Search here....",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
prefixIcon: Icon(Icons.search),
suffixIcon: IconButton(
onPressed: _textController.clear,
icon: Icon(Icons.clear),
),
),
onChanged: onItemChanged, //onItemChanged() called here
),
),
Expanded(
child: new FutureBuilder<List>(
future: _HomeState().getData(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? new MyStatelessWidget(
list: snapshot.data ?? [],
)
: new Center(
child: CircularProgressIndicator(),
);
},
),
)
],
),
);
}
}
The database contains 'Articles','Features', 'Features2','Description' table
class _ArticleDescription extends StatelessWidget {
const _ArticleDescription({
Key? key,
required this.articles,
required this.features,
required this.features2,
//required this.description,
}) : super(key: key);
final String articles;
final String features;
final String features2;
// final String description;
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
articles,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
const Padding(padding: EdgeInsets.only(bottom: 2.0)),
Text(
features,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
features2,
style: const TextStyle(
fontSize: 12.0,
color: Colors.black87,
),
),
/*Text(
description,
style: const TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),*/
],
),
),
],
);
}
}
Custom list view
class CustomListItemTwo extends StatelessWidget {
const CustomListItemTwo({
Key? key,
required this.thumbnail,
required this.articles,
required this.features,
required this.features2,
// required this.description,
}) : super(key: key);
final Widget thumbnail;
final String articles;
final String features;
final String features2;
//final String description;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: SizedBox(
height: 80,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 0.8,
child: thumbnail,
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(20.0, 0.0, 2.0,0.0),
child: _ArticleDescription(
articles: articles,
features: features,
features2: features2,
// description: description,
),
),
)
],
),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
final List list;
//final List<String> list;
const MyStatelessWidget({ required this.list});
//const MyStatelessWidget({Key? key, required this.list}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list == null ? 0 : list.length,
itemBuilder: (context, i) {
return Container(
padding: const EdgeInsets.all(10.0),
child: new GestureDetector(
onTap: ()=>Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context)=> new Detail(list:list , index: i,)
)
),
child:Column(
children:<Widget> [
CustomListItemTwo(
thumbnail: Container(
decoration: const BoxDecoration(color: Colors.blue),
),
articles: (list[i]['Articles']),
features: (list[i]['Features']),
features2: (list[i]['Features2']),
//description: (list[i]['Description']),
),
],
),
),
);
}
);
}
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list == null ? 0 : list.length,
....
To
#override
Widget build(BuildContext context) {
var activeList = searchlist ;
//create isSearchBar bool var. and change
//true if search is Active
//false if search is inActive
isSearchBar
? activeList = searchlist
: activeList = list;
return ListView.builder(
itemCount: activeList == null ? 0 : activeList.length,
....
articles: (activeList[i]['Articles']),
....

Making reusable flutter widget

I've been trying to make my custom widget reusable but kept on hitting dead ends... I want to be able to use change the colors of the icons, text within the card, the first card, and the enclosed card... Also, I should be able to change the icons, text that is within the icon and also the on tap function for each of the buttons should be able to do something different whenever it's tapped
class _CustomCardState extends State<CustomCard> {
#override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.fromLTRB(20, 10, 20, 1),
color: Colors.red,
child: InkWell(
onTap: (){},
child: ListTile(
leading: Card(
color: Colors.white,
margin: EdgeInsets.all(5),
child: Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
KycIcons.add_a_photo,
size: 20,
color: Colors.red,
),
),
),
title: Text('Uplaod your selfie',
style: TextStyle(color: Colors.white, fontSize: 16)),
),
),
);
}
}`
Here is a very simple example of how you can build reusable widgets:
import 'package:flutter/material.dart';
class ContainerMain extends StatelessWidget {
String text;
Color color;
ContainerMain(this.text, {this.color = Colors.white});
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
alignment: Alignment.center,
height: size.height / 10,
width: size.width,
child: Text(
text,
style: TextStyle(fontSize: 20, color: color),
),
);
}
}
Here is my contribution to your class. BTW, it should be a StatelessWidget instead of a StatefulWidget since there are no state variables involved.
import 'package:flutter/material.dart';
class CustomCard extends StatefulWidget {
final Color iconColor;
final double iconSize;
final Color cardColor;
final Color innerCardColor;
final String title;
final TextStyle style;
final void Function()? onTap;
const CustomCard({
Key? key,
this.iconColor = Colors.red,
this.iconSize = 20.0,
this.cardColor = Colors.red,
this.innerCardColor = Colors.white,
this.title = 'Upload your selfie',
this.style = const TextStyle(color: Colors.white, fontSize: 16),
this.onTap,
}) : super(key: key);
#override
_CustomCardState createState() => _CustomCardState();
}
class _CustomCardState extends State<CustomCard> {
#override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.fromLTRB(20, 10, 20, 1),
color: widget.cardColor,
child: InkWell(
child: ListTile(
leading: Card(
color: widget.innerCardColor,
margin: const EdgeInsets.all(5),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
KycIcons.add_a_photo,
size: iconSize,
color: widget.iconColor,
),
),
),
title: Text(widget.title, style: widget.style),
onTap: widget.onTap,
),
),
);
}
}

How to make a button return to its initial state

I created a custom button where it expands on the first tap, and on the second tap, it will go to another screen. Now, the problem is, I created three instances of that button. So, the button that I tapped first remains expanded and would not go to its initial size. I would like to make the button return to its initial state whenever the user taps any other widget. Sorry for my code. I'm still practicing. Hope someone help.
Here is the code for the custom button.
import 'package:flutter/material.dart';
double prevHeight = 0;
class CustomRetractableButton extends StatefulWidget {
double height;
double width;
String imagePath;
Color color;
CustomRetractableButton({
required this.height,
required this.width,
required this.color,
required this.imagePath,
}) {
prevHeight = height;
}
#override
_CustomRetractableButtonState createState() =>
_CustomRetractableButtonState();
}
class _CustomRetractableButtonState extends State<CustomRetractableButton>
with TickerProviderStateMixin {
String btnText = '';
Alignment imageAlignment = Alignment.center;
bool isSelected = false;
#override
Widget build(BuildContext context) {
double size = MediaQuery.of(context).size.height;
return AnimatedContainer(
duration: Duration(milliseconds: 150),
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(20.0),
image: DecorationImage(
image: AssetImage(widget.imagePath),
alignment: imageAlignment,
fit: BoxFit.fitWidth,
),
),
height: widget.height,
child: TextButton(
onPressed: () {
if (!isSelected) {
isSelected = !isSelected;
setState(() {
widget.height = size;
btnText = 'Send Alert';
imageAlignment = Alignment.topCenter;
});
} else {
//navigates to another screen
}
},
child: Align(
alignment: Alignment.bottomCenter,
child: RotatedBox(
quarterTurns: -1,
child: AnimatedSize(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 150),
child: Padding(
padding: const EdgeInsets.only(left: 100),
child: Text(
btnText,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
),
);
}
}
Here is the code of main.dart
import 'package:custom_animated_button/custom_retractable_button.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double height = 250;
String btnText = '';
bool isSelected = true;
Alignment imageAlignment = Alignment.center;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
flex: 1,
child: Column(
children: [
Container(
width: double.infinity,
child: const Text(
"Request Assistance",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 20,
),
),
),
Container(
width: double.infinity,
child: const Text(
"Please choose the type of responder",
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 14,
),
),
),
],
),
),
Expanded(
flex: 10,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: CustomRetractableButton(
height: 250,
width: double.infinity,
imagePath: 'assets/police3.png',
color: const Color(0xFF4F70A1),
),
),
const SizedBox(
width: 15,
),
Expanded(
child: CustomRetractableButton(
height: 250,
width: double.infinity,
imagePath: 'assets/medic.png',
color: const Color(0xFF81C6D6),
),
),
const SizedBox(
width: 15,
),
Expanded(
child: CustomRetractableButton(
height: 250,
width: double.infinity,
color: const Color(0xFFE05A45),
imagePath: 'assets/firefighter.png',
),
),
],
),
),
],
),
),
),
),
);
}
}
You want:
allow CustomRetractableButton to return to the initial state when the item is pressed.
You need:
save the initial setting of the CustomRetractableButton.
expose the change and restore the state
TODO:
Make all properties (height, width, imagePath) of CustomRetractableButton final.
Create an activeHeight property under _CustomRetractableButtonState.
Delete your global variable prevHeight, it will not work on multiple instances.
Create a static variable of type _CustomRetractableButtonState that will save the state of the last active CustomRetractableButton.
The complete code of your custom button
import 'package:flutter/material.dart';
class CustomRetractableButton extends StatefulWidget {
final double initialHeight;
final double width;
final String imagePath;
final Color color;
CustomRetractableButton({
required double height,
required this.width,
required this.color,
required this.imagePath,
}):initialHeight = height;
#override
_CustomRetractableButtonState createState() =>
_CustomRetractableButtonState();
}
class _CustomRetractableButtonState extends State<CustomRetractableButton>
with TickerProviderStateMixin {
static _CustomRetractableButtonState? oldActive;
String btnText = '';
Alignment imageAlignment = Alignment.center;
bool isSelected = false;
double activeHeight = 0;
#override
initState(){
activeHeight = widget.initialHeight;
super.initState();
}
revert(){
if(mounted){
setState((){
activeHeight = widget.initialHeight;
isSelected = false;
});
}
}
#override
Widget build(BuildContext context) {
double size = MediaQuery.of(context).size.height;
return AnimatedContainer(
duration: Duration(milliseconds: 150),
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(20.0),
image: DecorationImage(
image: AssetImage(widget.imagePath),
alignment: imageAlignment,
fit: BoxFit.fitWidth,
),
),
height: activeHeight,
child: TextButton(
onPressed: () {
if (!isSelected) {
if(oldActive != null){
oldActive!.revert();
}
isSelected = true;
oldActive = this;
setState(() {
activeHeight = size;
btnText = 'Send Alert';
imageAlignment = Alignment.topCenter;
});
} else {
if(oldActive != null){
oldActive!.revert();
oldActive = null;
}
//navigates to another screen
}
},
child: Align(
alignment: Alignment.bottomCenter,
child: RotatedBox(
quarterTurns: -1,
child: AnimatedSize(
vsync: this,
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 150),
child: Padding(
padding: const EdgeInsets.only(left: 100),
child: Text(
btnText,
style: const TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
),
);
}
}
Then you can improve the code by using the providers or other flutter state management approach
Wrap the whole body of Scaffold with GestureDetector and make your operations on the on tap method.
and add behavior: HitTestBehavior.opaque, to the gesture detector.

Creating a custom Flutter DropDown button as a separate widget

I am working on a Flutter project, which has multiple dropdown buttons on different pages. I have created a custom dropdown button and it's working fine. But when I try to make it a separate widget (to use in other pages) in a file having some errors. below is the code I tried.
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
List<Activities> _activities = [
Activities(id: 1, icon: Icons.place, title: 'Travel'),
Activities(id: 2, icon: Icons.food_bank, title: 'Eat'),
];
List<DropdownMenuItem<Activities>> activityListDropDownItems = [];
Activities selectedActivity;
int selectedActivityId = 0;
String selectedActivityTitle = '';
IconData selectedActivityIcon = Icons.addchart;
List<DropdownMenuItem<Activities>> buildActivityList(List activities) {
List<DropdownMenuItem<Activities>> items = [];
for (Activities activity in activities) {
items.add(
DropdownMenuItem(
value: activity,
child: Row(
children: [
Text(
'${activity.title}',
style: TextStyle(
color: Colors.red,
),
),
],
),
),
);
}
return items;
}
onChangeActivityListDropDownItem(Activities selected) {
setState(() {
selectedActivity = selected;
selectedActivityId = selected.id;
selectedActivityTitle = selected.title;
selectedActivityIcon = selected.icon;
});
}
#override
void initState() {
activityListDropDownItems = buildActivityList(_activities);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Custom DropDown'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$selectedActivityTitle'),
SizedBox(height: 10.0),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
width: 0.3,
),
borderRadius: BorderRadius.all(
Radius.circular(
30.0,
),
),
),
child: Row(
children: [
SizedBox(width: 10.0),
Icon(
selectedActivityIcon,
color: Colors.red.withOpacity(0.7),
),
SizedBox(width: 10.0),
Expanded(
child: DropdownButton(
hint: Text(
'Activity',
style: TextStyle(),
),
isExpanded: true,
value: selectedActivity,
items: activityListDropDownItems,
onChanged: onChangeActivityListDropDownItem,
underline: Container(),
),
)
],
),
),
SizedBox(height: 20.0),
//MyDropDown(
// value: selectedActivity,
// items: activityListDropDownItems,
// onChanged: onChangeActivityListDropDownItem,
//),
],
),
),
);
}
}
class MyDropDown extends StatelessWidget {
final List<DropdownMenuItem> items;
final IconData icon;
final dynamic value;
final String hintText;
final ValueChanged onChanged;
const MyDropDown(
{Key key,
#required this.items,
this.icon,
this.value,
this.hintText,
this.onChanged})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
width: 0.3,
),
borderRadius: BorderRadius.all(
Radius.circular(
30.0,
),
),
),
child: Row(
children: [
SizedBox(width: 10.0),
Icon(
icon,
color: Colors.red.withOpacity(0.7),
),
SizedBox(width: 10.0),
Expanded(
child: DropdownButton(
hint: Text(
hintText,
style: TextStyle(),
),
isExpanded: true,
value: value,
items: items,
onChanged: onChanged,
underline: Container(),
),
)
],
),
);
}
}
class Activities {
final int id;
final IconData icon;
final String title;
Activities({#required this.id, #required this.icon, #required this.title});
}
when I use the MyDropDown shown an error. How to pass value and onChanged to DropdownButton?
MyDropDown(
value: selectedActivity,
items: activityListDropDownItems,
onChanged: onChangeActivityListDropDownItem,
),
Your value expects a datetype dynamic while your passing Activities
class MyDropDown<T> extends StatelessWidget {
final T value;
const MyDropDown({Key key, this.value}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container();
}
}
Declare Object type
MyDropDown<Activies>(
value: selectedActivity,
items: activityListDropDownItems,
onChanged: onChangeActivityListDropDownItem,
),

AnimatedContainer not working with ListView

The AnimatedContainer class is not very clear for me. From the code below (that works) - all the children are created in all list views. I want to only populate certain list views - not all. How do I do this?
You can see the code works - no errors, but no matter how much I change this code, I cannot seem to get a list view or card to have uniquely its own children (or expandedContainers).
Its really causing me a lot of distress.
And when I press down the down button, it expands everything, not just the cell I need expanded.
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
appBar: new AppBar(
title: new Text("Expandable List"),
backgroundColor: Colors.redAccent,
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ExpandableListView(title: "Title $index");
},
itemCount: 5,
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
const ExpandableListView({Key key, this.title}) : super(key: key);
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue,
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.title,
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
decoration:
new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
child: new ListTile(
title: new Text(
"Cool $index",
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
leading: new Icon(
Icons.local_pizza,
color: Colors.white,
),
),
);
},
itemCount: 15,
))
],
),
);
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double collapsedHeight;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.collapsedHeight = 0.0,
this.expandedHeight = 300.0,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : collapsedHeight,
child: new Container(
child: child,
decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.blue)),
),
);
}
}