I have a scrollable list with a FloatingActionButton. I would like to make the list to finish before the FloatingActionButton because the last item of the list isn't visible anymore just like in the Gmail application.
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
appBar: AppBar(
brightness: Brightness.light,
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Text(
'OnePictura',
style: TextStyle(color: dark, fontSize: 20),
),
actions: [
Padding(
padding: const EdgeInsets.all(8),
child: Container(
decoration:
BoxDecoration(shape: BoxShape.circle, color: textWhite),
child: IconButton(
icon: Image.asset(
'graphics/setting_icon.png',
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => new SettingPage(),
),
);
},
),
),
),
],
),
body: LoadingIndicatorPage(
loading: _loading,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: ImageIcon(
AssetImage("graphics/icon_search.png"),
),
onPressed: () {
showSearch(context: context, delegate: DataSearch(albumList, _userId));
},
),
),
xyz(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
var result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => new CreateAlbumPage(),
),
);
if (result) {
_getAllAlbum();
setState(() {
albumList.clear();
});
}
},
backgroundColor: Colors.white,
child: Icon(
Icons.add,
color: purpleishBlueTwo,
),
),
);
}
Widget xyz() {
if (albumList.length == 0) {
return Expanded(
child: SmartRefresher(
onRefresh: _onRefresh,
controller: _refreshController,
enablePullDown: true,
enablePullUp: false,
child: Center(
child: Text(_message),
),
),
);
} else {
return Expanded(
child: SmartRefresher(
onRefresh: _onRefresh,
controller: _refreshController,
enablePullDown: true,
enablePullUp: false,
child: GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.5),
),
itemCount: albumList.length,
itemBuilder: (BuildContext context, int index) {
return buildRow(index);
},
),
),
);
}
}
Widget buildRow(int index) {
return Padding(
padding: EdgeInsets.only(top: 10.0, right: 10.0, left: 10.0, bottom: 10.0),
child: GestureDetector(
onTap: () async {
var result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
new AlbumPage(tabIndex: index, albumList: albumList),
),
);
if (result) {
_getAllAlbum();
_getSubscriptionDetails();
setState(() {
albumList.clear();
});
}
},
child: AlbumTile(
index: index,
currentUser: _userId,
albumList: albumList,
deleteAlbum: _deleteDialog,
),
),
);
}
child: GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.5),
),
itemCount: albumList.length,
itemBuilder: (BuildContext context, int index) {
return buildRow(index);
},
),
to
child: GridView.builder(
controller: _scrollController,
padding: EdgeInsets.only(bottom: 70),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.5),
),
itemCount: albumList.length,
itemBuilder: (BuildContext context, int index) {
return buildRow(index);
},
),
Related
as of the picture down below, I would like to make listview, where it is possible to add more lines(red) under each listview card.
I have implemented the overall listview(green), with the button that should add a list inside the list. Code is at the bottom
The picture is taken from the Strong app
My design right now is as follows:
Expanded(
// ignore: unnecessary_new
child: new ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int Index) {
return Card(
child: Padding(
padding: EdgeInsets.all(10),
child: ExpansionTile(
initiallyExpanded: true,
title: Text(
litems[Index],
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
children: <Widget>[
ElevatedButton(
onPressed: () {
litems.add('hei');
setState(() {});
},
child: const Text('Add Set')),
SizedBox(height: 5),
],
leading: IconButton(
icon: const Icon(
Icons.close,
color: Colors.red,
),
onPressed: () {
litems.removeAt(Index);
setState(() {});
},
),
)));
})),
ElevatedButton(
onPressed: () {
litems.add('hei');
setState(() {});
},
child: const Text('Add Exercises')),
Try my code:
List<List<String>> parent = [];//init Parent
//Parent(Exercises) layout
Column(
children: [
ListView.builder(
itemCount: parent.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return _buildList(parent[index]);
}),
TextButton(
onPressed: () {
parent.add([]);
setState(() {});
},
child: Text("Add Parent"))
],
)
//build children
_buildList(List<String> list) {
return Column(
children: [
ListView.builder(
itemCount: list.length,
shrinkWrap: true,
padding: EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
itemExtent: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.red.withOpacity((index * 5) / 100),
margin: EdgeInsets.symmetric(vertical: 0),
child: Text('Item'),
);
},
),
TextButton(
onPressed: () {
list.add("value");
setState(() {});
},
child: Text("Add Item"))
],
);
}
I am trying to list out a few texts after a bottom sheet opens, this is dynamic and comes from an API. Once the bottom sheet function is triggered, the API is called and the list view updates. In this list view I used CheckboxListTile, the problem is I am not able to do multiple selections (nor single select) on the checkboxes.
This is what I have so far:
var selectedIndex = [];
The above code is in the _MainScreenState and the function for the bottom screen is triggered in one of the buttons as:
...
onPressed: () {
_showModalBottomSheet(context).then((value) => setState(() {
index = value;
}));
}
...
Bottom sheet code
Future<AllApps?> _showModalBottomSheet(BuildContext context) {
return showModalBottomSheet<AllApps>(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
context: context,
isScrollControlled: true,
builder: (context) {
return FractionallySizedBox(
heightFactor: 0.9,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(150.0, 20.0, 150.0, 20.0),
child: Container(
height: 8.0,
width: 80.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(20.0),
),
),
),
FutureBuilder<AllApps>(
future: getAllApps(), // <- API call
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
physics: const ScrollPhysics(),
itemCount: snapshot.data?.data.length,
itemBuilder: (context, index) {
final app = snapshot.data?.data[index];
return CheckboxListTile(
enableFeedback: true,
title: Text(app!.name),
value: selectedIndex.contains(app.id),
onChanged: (_) {
if (selectedIndex.contains(app.id)) {
setState(() {
selectedIndex.remove(app.id);
});
} else {
setState(() {
selectedIndex.add(app.id);
});
}
},
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(child: CircularProgressIndicator());
},
),
Padding(
padding: const EdgeInsets.only(top: 20.0, right: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
),
ElevatedButton(
child: const Text('Save'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
)
],
),
);
});
}
I am able to see the lists being built but I am not able to select any one of them (gif screenshot):
How should I enable multiple selections?
In order to apply changes in the state to the modal, use StateFulBuilder:
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return StatefulBuilder( // this is new
builder: (BuildContext context, StateSetter setState) {
return FractionallySizedBox(
heightFactor: 0.9,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(150.0, 20.0, 150.0, 20.0),
child: Container(
height: 8.0,
width: 80.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(20.0),
),
),
),
FutureBuilder<AllApps>(
future: getAllApps(), // <- API call
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
physics: const ScrollPhysics(),
itemCount: snapshot.data?.data.length,
itemBuilder: (context, index) {
final app = snapshot.data?.data[index];
return CheckboxListTile(
enableFeedback: true,
title: Text(app!.name),
value: selectedIndex.contains(app.id),
onChanged: (_) {
if (selectedIndex.contains(app.id)) {
setState(() {
selectedIndex.remove(app.id);
});
} else {
setState(() {
selectedIndex.add(app.id);
});
}
},
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(child: CircularProgressIndicator());
},
),
Padding(
padding: const EdgeInsets.only(top: 20.0, right: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
),
ElevatedButton(
child: const Text('Save'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
)
],
),
);
});
});
You can use StatefulBuilder for providing setState in bottom sheet. Try as follows:
return StatefulBuilder(
builder:(BuildContext context,setState){
return FractionallySizedBox(....);
})
I was testing the "tap function" for items within a ListView, but it does not seem to work. The print function does not work when I tap upon the list.
return Scaffold(
appBar: AppBar(
// App Bar
title: Text(
"ListView On-Click Event",
style: TextStyle(color: Colors.grey),
),
elevation: 0,
backgroundColor: Colors.white,
),
// Main List View With Builder
body: ListView.builder(
itemCount: imgList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
print("button pressed");
print(index);
},
child: Container(
margin: const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 8.0,
),
child: Stack(
children: <Widget>[
cardDesign,
cardImage,
],
),
),
); // gesturedetector
}));
Where am I going wrong?
Try to used Column instead of Stack
ListView.builder(
shrinkWrap: true,
itemCount: 10,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
print("button pressed");
print(index);
},
child: Container(
margin: const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 8.0,
),
child: Column(
children: <Widget>[
Text(index.toString()),//put your widgets here
],
),
),
); // gesturedetector
}),
you can use ListTile inside ListView,because ListTile have onTap function, same here :
ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: ListTile(
title: Text('TextHere'),
onTap: () {
log('on tap');
},
),
);
});
In documentation of Hive we have delete method for deleting something from database, but this method don't delete from database and it only do null on index of found data and it cause some problem when we want to listen to database changes or making ListView with null data,
another problem is .values that return non-nullable data and when we try to make a ListView we get null error
late Box<Sal> _sal;
useEffect((){
_sal = Hive.box<Sal>('sal') ;
});
// ...
ValueListenableBuilder(
valueListenable: _sal.listenable(),
builder: (_, Box<Sal> sal, __) => ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return Container(
height: 50.0,
margin: EdgeInsets.symmetric(vertical: 0.0),
child: Card(
color: DefaultColors.$lightBrown,
child: Row(
children: [
CText(
text: _sal.get(index)!.salName,
color: Colors.white,
style: AppTheme.of(context).thinCaption(),
).pOnly(right: 16.0),
const Spacer(),
IconButton(
icon: Icon(
Icons.edit,
color: Colors.yellow,
),
onPressed: () => showGeneralDialog(
//...
),
),
IconButton(
icon: Icon(
Icons.delete,
color: Colors.white,
),
onPressed: () => showGeneralDialog(
//...
),
),
],
),
),
);
},
itemCount: _sal.values.length,
),
).pSymmetric(
h: 16,
),
//...
}
I found solution for this problem
late Box<Sal> _sal;
late List<Sal> _data;
useEffect(() {
_sal = Hive.box<Sal>('sal');
_data = _sal.values.toList();
});
//...
ValueListenableBuilder(
valueListenable: Hive.box<Sal>('sal').listenable(),
builder: (_, Box<Sal> sal, __) {
_data = _sal.values.toList();
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return Container(
height: 50.0,
margin: EdgeInsets.symmetric(vertical: 0.0),
);
},
itemCount: _data.length,
);
},
),
//...
I have 2 specific Widget which the first One has a RaisedButton which should be clicked and the widget with the RaisedButton will be substitute with another one.
this is my code:
Widget FundsWidget() {
return Container(
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Devi acquistare dei crediti per poter vedere i Post di ${widget.user.name}',
style: TextStyle(fontSize: 12),
),
RaisedButton(
elevation: 10,
color: Colors.blue,
child: Text('Acquista Crediti'),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PostGrid(),
),
),
),
],
),
);
}
and this is called into the main build method, once RaisedButton is clicked, should be substited by another one
Widget PostGrid() {
return Container(
height: 291,
child: GridView.builder(
controller: controller,
itemCount: widget.user.imageUrl.length + 1,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (BuildContext context, int index) {
if (index == widget.user.imageUrl.length) {}
return GestureDetector(
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
actions: [
Image.asset(widget.user.imageUrl),
Text(widget.user.name),
],
),
),
child: Card(
elevation: 5,
child: Image.asset(widget.user.imageUrl),
),
);
},
),
);
}
that at the moment is not into the build method.
how I can perform that?
I think you can use setState method inside your onPresesd and change a bool in it.
and check that bool to set suitable widget like below pattern:
class Test2 extends StatefulWidget {
#override
_Test2State createState() => _Test2State();
}
class _Test2State extends State<Test2> {
bool boolName = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child:boolName ?FontWeight:PostGrid ));
}
Widget FundsWidget() {
return Container(
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Devi acquistare dei crediti per poter vedere i Post di ${widget.user.name}',
style: TextStyle(fontSize: 12),
),
RaisedButton(
elevation: 10,
color: Colors.blue,
child: Text('Acquista Crediti'),
onPressed: () {
setState(() {
boolName = false;
});
}
),
),
],
),
);
}
Widget PostGrid() {
return Container(
height: 291,
child: GridView.builder(
controller: controller,
itemCount: widget.user.imageUrl.length + 1,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (BuildContext context, int index) {
if (index == widget.user.imageUrl.length) {}
return GestureDetector(
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
actions: [
Image.asset(widget.user.imageUrl),
Text(widget.user.name),
],
),
),
child: Card(
elevation: 5,
child: Image.asset(widget.user.imageUrl),
),
);
},
),
);
}
}