I want to display values from my API in a PopupMenuItem in PopupMenuButton. I manage to display it but I want it to be dynamic. Currently, I hard-coded the index of each item because it seems that I cannot do looping inside PopupMenuButton.
`Widget _simplePopup4() => PopupMenuButton<int>(
child: Icon(Icons.arrow_drop_down, color: Colors.orangeAccent),
offset: Offset(0, 100),
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[1].title); //index 1
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[2].title); //index 2
}
}
return CircularProgressIndicator();
})),
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Text(snapshot.data.setTitle[3].title); //index 3
}
}
return CircularProgressIndicator();
})),
),
],
);`
//First attempt which gives error: RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.setTitle == null) {
return Container();
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.setTitle.length,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data.setTitle[index].title);
});
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
//Second attempt which gives error: Another exception was thrown: A RenderFlex overflowed by 85 pixels on the bottom.
Widget _simplePopup5() => PopupMenuButton(
itemBuilder: (context) {
var list = List<PopupMenuEntry<Object>>();
list.add(
PopupMenuItem(
value: 1,
child: Container(
child: FutureBuilder<SettingCtrl>(
future: getSettingCtrl(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final listTitle = <Widget>[];
for (var i = 0;
i < snapshot.data.setTitle.length;
i++) {
listTitle.add(SingleChildScrollView(
scrollDirection: Axis.vertical,
child: InkWell(
child:
Text(snapshot.data.setTitle[i].title),
)));
}
if (snapshot.data.setTitle == null) {
return Container();
} else {
return Column(children: listTitle);
}
}
return CircularProgressIndicator();
})),
),
);
list.add(
PopupMenuDivider(
height: 10,
),
);
return list;
},
icon: Icon(
Icons.settings,
size: 50,
color: Colors.white,
),
);
From the screenshot, only one item are clearly displayed which is "MR" while the other item (before item "MR") are displayed in half. Meanwhile, the rest of the item (after item "MR") being replaced with error message.
The screenshot of the second attempt error
The cause of the RenderFlex error is because the child Widget expands beyond the parent Widget. What you can do here is fetch the List of PopupMenu items prior to rendering the PopupMenuButton. With this approach, the List items is ready prior to clicking the PopupMenuButton.
Related
It seems that the GridView.builder inside FutureBuilder duplicates each element a number of times equal to the list length.
Here is the code:
InformationScreen:
List<Reference> documentReference = [];
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.items.length,
itemBuilder: (context, index) {
final photo = snapshot.data!.items[index].getDownloadURL();
final photoName = snapshot.data!.items[index].name;
final metaData = snapshot.data!.items[index].getMetadata();
documentReference = snapshot.data!.items;
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if(snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data;
List<Document> documents = [];
for (int i = 0; i < documentReference.length; i++) {
Document document = Document(user!.uid, image!, photoName, photoType);
documents.add(document);
}
return DocumentGrid(documents: documents,); // <------------------------------
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if (snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);
DocumentGrid
import 'package:flutter/material.dart';
import 'package:app_test/constant/color.dart';
import '../constant/text.dart';
import '../model/document.dart';
class DocumentGrid extends StatelessWidget {
final List<Document> documents;
const DocumentGrid({Key? key, required this.documents}) : super(key: key);
#override
Widget build(BuildContext context) {
return buildGridView();
}
//****************************************************************************
// Create GridView
//****************************************************************************
Widget buildGridView() => GridView.builder(
itemCount: documents.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final photo = documents[index].photo;
final title = documents[index].title;
final type = documents[index].type;
return buildGridViewItem(photo, title, type);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
//****************************************************************************
// Create GridView item
//****************************************************************************
Widget buildGridViewItem(String photo, String? title, String type) => Container(
width: 50,
height: 50,
color: phoneButtonColor,
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
buildNetworkImage(photo, type),
buildBlackOpacity(title),
],
),
);
//****************************************************************************
// Create Network image
//****************************************************************************
Widget buildNetworkImage(String photo, String type) => Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
photo,
errorBuilder: (context, exception, stackTrace) {
return type == "pdf" || type != "jpg"
|| type != "jpeg" || type != "png"
? Image.asset(
fit: BoxFit.cover,
width: 100,
height: 100,
"assets/images/pdf.png",
)
: Container(
color: grey,
width: 100,
height: 100,
child: const Center(
child: Text(
errorLoadImage,
textAlign: TextAlign.center,
),
),
);
},
);
//****************************************************************************
// Create Black opacity
//****************************************************************************
Widget buildBlackOpacity(String? title) => Container(
color: Colors.black54,
padding: const EdgeInsets.symmetric(
vertical: 30,
horizontal: 20,
),
child: Column(
children: [
Expanded(
child: Center(
child: Text(
title!,
style: const TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
),
],
),
);
}
How can I solve that, thanks in advance
Problem solved
Replacing ListView by GridView
Widget showSavedDocument() => FutureBuilder(
future: _futureListResult,
builder: (context, AsyncSnapshot<ListResult> snapshot) {
if(snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return GridView.builder(
itemCount: snapshot.data!.items.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 2,
mainAxisSpacing: 2,
),
itemBuilder: (context, index) {
final instructorDocument = snapshot.data!.items;
final photo = instructorDocument[index].getDownloadURL();
final photoName = instructorDocument[index].name;
final metaData = instructorDocument[index].getMetadata();
return Column(
children: [
FutureBuilder(
future: metaData,
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if (snapshot.hasData) {
photoType = snapshot.data!.contentType!;
}
return Container();
},
),
FutureBuilder(
future: photo,
builder: (context, AsyncSnapshot<String?> snapshot) {
if (snapshot.hasData) {
final image = snapshot.data!;
return Expanded(
child: buildGridViewItem(image, photoName, photoType),
);
}
return Container();
},
),
],
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
return Container();
},
);
I am using future builder with service function like this, when getUserProfile function
When I get the APIService.getUserProfile() funciton return Forbidden(403) error I want to navigate to login page, how can I do that where can I add this navigator function in my code ? How can I detect and return Login Page
body: FutureBuilder(
future: APIService.getUserProfile(),
builder: (
BuildContext context,
AsyncSnapshot<String> model,
) {
if (model.hasData) {
return SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 240,
child: FutureBuilder(
future: getCurrentUser(),
builder: (context, snapshot) {
return ListView.separated(
itemBuilder: (context, index) {
print(index);
if (username == null) {
return Text(
""); //GetUserName(documentId: user!.uid);
} else {
print("build");
return MyCard(
balance: balance ?? 0,
creditCardsNumber: 0,
userName: username.toString(),
card: myCards[index],
);
}
},
separatorBuilder: (context, index) {
return SizedBox(width: 10);
},
itemCount: 1,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
);
}),
),
SizedBox(
height: 30,
),
Text(
"Recent Payments",
style: ApptextStyle.BODY_TEXT,
),
SizedBox(
height: 15,
),
FutureBuilder(
future: getCurrentUser(),
builder: (context, snapshot) {
return ListView.separated(
itemBuilder: (context, index) {
if (username != null) {
return TransactionCard(
transaction: myTransactions[index]);
} else {
return Text("");
}
},
separatorBuilder: (context, index) {
return SizedBox(height: 10);
},
itemCount: myTransactions.length,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
);
})
],
),
),
);
} else if (model.hasError) {
print("HEREEE");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LoginScreen();
},
),
);
return Text("asdasfsdfd");
}
return Text("");
}));
I have a problem, this problem is that I want to run listview.builder in column. But when I set the scroll feature to column, the application does not work even though it does not give an error. I have codes below.
return SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
Flexible(
child: buildFutureBuilder(),
),
],
),
);
}
FutureBuilder<MarkaResponse> buildFutureBuilder() {
return FutureBuilder<MarkaResponse>(
future: getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
default:
if (snapshot.hasError) {
return Center(child: Text("Hata"));
} else {
return ListView.builder(
itemCount: 7,
itemBuilder: (context, index) {
this.response = snapshot.data.liste;
Marka item = response[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.motorcycle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Modeller(
numMarka: item.id,
)));
},
);
});
}
}
},
);
}
SingleChildScrollView forces infinite height, so you can have, Spacers, Expanded,Flexible etc. If you give a fixed height for your ListView it will work
try this:
return SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
Container(height: 50),
Expanded(
child: buildFutureBuilder(),
),
],
),
);
}
FutureBuilder<MarkaResponse> buildFutureBuilder() {
return FutureBuilder<MarkaResponse>(
future: getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
break;
default:
if (snapshot.hasError) {
return Center(child: Text("Hata"));
} else {
return ListView.builder(
shrinkWrap: true,
itemCount: 7,
itemBuilder: (context, index) {
this.response = snapshot.data.liste;
Marka item = response[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.motorcycle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Modeller(
numMarka: item.id,
)));
},
);
});
}
}
},
);
}
Make a parent container for ListView.builder, and set a height of that container.
I'm trying to create a Futurebuilder function to call and display all data that inserted in database unfortunately I got this error 'UnimplementedError' and im pretty stock on this any suggestion will be appreciated.
Here in my full code for implementation to display data in been trying to fix my error 'UnimplementedError' in which I'm trying to do is to display inserted in list view not in web view any suggestion will be appreciated.
body: Center(
child: Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<ContactsDao>(
future: _calltheStream(),
builder: (BuildContext context,
AsyncSnapshot<ContactsDao> snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState == ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
return StreamBuilder<List<ContactObject>>(
stream: snapshot.data.findallContactsById(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.connectionState ==
ConnectionState.none) {
return Container(
child: CircularProgressIndicator(),
);
} else {
if(widget.Contactlist.length != snapshot.data.length){
widget.Contactlist = snapshot.data;
}
if(snapshot.data.length == 0){
return Center(
child: Text('No Data Found'),
);
}
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Checkbox(
value: widget.Contactlist[index].isSelect,
onChanged: (bool value) {
setState(() {
widget.Contactlist[index].isSelect = value;
});
},
),
trailing: GestureDetector(
onTap: () {
_selectedDetele(snapshot.data[index].id);
},
child: Icon(Icons.delete),
),
title: Text('${snapshot.data[index].task}',maxLines: 1,),
subtitle: Text('${snapshot.data[index].time}',style: TextStyle(fontSize: 10),),
));
}),
);
}
}); //DATA
} //DATA
}), // DATA
], // DATA
), // DATA
),//DATA
),
Future<ContactsDao> _calltheStream() async { //GET ALL DATA HERE
ContactDatabase contactDatabase = await widget.database;
widget._contactsdao = contactDatabase.contactsDao;
return contactDatabase.contactsDao;
}
I have data from firestore
I need to display the name in a single page and the name of content in another page and so episodes
Is there a better way than this
tvshow page
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
tvSelected = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(selectedTv: tvSelected);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
seasons page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
selectedSeason = index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
selectedTv: selectedTv,
selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
episodes page:
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedTv].content[selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedTv]
.content[selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
look at when I pass a route page
.............................
.............................
...........................
Firstly you you create a new provider class that will hold the indices of the currently selected tv,episode and season as shown bellow:
class CurrentIndexProvider with ChangeNotifier {
int _selectedTv;
int _selectedSeason;
int _selectedEpisode;
set selectedTv(int newIndex) {
this._selectedTv = newIndex;
notifyListeners();
}
set selectedSeason(int newIndex) {
this._selectedSeason = newIndex;
notifyListeners();
}
set selectedEpisode(int newIndex) {
this._selectedEpisode = newIndex;
notifyListeners();
}
int get selectedTv => this._selectedTv;
int get selectedSeason => this._selectedSeason;
int get selectedEpisode => this._selectedEpisode;
}
Then your tv shows page becomes:
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
// tvSelected = index; -->we dont need this anylonger
//we set the current show number to the index of the current
//listview item when tapped
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return SeasonsPage(
//we don't need to pass anything in the constructor again
// selectedTv: tvSelected
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[index].name),
),
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
your seasons page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[ selectedItems.selectedTv].content.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
//selectedSeason = index; --> we dont need this any longer
selectedItems.selectedSeason=index;
Navigator.push(context, MaterialPageRoute(
builder: (BuildContext context) {
return EpisodesPage(
//we don't need any parameter in the constructor now
// selectedTv: selectedTv,
// selectedSeason: selectedSeason,
);
}));
},
child: Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(
snapshot.data[selectedItems.selectedTv].content[index].name),
),
);
},
)
: Center(child: CircularProgressIndicator()),
),
And finally the episodes page becomes
final selectedItems = Provider.of<CurrentIndexProvider>(context);
FutureBuilder(
future: Provider.of<Data>(context).fetchShows(),
builder: (context, snapshot) => snapshot.hasData
? ListView.builder(
itemCount: snapshot.data[selectedItems.selectedTv ].content[selectedItems.selectedSeason]
.episodes.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(10.0),
width: 100.0,
color: Colors.orangeAccent,
child: Text(snapshot.data[selectedItems.selectedTv]
.content[selectedItems.selectedSeason].episodes[index]),
);
},
)
: Center(child: CircularProgressIndicator()),
),
With this you can have access to the currently selected tv show or seasons or even episodes anywhere within you code by using provider. hope this helped