Force navDrawer state update from outside state scope - flutter

I am currently developing an e-commerce mobile app.
Right now I am working on my navigation. There's a bunch of categories that can have subcategories and the subcategories can have their own subcategories. I retrieve a list of all categories via an API on app init and then I store it.
Here's an example of my dataset:
{
"id":"41490",
"name":"Electrical Equipment",
"subCategories":[
{
"id":"41492",
"name":"Breakers",
"subCategories":[
{
"id":"167542",
"name":"1 Pole",
"subCategories":[
{
"id":"167577",
"name":"15 Amp",
"subCategories":null
},
{
"id":"167585",
"name":"20 Amp",
"subCategories":null
},
{
"id":"167600",
"name":"30 Amp",
"subCategories":null
},
{
"id":"167606",
"name":"40 Amp",
"subCategories":null
}
]
},
I am using a listview and a listview builder to make my category list.
The listview builder also calls a recursive function to make the subcategories.
I've managed to get everything to generate dynamically meaning that if on the website we add a bunch of categories then the app will update itself automatically via the API.
My problem now is that when I click my categories, the navDrawer doesn't redraw. I have to close the categories and re-open them to make it redraw. I need some new concepts, been scratching my head on this one for a while.
I think there might be an issue with the structure of my code since I initialize the categories outside the state.
Here's my navDrawer class:
class navDrawer extends StatefulWidget {
bool _expandCategories = false;
bool _expandAccount = false;
List _categories;
var _categoryList;
List _tempSubCats;
void flickCategories(){
//_expandCategories = !_expandCategories;
//sleep(const Duration(microseconds: 100));
//_expandCategories = !_expandCategories;
}
void setCategories(List categories){
_categories = categories;
int catCount = categories.length;
_categoryList = new ListView.builder(
//shrinkWrap: true,
//physics: ClampingScrollPhysics(),
padding:EdgeInsets.all(0.0),
itemCount: catCount,
itemBuilder: (BuildContext context, int index) => buildCategories(context, index),
);
}
Widget buildCategories(BuildContext context, int index){
if(_categories[index]['subCategories']!=null){
if(idHandler.isIdOpen(_categories[index]['id'])){
_tempSubCats = [];
buildSubCategories(_categories[index],2);
ListView subCategories = new ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _tempSubCats.length,
itemBuilder: (BuildContext ct, int i){
return _tempSubCats[i];
}
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(" " + _categories[index]['name']),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap: () {
flickCategories();
idHandler.toggleId(_categories[index]['id']);
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: subCategories
)
]
);
} else {
return Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(" " + _categories[index]['name']),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: () {
flickCategories();
idHandler.toggleId(_categories[index]['id']);
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
} else {
return Container(
height: 30.0,
child: ListTile(
title: Text(" "+_categories[index]['name']),
onTap: () {
//TODO: implement category navigation
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
}
void buildSubCategories(var parent, int depth){
if(parent['subCategories']!=null){
List subCategoryList = parent['subCategories'];
int subCategoryCount = subCategoryList.length;
//Column subCats = new Column();
if(idHandler.isIdOpen(parent['id'])) {
for (var i = 0; i < subCategoryCount; i++) {
String formattedCategory = indentCategory(parent['subCategories'][i]['name'], depth);
_tempSubCats.add(
parent['subCategories'][i]['subCategories']!=null ?
Container(
height:20.0,
child:
ListTile(
title: idHandler.isIdOpen(parent['subCategories'][i]['id']) ?
Row(
children:[
Text(formattedCategory),
Transform.rotate(
angle:-math.pi/2,
child:
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
)
]
)
:
Row(
children: [
Text(formattedCategory),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: (){
flickCategories();
idHandler.toggleId(parent['subCategories'][i]['id']);
}
)
)
:
Container(
height:20.0,
child:
ListTile(
title: Text(formattedCategory),
onTap: (){
//TODO: implement category navigation
}
)
)
);
buildSubCategories(parent['subCategories'][i], depth+1);
}
}
}
}
String indentCategory(String input, int amount){
String output='';
for(var i=0; i<amount; i++){
output += ' ';
}
output+=input;
return output;
}
#override
_navDrawerState createState() => _navDrawerState();
}
class _navDrawerState extends State<navDrawer>{
#override
Widget build(BuildContext Context){
return Drawer(
child:
Container(
padding:EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 5),
child:
Column(
children:[
Container(
height:80.0,
width:double.infinity,
child:
DrawerHeader(
child: Text('Menu'),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Colors.grey,
Colors.red
])
)
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Expanded(
child:
ListView(
padding:EdgeInsets.zero,
children: <Widget>[
widget._expandCategories?
Column(
children:[
Container(
height:40.0,
child: ListTile(
title: Row(
children: [
Text('Categories'),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap:() {
_expandCat();
}
)
),
MediaQuery.removePadding(
removeTop:true,
context: context,
child:
SizedBox(
height:300.0,
child: widget._categoryList,
)
)
]
)
:Container(
height:40.0,
child:
ListTile(
title: Row(
children: [
Text('Categories'),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap:(){
_expandCat();
//Update state of the app
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Container(
height:40.0,
child:
ListTile(
title:Text('Your quotes'),
onTap:(){
//Update state of the app
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
widget._expandAccount?
Column(
children:[
Container(
height:40.0,
child:
ListTile(
title: Row(
children:[
Text('Your account'),
Transform.rotate(
angle:-math.pi/2,
child:
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap:(){
_expandAcc();
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Your Information'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Your Address'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Listed Equipment'),
onTap:(){
}
)
),
Container(
height:30.0,
child:
ListTile(
title:Text(' Add Equipment'),
onTap:(){
}
)
),
]
)
:Container(
height:40.0,
child:
ListTile(
title: Row(
children:[
Text('Your account'),
Transform.scale(
scale:0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap:(){
_expandAcc();
}
),
margin:EdgeInsets.all(0.0),
padding:EdgeInsets.all(0.0)
)
]
),
)
]
)
)
);
}
void _expandCat(){
setState((){
widget._expandCategories=!widget._expandCategories;
});
}
void _expandAcc(){
setState((){
widget._expandAccount=!widget._expandAccount;
});
}
}
NOTE: idHandler is a public member of main.dart.
NOTE2: flickCategories() is one of my attempts at updating the state.
In the screenshot below you can see what I mean:
If I click Electrical Equipment then I have to click Categories twice to make it redraw and I have to scroll back to where it was in the list.
So, how do I make the state update when one of my categories gets clicked?
Do I need something like a stateful category widget?
I'm trying to make it look responsive with arrows and indents and etc.

I figured this out on my own.
I needed to make a productCategory stateful widget and update its state from within the widget.
Each productCategory widget has a List representing the subCategories. During my recursion I add to the subCategories for each productCategory.
The productCategory widget redraws itself properly because I call setState() which has the added bonus of keeping the scroll position where it is.
Here's my productCategory widget:
class productCategory extends StatefulWidget{
String _id = '';
String _name = '';
List<productCategory> _subCategories = [];
productCategory(String id, String name){
_id = id;
_name = name;
}
void addAllSubCategories(List<productCategory> subCats){
_subCategories.addAll(subCats);
}
void addSubCategory(productCategory cat){
_subCategories.add(cat);
}
void setName(String name){
_name = name;
}
void setId(String id){
_id = id;
}
#override
_productCategoryState createState() => _productCategoryState();
}
class _productCategoryState extends State<productCategory>{
#override
Widget build(BuildContext context) {
if(widget._subCategories.isNotEmpty){
if(idHandler.isIdOpen(widget._id)){
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(widget._name),
Transform.rotate(
angle: -math.pi/2,
child:
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
)
]
),
onTap: () {
setState((){
idHandler.toggleId(widget._id);
});
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
),
MediaQuery.removePadding(
removeTop: true,
removeBottom: true,
removeLeft: true,
removeRight: true,
context: context,
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(0.0),
itemCount: widget._subCategories.length,
itemBuilder: (BuildContext context, int index){
return widget._subCategories[index];
}
)
)
]
);
} else {
return Container(
height: 30.0,
child: ListTile(
title: Row(
children: [
Text(widget._name),
Transform.scale(
scale: 0.75,
child:
Icon(Icons.arrow_back)
)
]
),
onTap: () {
setState((){
idHandler.toggleId(widget._id);
});
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
} else {
return Container(
height: 30.0,
child: ListTile(
title: Text(widget._name),
onTap: () {
//TODO: implement category navigation
}
),
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(0.0)
);
}
}
}

Related

Listview show nothing with asynchronous method

I don't know why when I build my project, no error are return but my listview is empty..
The class :
final LocationService service = LocationService();
late Future<List<Location>> _locations;
#override
void initState() {
super.initState();
_locations = service.getLocations();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mes locations'),
),
bottomNavigationBar: const BottomNavBar(2),
body: Center(
child: FutureBuilder<List<Location>>(
future: _locations,
builder:
(BuildContext context, AsyncSnapshot<List<Location>> response) {
List<Widget> children;
if (response.hasData) {
children = <Widget>[
ListView.builder(
itemCount: response.data?.length,
itemBuilder: (context, index) =>
_BuildRow(response.data?[index]),
itemExtent: 285,
),
];
} else if (response.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 40,
),
const Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Un problème est survenu'),
),
];
} else {
children = const <Widget>[
SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
strokeWidth: 6,
),
),
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
);
}),
),
);
}
// ignore: non_constant_identifier_names
_BuildRow(Location? location) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Text(
"OKOK",
style: LocationTextStyle.priceBoldGreyStyle,
),
Text("${location?.dateFin}")
],
),
Text("${location?.montanttotal}€")
],
)
],
);
}
I have print my response.data?.length and it not empty.
At first it gave me the error "has size" but now the debug console is empty too...
You can find my project on GitLab : https://gitlab.com/victor.nardel/trash-project-flutter
Thank you in advance for your help
the error is caused by ListView.builder
simple solution:
wrap your ListView with Expanded
if (response.hasData) {
children = <Widget>[
Expanded(
child:ListView.builder(
....
for better code: just return the Widget, not List of widget.
something like below:
if(hasdata) return Listview();
else if(has error) return Column();
else return CircularIndicator();
so you can avoid redundant Widget.

How to wait for a request to complete using ObservableFuture?

When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');

Update view in listview.builder child

I just started working with flutter, so far so good. But I have an issue at the moment:
I wish to make a check Icon visible when I tap on the child view in a Listview.builder widget
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return FlatButton(
onPressed:(){
setState(() {
_selected = !_selected;
choosenUser = users[index];
print("the user:${users[index].fullName},$_selected");
});
},
child:(_selected) ? UserCard(users[index], _selected):UserCard(users[index], _selected)
);
}
)
inside UserCard there is a check Icon I wish to show or hide when the FlatButton in the ListView.builder is clicked.
I passed in a boolean to the UserCard but it does not work
class UserCard extends StatefulWidget{
UserItem userItem;
bool selected;
UserCard(this.userItem, this.selected);
#override
_UserCard createState() => _UserCard(userItem,selected);
}
class _UserCard extends State<UserCard>{
UserItem _userItem;
bool selected;
_UserCard(this._userItem, this.selected);
#override
Widget build(BuildContext context) {
// TODO: implement build
return /* GestureDetector(
onTap: () {
setState(() {
selected = !selected;
print("user:${_userItem.fullName}");
});
},
child:*/Container(
height:80 ,
child:
Column(
children: <Widget>[
Row(
children: <Widget>[
_userItem.profileUrl != null? CircleAvatar(child: Image.asset(_userItem.profileUrl),): Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white70,
shape: BoxShape.circle,
image: DecorationImage(
image:AssetImage('assets/plus.png') //NetworkImage(renderUrl ??'assets/img.png')
)
),
),
SizedBox(width: 30,),
Expanded(
flex: 1,
child:
Container(
child:
Row(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12,),
_userItem.fullName != null? Text(_userItem.fullName, style: TextStyle(fontSize: 18)): Text('Anjelika Thompson', style: TextStyle(fontSize: 18),),
SizedBox(height: 12,),
Row(
//crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(child: Icon(Icons.location_on),alignment: Alignment.topLeft,),
SizedBox(width: 10,),
_userItem.distance_KM.toString() != null ? Text(_userItem.distance_KM.toString()):Text('48.7 km')
]),
],
),
],
)
),
),
SizedBox(width: 0,),
selected ? Icon(Icons.check,color: Colors.red,size: 40,):SizedBox(child: Text('$selected'),)
],
),
Container(
height: 0.5,
color: Colors.grey,
)
],
) ,
// )
);
}
}
Please what am I doing wrong here
Save your selections in list of Boolean.
list<bool> selected = list<bool>();
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return FlatButton(
onPressed:(){
setState(() {
selected[index] = !selected[index];
choosenUser = users[index];
print("the user:${users[index].fullName},$_selected");
});
},
child:UserCard(users[index], selected[index])
);
}
)
so I had to go a different route to fix the issue in my code. here is my code:
in my model class called UserItem, I introduced another parameter called selectedd
class UserItem{
String fullName, profileUrl;
double distance_KM;
bool selected;
UserItem(this.fullName, this.profileUrl, this.distance_KM, this.selected);
}
since am using static values for now, i passed in "false"
List<UserItem> users = []
..add(UserItem("Edward Norton","assets/profile_img.png", 12.0, false))
..add(UserItem("Gary Owen","assets/img.png", 21, false))
..add(UserItem("Eddie L.","assets/img_details.png", 12.7, false))
..add(UserItem("Carlos Snow","assets/header_user.png", 1.3, false))
..add(UserItem("Idibbia Olaiya","assets/profile_img.png", 0, false));
then when user clicks on any of the child item the selected value that was already set as false will be updated. here is my Listview.builder widget:
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: users.length,
itemBuilder: (BuildContext context, int index){
// final item = feeds[index];
return
Stack(
children: <Widget>[
Container(
child: FlatButton(
onPressed:(){
setState(() {
selected = !selected;
users[index].selected =selected;
// _theIcon = selected ? _theIcon : Icon(Icons.check,color: Colors.grey,size: 40,);
choosenUser.add(users[index]) ;
// print("the user:${users[index].fullName},$selected");
// child_card(users[index], selected,index);
});
}, child:child_card(users[index]),
),
)
],
);
}
)
)
Widget child_card(UserItem user){
// print("the user:${user.fullName},$selected");
return UserCard(user);
}
Hope this helps someone.

How to add footer to ReorderableListView in flutter

Trying to make a ui that contains Header and Footer with rearrangeable content items. There is a property called header from which we can add header item. But what to do if I want to add footer item as well.
import 'package:flutter/material.dart';
class MyStickyHeader extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyStickyHeaderState();
}
}
class _MyStickyHeaderState extends State<MyStickyHeader> {
List<Widget> _list = [
Text("Apple"),
Text("Ball"),
Text("Cat"),
Text("Dog"),
Text("Elephant")
];
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 30, left: 10),
color: Colors.white,
child: showData(),
);
}
Widget showData() {
return Container(
child: ReorderableListView(
header: Container(
height: 100,
color: Colors.red,
),
children: _list
.map((item) => Container(
padding: EdgeInsets.all(10),
key: Key("${(item as Text).data}"),
child: Row(
children: <Widget>[
Icon(Icons.ac_unit),
Expanded(
child: item,
)
],
),
))
.toList(),
onReorder: (int start, int current) {
// dragging from top to bottom
if (start < current) {
int end = current - 1;
Widget startItem = _list[start];
int i = 0;
int local = start;
do {
_list[local] = _list[++local];
i++;
} while (i < end - start);
_list[end] = startItem;
}
// dragging from bottom to top
if (start > current) {
Widget startItem = _list[start];
for (int i = start; i > current; i--) {
_list[i] = _list[i - 1];
}
_list[current] = startItem;
}
setState(() {});
},
),
);
}
}
Last element of your list can be a footer. It has to be a widget with onLongPress property. For example:
ReorderableListView(
onReorder: (int oldIndex, int newIndex) {},
children: List.generate(someList.items.length + 1, (index) {
if (index < someList.items.length)
return ListTile(
key: Key(someList.items[index].description),
);
else
return RaisedButton(key: Key('footer'), onPressed: () {}, onLongPress: (){}, child: Text('Button'));
})),
If you wrap your ReorderableListView with a Column and an Expanded widget, you can add a Container at the bottom to act as a footer:
Column(
children: <Widget>[
Expanded(
child: ReorderableListView(
header: Container(
height: 100,
color: Colors.red,
),
children: _list
.map((item) => Container(
padding: EdgeInsets.all(10),
key: Key("${(item as Text).data}"),
child: Row(
children: <Widget>[
Icon(Icons.ac_unit),
Expanded(
child: item,
)
],
),
)).toList(),
onReorder: (int start, int current) {
// dragging from top to bottom
if (start < current) {
int end = current - 1;
Widget startItem = _list[start];
int i = 0;
int local = start;
do {
_list[local] = _list[++local];
i++;
} while (i < end - start);
_list[end] = startItem;
}
// dragging from bottom to top
if (start > current) {
Widget startItem = _list[start];
for (int i = start; i > current; i--) {
_list[i] = _list[i - 1];
}
_list[current] = startItem;
}
setState(() {});
},
),
),
Container(
height: 40,
alignment: Alignment.center,
child: Text('Footer'),
color: Colors.orange,
),
],
),
To implement such view i recommend using Slivers.
benefits:
Sticky header/Footer.
infinity body/content scroll.
check the code below:
import 'package:flutter/material.dart';
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildListDelegate(
[
Container(
width: double.infinity,
height: 50,
color: Colors.orangeAccent,
child: Center(
child: Text(
'Header',
style: TextStyle(color: Colors.white, letterSpacing:4),
),
),
),
ListView.builder(
shrinkWrap: true,
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Center(child: Text('$index')),
);
},
),
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity,
height: 50,
color: Colors.blueAccent,
child: Center(
child: Text(
'Footer',
style: TextStyle(color: Colors.white, letterSpacing: 4),
),
),
),
),
)
],
),
);
}
}
For more detail take a look here:
https://itnext.io/create-a-header-footer-with-scrollable-body-view-in-flutter-5551087270de

Flutter display Listview when button pressed

List<ServicesMensCollection> menServicesList = []
..add(ServicesMensCollection('ihdgfstfyergjergdshf', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjerg', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjerg', 'janik', 10))
..add(ServicesMensCollection('ihdgfstfyergjergdf', 'janik', 10))
bool _value2 = false;
void _value2Changed(bool value) => setState(() => _value2 = value);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
body: new Container(
decoration: new BoxDecoration(color: const Color(0xFFEAEAEA)),
child: Padding(
padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
child: Column(
children: <Widget>[
servicesCategory(),
],),),)); }
Widget servicesButton() {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
onPressed: () {listView();},
child: Text('Mens'),),
RaisedButton(
onPressed: () {listView();},
child: Text('Womens')),
RaisedButton(
onPressed: () {listView();},
child: Text('Childrens'),
)]); }
Widget listView(){
return ListView.builder(
itemCount: menServicesList.length,
itemBuilder: (BuildContext context, int index) {
return list(index); },);
}
Widget list(int index){
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(menServicesList[index].name),
Text(menServicesList[index].name),
Checkbox(onChanged:_value2Changed,
value: _value2,
)],),);
}}
I am implementing listview with checkbox in my project.I have 3 buttons which is created in a row.I want to display the list when the button is clicked.Here the issue is listview is not at all visible for me.I had implemented the same example in android but i don't know how to do this in flutter.
Try this. This is a sample screen which you can refer for your implementation.
In this there are 3 sample list which are being replaced to main list on selection, you can add a function which will sort the list based on selection (so no need to have multiple lists)
import 'package:flutter/material.dart';
/*
These are the sample list for demo
*/
List<ItemVO> mainList = List();
List<ItemVO> sampleMenList = [
ItemVO("1", "Mens 1"),
ItemVO("2", "Mens 2"),
ItemVO("3", "Mens 3")
];
List<ItemVO> sampleWomenList = [
ItemVO("1", "Women 1"),
ItemVO("2", "Women 2"),
ItemVO("3", "Women 3")
];
List<ItemVO> sampleKidsList = [
ItemVO("1", "kids 1"),
ItemVO("2", "kids 2"),
ItemVO("3", "kids 3")
];
class TestScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _TestScreen();
}
}
class _TestScreen extends State<TestScreen> {
#override
void initState() {
super.initState();
mainList.addAll(sampleMenList);
}
#override
Widget build(BuildContext context) {
return Material(
child: Stack(
children: <Widget>[
ListView.builder(
itemBuilder: (BuildContext context, index) {
return getCard(index);
},
itemCount: mainList.length,
),
Container(
margin: EdgeInsets.only(bottom: 20),
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleMenList);
});
},
heroTag: "btn1",
child: Text("Mens"),
),
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleWomenList);
});
},
heroTag: "btn2",
child: Text("Women"),
),
FloatingActionButton(
onPressed: () {
mainList.clear();
setState(() {
mainList.addAll(sampleKidsList);
});
},
heroTag: "btn3",
child: Text("Kids"),
)
],
),
),
],
),
);
}
/*
Get the card item for a list
*/
getCard(int position) {
ItemVO model = mainList[position];
return Card(
child: Container(
height: 50,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"ID:: "+model._id,
style: TextStyle(fontSize: 18, color: Colors.black),
),
Padding(padding: EdgeInsets.only(left: 5,right: 5)),
Text(
"Name:: "+model._name,
style: TextStyle(fontSize: 18, color: Colors.black),
)
],
),
),
margin: EdgeInsets.all(10),
);
}
}
/*
Custom model
i.e. for itemList
*/
class ItemVO {
String _id, _name;
String get id => _id;
set id(String value) {
_id = value;
}
get name => _name;
set name(value) {
_name = value;
}
ItemVO(this._id, this._name);
}
In your code you didn't added ListView in widget, so it will not show any list, so try adding ListView in widget and then change the list data and try it.
I think You have 2 choices on how to tackle your problem.
Preload the listViews and set their visibility to gone / invisible
Try to play around with the code from this blog