Flutter - using ListView.builder inside FutureBuilder and SliverList - scroll behaviour - flutter

I'm trying to improve my app and want to wrap an existing FutureBuilder with a SliverList. Unfortunately I'm having a strange and incomprehensible scrolling behaviour.
Here's my code so far:
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 300,
floating: true,
flexibleSpace: FlexibleSpaceBar(
title: Padding(
padding: EdgeInsets.fromLTRB(30, 10, 30, 10),
child: Text(
'some subtext here...',
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.white,
fontSize: 12.0)),
),
background: Stack(
fit: StackFit.expand,
children: [
Container(
child: Image.network(
'https://dummyimage.com/600x400/000/fff',
fit: BoxFit.cover,
),
),
const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment(0.0, 0.5),
end: Alignment(0.0, 0.0),
colors: <Color>[
Color(0xcc000000),
Color(0x55000000),
],
),
),
),
],
),
),
),
SliverPadding(
padding: EdgeInsets.only(top: 10),
sliver: SliverList(
delegate: SliverChildListDelegate([
Container(
child: FutureBuilder(
future: fetchData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Text('loading...');
} else {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext content, int index) {
// return Text('Text ' + snapshot.data[index].name);
return snapshot.hasData ? Card(
elevation: 4,
child: ListTile(
leading: CircleAvatar(
backgroundImage:
NetworkImage(snapshot.data[index].image_url),
),
trailing: Text('Test'),
title: Text(snapshot.data[index].name),
subtitle: Text(snapshot.data[index].latin_name, style: TextStyle(
fontStyle: FontStyle.italic,
)),
onTap: () {
/*Navigator.push(context,
new MaterialPageRoute(builder: (context)=> DetailPage(snapshot.data[index]))
);*/
print('tapped');
},
),
) : Text('no Data');
},
);
//return Text('loading complete');
}
}
),
),
],),
),
)
],
)),
],
));
}
The SliverAppBar gets displayed correctly, but unfortunately since adding the Slivers my FutureBuilder doesn't display anything at all.
After adding scrollDirection and shrinkWrap to the ListView the data get's displayed but I'm unable to scroll correctly.
When dragging it at the red area the List snaps back up right after releasing The SliverAppBar doesn't shrink either. When scrolling in the green area the Sliver Header works correctly and the Scrolling doesn't snap right at the top when releasing.
Has anyone had this issue before? Or could someone explain on why this could happen?

Related

How to control widget after listview in stack

I build a flutter app with ads as a carousel slider as a bottom layer of the stack widget, and I have I list view in the top layer of the stack widget
and when the list is scrolled vertically it overlays the ads on the screen.
now I have a problem with the carousel slider, I can't scroll it manually and I can't click any
how to solve it?
demo code:
Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(
children: [
CarouselSlider(
options: CarouselOptions(
autoPlay: true,
),
items: imgList
.map((item) => Center(
child: Image.network(
item,
fit: BoxFit.cover,
)))
.toList(),
),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.only(top: 210),
itemBuilder: (context, index) => Container(
color: Colors.white,
child: Text(
'ITEM $index',
textAlign: TextAlign.center,
),
))
],
));
It depends on how you want to lay your widgets out.
If you want to keep the ads persistent (not to scroll away with list) then keep the layout like :
Scaffold(
body: Column(
children: [
CarouselSlider(),
Expanded(
child: ListView.builder(),
),
],
),
)
Using positioned widget inside stack you can easily position the widgets.
Maybe this will help you.
Stack(
children: [
Positioned(
top: 0,
bottom: 250,
left: 0,
right: 0,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) => Container(
color: Colors.white,
child: Text(
'ITEM $index',
textAlign: TextAlign.center,
),
)),
),
Positioned(
bottom: 8,
right: 0,
left: 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: CarouselSlider(
items: imgList.map((i) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 5.0),
decoration: const BoxDecoration(color: Colors.amber),
child: GestureDetector(
child: Image.network(i, fit: BoxFit.fill),
onTap: () {
print("hey");
}));
},
);
}).toList(),
options: CarouselOptions(),
)),
),
],
)

Flutter: Keyboard causing a renderflex overflow error

I'm trying to display a message to the user unless there is a renderflex overflow error of 212px at the bottom. Actually, I use a separate widget to display the message, and every time I try to type with my phone, I get an error. I tried several solutions but none of them worked for me.
I would appreciate it if someone took a look. Thanks in advance!
Here is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
'Passation chat',
style: TextStyle(color: Colors.black),
),
centerTitle: true,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Messages'),
Container(
height: 590,
child: SingleChildScrollView(
physics: ScrollPhysics(), reverse: true, child: ShowMessages()),
),
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.blue, width: 0.2))),
child: TextField(
controller: msgController,
decoration: InputDecoration(hintText: 'Enter Message'),
),
),
),
IconButton(
onPressed: () {
if (msgController.text.isNotEmpty) {
storeMessage.collection('Messages').doc().set({
"msg": msgController.text.trim(),
"user": logInUser!.email.toString(),
"time": DateTime.now()
});
msgController.clear();
FocusManager.instance.primaryFocus?.unfocus();
}
},
icon: Icon(
Icons.send,
color: Colors.blueAccent,
))
],
),
],
),
);
}
}
class ShowMessages extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Messages')
.orderBy('time')
.snapshots(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
QueryDocumentSnapshot x = snapshot.data!.docs[index];
return ListTile(
title: Column(
crossAxisAlignment: logInUser!.email == x['user']
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Container(
child: Column(children: [
Text(x['msg']),
SizedBox(
height: 5,
),
Text(
x['user'],
style: TextStyle(fontSize: 10),
)
]),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(
color: logInUser!.email == x['user']
? Colors.blue.withOpacity(0.2)
: Colors.amber.withOpacity(0.1),
borderRadius: BorderRadius.circular(15)),
),
],
),
);
},
shrinkWrap: true,
primary: true,
physics: ScrollPhysics(),
);
},
);
}
}
Screenshots from the app:
Make SingleChildScrollView the first widget of Scaffold body.
Fixed it by wrapping the Column widget by a SingleChildScrollView. Thanks mates!

Flutter Visibility with Condition (How can I hide only one widget with condition)

I have a problem with using Visibility widget.
What I want to do is hiding certain listview's by depending user's click on my Row. But when User click on row, all Listview is showing. When clicked again, all listview widget's are going away.
What I want to do is with images:
This is how my page looks
This is what happens when I click on arrow button or "Sezon 1" Text
This is what I want to do when I click on arrow button or "Sezon 1" Text
What I'm trying to do is When I click Season 2, it will show season 2 episodes. When I click season 3 it will show season 3 episodes etc.
Here is my code (I know It's a little messy for right now, apologize for that) :
GestureDetector is working same for every click.
bool viewVisible = false;
void hideWidget() {
setState(() {
viewVisible = !viewVisible;
print(viewVisible);
});
StreamBuilder<QuerySnapshot>(
stream: seasons.snapshots(),
builder:
(BuildContext context, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.hasError) {
return Center(
child:
Text('Error'));
} else {
if (asyncSnapshot.hasData) {
List<DocumentSnapshot> listOfDocumentSnap =
asyncSnapshot.data.docs;
return Padding(
padding: const EdgeInsets.only(top: 0, left: 0),
child: Align(
alignment: Alignment.topLeft,
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: listOfDocumentSnap.length,
itemBuilder: (context, index) {
var episodes = firestore
.collection('shows')
.doc(data.id)
.collection('Seasons')
.doc(listOfDocumentSnap[index].id)
.collection('Episodes');
return Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
top: 0, left: 18, right: 18),
child: GestureDetector(
onTap: () {
hideWidget();
},
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(
8),
border: Border.all(
color: Colors.pink)),
child: Row(
children: [
SizedBox(
width: 20,
),
Text(
listOfDocumentSnap[index]
.get('name')
.toString()
.toUpperCase(),
style: TextStyle(
fontSize: 20,
color:
Colors.grey[800],
fontWeight:
FontWeight.w700),
),
Spacer(flex: 1),
Icon(
Icons.arrow_drop_down,
size: 45,
color: Colors.pink,
),
],
),
),
),
),
StreamBuilder<QuerySnapshot>(
stream: episodes.snapshots(),
builder: (BuildContext context,
AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.hasError) {
return Center(
child: Text(
'Error'));
} else {
if (asyncSnapshot.hasData) {
List<DocumentSnapshot>
listOfDocumentSnap =
asyncSnapshot.data.docs;
return Padding(
padding:
const EdgeInsets.only(
top: 5, left: 18.0),
child: Visibility(
visible: viewVisible,
child: Align(
alignment:
Alignment.topLeft,
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount:
listOfDocumentSnap
.length,
itemBuilder:
(context,
index) {
return ListTile(
onTap: () {
setState(
() {
selectedIndex =
index;
});
},
trailing:
Icon(Icons
.play_arrow),
title: Text(listOfDocumentSnap[
index]
.id
.toString()),
);
/* return Text(
listOfDocumentSnap[
index]
.get(
'name'));*/
},
),
],
),
),
),
);
} else {
return Center(
child:
CircularProgressIndicator());
}
}
},
),
SizedBox(
height: 30,
)
],
);
},
),
],
),
),
);
} else {
return Center(child: CircularProgressIndicator());
}
}
},
),
Sorry, I did not check what is wrong with your code. But, instead of writing your own hide/show logic for each row, try to use Flutter's ExpansionTile widget. It will handle the expanded/collapsed states for you:
https://medium.com/flutterdevs/expansion-tile-in-flutter-d2b7ba4a1f4b
There are two different way I know to handle widget visibility. Create a bool to switch visibility. I'm using _show.
Using if condition
inside (column or any widget)
if (_show) Container(),
Using Visibility Widget
Column(
children: [
Visibility(
visible: _show,
child: Container(),
),
)

Flutter: Creating GridView From Firebase Realtime DB items

Currently, in my app's homescreen I have a firebase_animated_list widget which creates a listView. However, I want to show these items in a GridView because it'll look much better.
Here's my code snippet.👇🏼
body: Column(
children: <Widget>[
Flexible(
child: FirebaseAnimatedList(
query: firebaseRef,
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return InkWell(
child: ListTile(
contentPadding: EdgeInsets.all(7),
title: Text(mynode[index].key),
leading: CircleAvatar(
radius: 30,
child: FittedBox(
child: Text(mynode[index].id),
),
),
trailing: Icon(Icons.play_arrow),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DemoDb(
id: othernode[index].id,
),
),
),
),
);
},
),
),
],
),
This perfectly creates a list of items but how should I change it to a GridView? I tried using GridView.count in place of ListTile widget. but because it was nested inside the firebase_animated_list , Each grid is layed out inside this animated list.
Is there any plugins or libraries that can help me in this? perhaps a code snippet Or if someone can suggest me any better approach to achieve this, it would mean world to me.
Thank You.
try Using StreamBuilder to load the items in a map and then use a Gridview and then you can populate the Gridview with your data.
have a look at the following Example:
Here we have a model of the data that we're trying to pull from firebase.
class Product {
String key;
String cardid;
String cardname;
String cardimage;
int cardprice;
String carddiscription;
Product(this.key,this.cardid,this.cardname,this.cardimage,this.cardprice,this.carddiscription);
}
and here is how to implement a GridView inside a StreamBuilder and populate it with the data:
return StreamBuilder(
stream: FirebaseDatabase.instance
.reference()
.child("Products")
.onValue,
builder: (BuildContext context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
Map<dynamic, dynamic> map = snapshot.data.snapshot.value;
products.clear();
map.forEach((dynamic, v) =>
products.add( new Product(v["key"],v["cardid"] , v["cardname"],v["cardimage"] ,v["cardprice"], v["carddiscription"]))
);
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemCount: products.length,
padding: EdgeInsets.all(2.0),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: (){
},
child: Padding(
padding: EdgeInsets.all(5),
child: Container(
width: (screenWidth(context)/2)-15,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
image: DecorationImage(
image: NetworkImage(products[index].cardimage),
fit: BoxFit.cover,
),
),
child: Padding(
padding: EdgeInsets.all(0),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(15.0)),
gradient: new LinearGradient(
colors: [
Colors.black,
const Color(0x19000000),
],
begin: const FractionalOffset(0.0, 1.0),
end: const FractionalOffset(0.0, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
products[index].cardname,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500,color: Colors.white),
),
Text('Rs. ${products[index].cardprice}'
,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w200,color: Colors.white),
),
],
),
),
),
), /* add child content here */
),
),
);
},
);
} else {
return CircularProgressIndicator();
}
}),

FLUTTER: How to change background color inside a gridview?

I use gridview in flutter and I have a problem, the backgroundcolor of the screen is black but when I return a gridview the background color of the cells are white. I want to change the background color of the cells.
I try to move the gridview inside a container and add a Boxdecoration but it doesn't works, can you help me ? There is my code:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Center(
child: Column(
children: <Widget>[
conseil(text, lien),
SizedBox(height: 20,),
Text("GAME PIN", style: TextStyle(fontSize: 40),),
Container(
padding: EdgeInsets.fromLTRB(30, 10, 30, 10),
decoration: BoxDecoration(
border: Border.all(width: 2.0, color: Color(0xffa8277b)),
borderRadius: BorderRadius.circular(15),
),
child: Text(id, style: TextStyle(fontSize: 30),)),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('rooms')
.document(id)
.collection('users')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Text("Chargement....");
else {
return GridView.count(
crossAxisCount: 6,
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return OvalPic(
document['photo'], document['couleur']);
}).toList());
}
},
),
),
button(mypink, 'COMMENCER', context),
SizedBox(height: 15,)
],
),
),
);
}
Wrapping it in a Container and adding color to it should do it,
return Container(
color: Colors.black,
child: GridView.count(
crossAxisCount: 4,
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return OvalPic(
document['photo'], document['couleur']);
}).toList()));
I was able to remove Grid Tile background color using elevation property. I tried as below,
GridTile(
child: new Card(
elevation: 0,
color: Colors.transparent,
child:Text('Sample')))
Sharing this as it may be useful later.