I'm trying to call an api when I expand the tile by using future builder. Which returns a list. However, when I click on it, it expands the whole screen and doesn't display anything.
Here's an image of what I'm describing:
Here's the code:
Widget build(BuildContext context) {
return FutureBuilder(
future: marketApiCall(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if(snapshot.hasData) {
return ListView.separated(
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int count) {
return ExpansionTile(
title: Text(snapshot.data[count].itemName),
children: <Widget>[
FutureBuilder(
future: getItemDetail(itemName: snapshot.data[count].urlName),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if(snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData){
debug.output(fromFunction:"Widget build", message: snapshot.data[0]['en']['description']);
return Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.8,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Item description: ", style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: Text(snapshot.data[0]['en']['description'].toString().replaceAll(reg, ''))
)
],
)
],
)
);
} else {
return Center(
child: Text("Error: No data")
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
)
],
);
},
);
} else {
return ListTile(
title: Text("There was an error with the api call, please try again later")
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
I've tried adding shrinkwrap to see if that would work. That didn't.
Also, debug.output is just a custom function that displays print messages so there is no need to worry about that. As you can see, it's a future builder inside a futurebuilder. I've tried separating the inside future builder to a separate stateful class and returning a column to the expansion tile, however that didn't work either.
I think the proble is right after the "FutureBuilder"
return Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.8,
You're giving its child Container an 80% of the screen height, that, with the ExpansionTile's height, will occupy the whole screen (or almost).
Try removing the container height and let its child to size the container.
Related
so I'm trying to create a loop that will add a string to a List from my Bloc, however whenever i successfully added the string into the list, every single time a setstate is ran the program runs the loop all over again and adds the string into the list, i think adding a boolean for the for loop could work, but is there a more efficient way to do this?? I only want the loop to run once, even when the build method is called again.
code:
Container(
height: MediaQuery.of(context).size.height / 1.2,
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => setState(() {}),
child: Text("set state")),
ElevatedButton(
onPressed: () {
BlocProvider.of<BlackjackkBloc>(context).add(
DrawCardEvent(deck_id: deckId!, draw_count: "1"));
setState(() {
dealerCards++;
dealerVal += 10;
});
},
child: Text("deal")),
BlocBuilder<BlackjackkBloc, BlackjackkState>(
builder: (context, state) {
if (state is BlackjackkDrawLoaded) {
for (int i = 0; i < state.bjdraw.cards.length; i++) {
print("going $i");
cards.add(state.bjdraw.cards[i].image);
print("repeat");
}
return Column(
children: List.from(cards.map((name) => Text(name))),
);
/*return Text("${cards}");*/
}
return Text("o");
}),
Text("cards length = ${cards.length}"),
Container(
height: 150,
width: 100 * dealerCards.toDouble(),
child: ListView.builder(
physics: BouncingScrollPhysics(),
/*separatorBuilder: (context, index) => SizedBox(
width: 0.1,
),*/
itemCount: dealerCards,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 100,
height: 150,
child: CachedNetworkImage(
imageUrl:
"https://deckofcardsapi.com/static/img/AS.png",
),
);
},
),
),
SizedBox(height: 30),
SizedBox(
height: 40,
),
Text(
"Dealer",
style: desctext,
),
SizedBox(height: 10),
Text(playerCards.toString()),
Text(
dealerVal.toString(),
style: cardtext,
),
BlocBuilder<BlackjackkBloc, BlackjackkState>(
builder: (context, state) {
if (state is BlackjackkShuffleLoaded) {
deckId = state.bjshuffle.deckId;
return Text(state.bjshuffle.deckId);
}
if (state is BlackjackkLoad) {
return CircularProgressIndicator();
}
return Text("blum load");
},
)
],
),
),
Try add a buildWhen method in your blocBuilder that only builds if current state differ from previous state.
BuildWhen: (previous, current) { current.state != previous.state }
Why are using setState within the bloc?
Emit a new state from the DrawCardEvent function and change your UI as per that state.
I guess you are using a bloc instead of a cubit. If you are new to the bloc, maybe start with cubit. Cubit is simple and is good enough in 99% of the business cases.
I try to create a scrollable ListView, but for somes reasons it's not working.
My goal: create a ListView connected to Firebase. My ListView gather well datas, but scoll is impossible.
The body of my Scaffold:
body: const SingleChildScrollView(
child: RecipesInformations(),
),
My widget:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _recipesStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Chargement recettes");
}
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
List tags = List.from(document["tags"]);
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: const Color(0xFF011200)),
borderRadius: const BorderRadius.all(Radius.circular(8))),
margin: const EdgeInsets.only(
bottom: 32, left: 16, right: 16, top: 8),
padding: const EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data["title"],
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
style: Theme.of(context).textTheme.headline1,
)
],
),
],
));
}).toList(),
);
},
);
}
Let me know if you need more informations.
Removing SingleChildScrollView might help.
Use ListView.builder to simplify things and remove unnecessary nested scrollviews. Something like this could work:
body: RecipesInformations(),
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _recipesStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
...
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
final document = snapshot.data!.docs[index];
return Container(
...
);
},
)
...
You'd need to use FutureBuilder instead of StreamBuilder.
From the docs, the StreamBuilder is a Widget that builds itself based on the latest snapshot of interaction with a Stream. So, only the last item is going to show up on the screen.
Instead, transform the stream to a List with the toList() method and use it on the FutureBuilder.future property like so:
...
return FutureBuilder<List<QuerySnapshot>>(
future: _recipesStream.toList(),
builder: (BuildContext context, snapshot) {
...
snapshot.data now is a List of QuerySnapshot. So, it's going to be a bit different to iterate over it:
...
return ListView(
shrinkWrap: true,
children: snapshot.data!.map((query) {
Map<String, dynamic> data = query.docs.first;
...
The result is going to be something like this:
In you ListViewconstructor, set primary: false since you are nesting it inside of a SingleChildScrollView.
I'm getting the error stated that Incorrect use of ParentDataWidget while using ListView widget into Stack widget and using ListView.builder widget into ListView widget. (I need a scrollable page that contains ListView and GridView)
It works fine in the emulator (not working in the real device) but in a log, I'm getting error.
I'm posting both screenshots and code here.
Please help!
Screenshot of emulator
Screenshot of a real device
Check the below code
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: buildAppBar(),
body: Stack(
children: [
buildBody()
],
)
);
}
Widget buildBody() {
return Container(
padding: EdgeInsets.all(15.0),
child: Expanded(
child: ListView(
shrinkWrap: true,
children: [
buildTitleSection(),
buildSearchSection(),
buildFilterSortSection(),
buildListView(),
buildGridView()
],
),
)
);
}
Widget buildListView() {
return Visibility(
visible: isListVisible,
child: Expanded(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(),
child: ListView.builder(
itemCount: items.length,
shrinkWrap: true,
physics: PageScrollPhysics(),
scrollDirection: Axis.vertical,
itemBuilder: (context, index) =>
buildRowItemsList(context, index),
)))),
);
}
Widget buildGridView() {
var screenWidth = MediaQuery
.of(context)
.size
.width;
var screenHeight = MediaQuery
.of(context)
.size
.height;
return Builder(builder: (BuildContext context) {
return Visibility(
visible: isGridVisible,
child: Expanded(
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: screenWidth / (screenHeight * 0.7),
scrollDirection: Axis.vertical,
physics: PageScrollPhysics(),
shrinkWrap: true,
children: List.generate(items.length, (index) {
return buildRowItemsGrid(context, index);
})),
),
);
});
}
When the scrollDirection is set to vertical, it works as expected. The problem is when I set the section.axis to Axis.horizontal so that the ListView displays the widgets horizontally.
This problem wont solve using Flexible or Expanded widget, because the the height of the ListView needs to be defined by the widgets in the list.
As you can see shrinkWrap is also enabled. So I dont know wth is going on here, thanks for helping.
Console says:
'constraints.hasBoundedHeight': is not true.
The relevant error-causing widget was: ListView
class SectionWidget extends StatelessWidget {
final Section section;
const SectionWidget({#required this.section});
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(section.title),
ListView.separated(
shrinkWrap: true,
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
width: 100,
color: Colors.red,
); // Just to test if it works
},
separatorBuilder: (BuildContext context, int index) {
double paddingBetween = 10;
if (section.axis == Axis.horizontal) {
return SizedBox(width: paddingBetween);
} else {
return SizedBox(height: paddingBetween);
}
},
),
],
);
}
}
That's because Column or Row gives as much height/width that their children need and ListView takes as much height/width available from its parent.
To fix this, just wrap ListView in a Container. Like this:
import 'package:flutter/material.dart';
class SectionWidget extends StatelessWidget {
final Section section;
const SectionWidget({#required this.section});
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(section.title),
Container(
height: section.axis == Axis.horizontal ? 100 : 700, // I just set these values
width: section.axis == Axis.horizontal ? 350 : 100, // to fit with my device.
child: ListView.separated( // If you want to fit for
shrinkWrap: true, // all devices, use MediaQuery
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
width: 100,
color: Colors.red,
); // Just to test if it works
},
separatorBuilder: (BuildContext context, int index) {
double paddingBetween = 10;
if (section.axis == Axis.horizontal) {
return SizedBox(width: paddingBetween);
} else {
return SizedBox(height: paddingBetween);
}
},
),
),
],
);
}
}
For more info about MediaQuery
Edit:
I think using a gridview would be more suited for this. I've remade the build code for ya to suit different children's sizes. The positioning of the children and other stuff I think you can manage.
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Text(section.title),
),
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 1),
shrinkWrap: true,
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (context, index) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
// Spacer(),
Container(
margin: EdgeInsets.all(10),
height: 100,
width: 100,
color: Colors.red,
),
// Spacer(),
],
);
},
),
),
],
);
}
I did an app and I have a problem, I runned the app on ios and android but one screen is not like I want, all my child in SizedBox are not full displayed (only on IOS), I think it is a problem with Sizedbox in IOS but I have no Idea how to resolve it.
I try to use other widget but no one works.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: StreamBuilder(
stream: Firestore.instance
.collection('rooms')
.document(pincode)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
int result =
snapshot.data['Votes'][0] - snapshot.data['Votes'][1];
if (result <= 0)
perdantID = snapshot.data['Nominés'][1];
else
perdantID = snapshot.data['Nominés'][0];
leperdant = perdantID;
if (snapshot.data['nb screen'] == 2 && gotdata == 0) {
gotdata++;
gonext(context, perdantID);
}
return Center(
child: FutureBuilder(
future: Firestore.instance
.collection('rooms')
.document(pincode)
.collection('users')
.document(perdantID)
.get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var prenomperdant = snapshot.data['prénom'];
return Center(
child: Column(
children: <Widget>[
SizedBox(height: 30),
Text('$prenomperdant',
style: TextStyle(fontSize: 50)),
Padding(
padding: const EdgeInsets.fromLTRB(80, 0, 0, 0),
child: Text(' $subquestion',
style: TextStyle(fontSize: 35)),
),
StreamBuilder(
stream: Firestore.instance
.collection('rooms')
.document(pincode)
.collection('users')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
return Column(
children: <Widget>[
SizedBox(
height: 140,
child: GridView.count(
crossAxisCount: 2,
children: snapshot.data.documents.map(
(DocumentSnapshot document) {
if (document['id'] == nomines[0] ||
document['id'] == nomines[1])
return Container(
child: OvalPic(
document['photo'],
document['couleur']),
);
else
return null;
}).toList()
..removeWhere((el) => el == null)),
),
SizedBox(
height: 190,
child: GridView.count(
crossAxisCount: 2,
children: <Widget>[
ListView.builder(
physics:
NeverScrollableScrollPhysics(),
itemCount: nbvotes[0],
reverse: true,
itemBuilder:
(BuildContext context,
int index) {
return Align(
alignment: Alignment.center,
heightFactor: 0.94,
child: Image(
image: AssetImage(
'assets/images/results/green.png'),
));
},
),
ListView.builder(
physics:
NeverScrollableScrollPhysics(),
itemCount: nbvotes[1],
reverse: true,
itemBuilder:
(BuildContext context,
int index) {
return Align(
alignment: Alignment.center,
heightFactor: 0.94,
child: Image(
image: AssetImage(
'assets/images/results/green.png'),
));
},
),
],
)),
],
);
} else
return CircularProgressIndicator();
},
),
(perdantID == nameid)
? Column(
children: <Widget>[
rbutton(mygreen, 'PASSER AU DEFI', context,
perdantID, ledefi),
SizedBox(height: 5),
Text('OU'),
SizedBox(height: 5),
rbutton(mypink, 'BOIRE', context, perdantID,
legage),
],
)
: SizedBox(height: 1)
],
));
} else
return Center(child: CircularProgressIndicator());
},
),
);
} else
return Center(child: CircularProgressIndicator());
},
));
}
}
Try to use
ConstrainedBox(
constraints: const BoxConstraints(minHeight: 140),
child:GridView....,
)
instead of
SizedBox(
height: 190,
child: GridView...
)