Listview show nothing with asynchronous method - flutter

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.

Related

Why setState() is calling reloading listview upon item selected, how to stop it ? in flutter App

My app reads data from PostgreSQL and displays on the screen in a listview. While selecting an item from listview app is getting refreshed and items are appending to existing list. My intention is to read data only once from the DB, display in list view, select single/multiple and proceed with processing. Any suggestions would be appriciated.
import 'package:e2/Models/MasterPositions.dart';
import 'package:flutter/material.dart';
import 'package:e2/Models/model_positions.dart';
class MasterControl extends StatefulWidget {
const MasterControl({super.key});
#override
State<MasterControl> createState() => _MasterControlState();
}
class _MasterControlState extends State<MasterControl> {
List<MasterPositions> selectedContacts = [];
List<MasterPositions> fetchedPositions = [];
List<MasterPositions> positions = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Master Control"),
centerTitle: true,
automaticallyImplyLeading: false,
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: FutureBuilder<List<dynamic>>(
future: ModelsPositions().fetchPositionsData(),
builder: (context, snapshot) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return const Center(
child: Text(
'Error while loading Master Positions screen'));
} else {
return buildPositions(positions);
}
}
},
)),
],
),
),
);
}
Widget buildPositions(List<dynamic> positions) {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: positions.length,
itemBuilder: (context, index) {
final pos = positions[index];
final custID = pos.custID;
final custName = pos.custName;
final mtm = pos.mtm;
final availableCash = pos.availableCash;
final isSelected = pos.isSelected;
return ListTile(
horizontalTitleGap: -5,
title: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(custID),
const SizedBox(height: 5),
Text(custName)
],
),
),
Flexible(
fit: FlexFit.tight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('MTM : $mtm',
softWrap: false,
style: const TextStyle(fontFamily: 'Roboto')),
const SizedBox(height: 10),
Text(
'Available : $availableCash',
softWrap: false,
),
const SizedBox(
height: 10,
),
],
),
),
],
),
),
leading: isSelected
? Icon(
Icons.check_circle,
color: Colors.green[700],
)
: const Icon(
Icons.check_circle_outline,
color: Colors.grey,
),
onTap: () {
setState(() {
positions[index].isSelected = !positions[index].isSelected;
if (positions[index].isSelected == true) {
selectedContacts.add(MasterPositions(
custID: custID,
custName: custName,
mtm: mtm,
availableCash: availableCash));
} else if (positions[index].isSelected == false) {
selectedContacts.removeWhere(
(element) => element.custID == positions[index].custID);
}
});
},
);
});
}
}
I often use something like this:
if (snapshot.hasData) {
if (snapshot.data!.isNotEmpty) {
List<dynamic> positionsRaw = snapshot.data ?? [];
for (var pos in positionsRaw) {
positions.add(MasterPositions(
custID: pos[0],
custName: pos[1],
mtm: double.tryParse(pos[2]) ?? 0.0,
availableCash: double.tryParse(pos[3]) ?? 0.0,
));
return buildPositions(positions);
} else {
// return something
}
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
And so you can guarantee that your widget will be built with data, and only be rebuilt when your new data it's loaded

Pass items to a list to previous screen in Flutter

I have a search page that displays names with an add icon. When I press the add icon I want to pass the name to my previous screen that displays a list with names. I tried to do it as you can see down in my code but I have an error that my Athlete model doesn't have the constructor add. Can you help me figure out how to display the names in my list in previous screen? Thanks in advance!
My first screen that I display a list with names:
class AthleteScreen extends StatefulWidget {
const AthleteScreen({Key? key}) : super(key: key);
#override
State<AthleteScreen> createState() => _AthleteScreenState();
}
class _AthleteScreenState extends State<AthleteScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Future<List<Athlete>>? futureAthletebyTeamKey;
final List<Athlete> _athlete = [];
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'Athletes'),
actions: <Widget>[
Row(
children: [
IconButton(
onPressed: () {
Navigator.of(context)
.push<Athlete>(
MaterialPageRoute(builder: (_) => const AddAthlete()))
.then((value) => setState(() {
if (value != null && value is Athlete) {
Athlete.add(_athlete[index].lastName, _athlete[index].firstName,_athlete[index].fatherName); //here is when I push to the page where the names that I want to add are displayed
}
}));
},
icon: const Icon(Icons.add),
color: Colors.black,
iconSize: 30.0,
),
],
),
],
),
body: Stack(
children: [
SingleChildScrollView(
child: Column(children: [
FutureBuilder<List<Athlete>>(
future: futureAthletebyTeamKey,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Athlete> _athlete = snapshot.data;
return ListView.builder(
itemCount: _athlete.length,
itemBuilder: (BuildContext context, int i) {
return CheckboxListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
'${_athlete[i].lastName} ${_athlete[i].firstName}',
),
),
],
),
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
return const Center(
heightFactor: 20,
child: CircularProgressIndicator.adaptive(),
);
},
),
]),
),
);
}
}
My second screen where the names that I want to add in the list of my first page are displayed
class AddAthlete extends StatefulWidget {
const AddAthlete({Key? key}) : super(key: key);
#override
State<AddAthlete> createState() => _AddAthleteState();
}
class _AddAthleteState extends State<AddAthlete> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Future<List<Athlete>>? futureSearchAthleteByName;
#override
void initState() {
futureSearchAthleteByName =
ApiService.searchAthletesByName(context) as Future<List<Athlete>>?;
text = myController.text;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text(
'Add Athletes',
),
],
),
),
body: SingleChildScrollView(
child: Column(
children: [
Stack(
children: [
SingleChildScrollView(
child: Column(children: [
FutureBuilder<List<Athlete>>(
future: futureSearchAthleteByName,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<Athlete> _athlete = snapshot.data;
return ListView.builder(
itemCount: _athlete.length,
itemBuilder: (BuildContext context, int index) {
if (myController.text == '') {
return Container();
} else if (myController.text != '' &&
_athlete[index]
.lastName!
.toLowerCase()
.contains(myController.text
.toLowerCase()) ||
_athlete[index]
.firstName!
.toLowerCase()
.contains(
myController.text.toLowerCase())) {
return Column(
children: [
ListTile(
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
),
Row(
children: [
Flexible(
child: Text(
'${_athlete[index].lastName} ${_athlete[index].firstName}',
),
),
],
),
Row(
children: [
Flexible(
child: Text(
'(${_athlete[index].fatherName})',
),
),
],
),
],
),
trailing: IconButton(
icon: const Icon(
Icons.add,
color: Colors.black,
),
onPressed: () {
Navigator.pop(
context,
Athlete(
lastName: _athlete[index]
.lastName,
firstName: _athlete[index]
.firstName,
fatherName: _athlete[index]
.fatherName));
print(_athlete[index].lastName);
print(_athlete[index].firstName);
print(_athlete[index].fatherName); \\here is when I pop the names in my previous screen
},
),
),
],
);
}
});
} else if (snapshot.hasError) {
logger.e('${snapshot.error}');
}
return Container();
},
),
]),
),
],
),
],
),
),
);
}
}
If I was you I might do it in a different way
I add all the user id to the list on the second screen and pass the list to the second screen
in the first screen I call the API and get all the data by id and show it
(when a user doesn't select any element don't call the API)

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');

FutureBuilder shows [instance] instead of actual data

I want to download a list from firestore and display as a list in flutter widget. The data is successfully downloaded (proved by the print(cp.data()).
However, the result shown is [Instance of '_JsonQueryDocumentSnapshot'] instead of the actual carpark data.
Could anyone pls help point out what the bug is.
Thanks
class DownloadDataScreen extends StatefulWidget {
#override
_DownloadDataScreen createState() => _DownloadDataScreen();
}
class _DownloadDataScreen extends State<DownloadDataScreen> {
List<DocumentSnapshot> carparkList = []; //List for storing carparks
void initState() {
super.initState();
readFromFirebase();
}
Future readFromFirebase() async {
// await FirebaseFirestore.instance
await FirebaseFirestore.instance
.collection('carpark')
.get()
.then((QuerySnapshot snapshot) {
snapshot.docs.forEach(
(DocumentSnapshot cp) {
carparkList.add(cp);
print('printing cp');
print(cp.data());
},
);
});
**return carparkList;**
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text(
'Car Park',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: FutureBuilder(
future: readFromFirebase(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Column(
children: [
Text('Result: ${snapshot.data}'),
],
),
)
];
} else if (snapshot.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
];
} else {
children = const <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting result...'),
)
];
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
);
},
),
);
}
}
First, you don't need to call the function from the init because you already use the FutureBuilder.
Also, you don't need to cast it because when the future completes, the async snapshot already would provide you a list of DocumentSnapshot in the data and the .doc propertie.
Like this:
FutureBuilder<QuerySnapshot>(
builder:(context,snapshot){
if(snapshot.hasData){
/// here your data
snapshot.data.docs;
}
)

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