I'm trying to make a Fetch Data on Flutter but my app gives the error:
The getter 'length' was called on null. Receiver: null Tried calling: length.
If I insert a log in result.statusCode, my value return in console.
I tried to consult other projects and documentation, but nothing works. I need the data to be applied to a label or even a text and return, but this is my main problem.
My code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class UserList extends StatelessWidget{
final String apiUrl = "myAPI";
Future<List<dynamic>> fetchUsers() async {
var result = await http.get(apiUrl,
headers: {HttpHeaders.authorizationHeader: "Bearer TOKEN"});
if(result.statusCode == 200){
return json.decode(result.body)['results'];
} else{
throw Exception('Não foi possível funcionar');
}
}
bool _sucess(dynamic sucess){
return sucess['sucess'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('User List 1'),
),
body: Container(
child: FutureBuilder<List<dynamic>>(
future: fetchUsers(),
// ignore: missing_return
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData){
print(_sucess(snapshot.data[0]));
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index){
return
Card(
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(snapshot.data[index]['picture']['large'])),
title: Text(_sucess(snapshot.data[index]).toString()),
)
],
),
);
});
}
else {
print(_sucess(snapshot.data[3]));
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index){
return
Card(
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(snapshot.data[index]['picture']['large'])),
title: Text(_sucess(snapshot.data[index]).toString()),
)
],
),
);
});
}
},
),
),
);
}
} ```
My JSON:
{
"success": true,
"data": [
{
"id": 15014,
"itens": [
{
"data": "2020-06-23T14:38:03.000Z",
"pac": 6816608,
}
],
"podeImprimir": true
}
]
} ```
When if (snapshot.hasData) returns false, you are still calling .length on snapshot.data, which is why you're receiving an error.
...
else { // This code is executing because (snapshot.hasData) has returned false
print(_sucess(snapshot.data[3]));
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: snapshot.data.length, // This is causing the error, snapshot.data is null
itemBuilder: (BuildContext context, int index){
...
Set your itemCount some other way, like with a constant- itemCount: 1, or with a variable that is not null.
Related
In flutter if i wanted to print the values using FutureBuilder
I have to write this 3 line individually
snapshot.data[index].courseName ,
snapshot.data[index].coursePrice,
snapshot.data[index].aboutCourse
So, Is there any way to make a list like this -> fieldItem = ['courseName' , 'coursePrice' , 'aboutCourse'] and then pass fieldItems after the .data[index].{our varibles}
**snapshot.data[index].fieldItems**
varibles will be passed through the fieldItem.
Sorry for my bad english
body: FutureBuilder(
future: _loadData(),
builder: (BuildContext ctx, AsyncSnapshot<List> snapshot) {
if(snapshot.hasData) {
// You can make variable here
// final _data = snapshot.data;
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, index) {
// Or as in your case
final _data = snapshot.data![index];
return Card(
margin: const EdgeInsets.all(10),
// render list item
child: ListTile(
contentPadding: const EdgeInsets.all(10),
title: Text(_data['title']),
subtitle: Text(snapshot.data![index]['body']),
),
),
}
);
} else {
return const Center(child: CircularProgressIndicator());
}
}
);
Refer 11th line, if it helps, upvote.
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();
},
);
One of my routes shows current data which is stored in firestore database. I am calling the function to retrieve the data from firestore in the initState method. The page will show all the data which are retrieved from firestore. It works fine i.e, when the user navigates to this page (quotesPage) it shows the data. But while navigating, for some fraction of seconds it shows error that the local variable which stores the retrieved data is null. It happens for only that fraction of seconds after which it receives the data and shows the data. So when the user navigates to that page, I want to show a progress indicator untill it receive the data. here is my code,
Map<String, dynamic> userInfo = {};
Future<void> getQoutes() async {
var data = await FirebaseFirestore.instance.collection('user').doc(auth.currentUser!.uid).get();
setState(() {
userInfo = data.data() as Map<String, dynamic>;
});
}
#override
void initState() {
getQoutes();
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
I am calling the function getQuotes() from initState() which will store the data to Map variable userInfo. So how to show a progress indicator untill the variable 'userInfo' gets data ?
Can anyone help ?
This is the updated code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Some Quotes',
),
backgroundColor: Colors.deepOrange,
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FutureBuilder<void>(
future: getQoutes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if(snapshot.hasError) {
return Text('Error : ${snapshot.error}');
}
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
default:
return const CircularProgressIndicator();
}
},
)
],
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await popUpForm();
},
),
);
You should try with Future builder or stream builder and here is the example with Future builder
FutureBuilder<String>(
future: getQoutes(), // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Expanded(
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
return Card_fun(userInfo['quotes'][index]);
}
)
);
}
},
)
For more read this article
another approach that might be worth looking at is using a addPostFrameCallback method called from your initState in which you can await the necessary condition and take appropriate action and trigger a setState.
I don't have much experience with flutter.
I would like to use the language_tool library (https://pub.dev/packages/language_tool) for Dart and Flutter.
To show the data obtained from the tool() function, I created a FutureBuilder with a ListView.builder inside, which returns a Column.
I would like there to be 2 children inside the column:
1- a Text with mistake.issueDescription as text (for each "mistake")
2- another ListView that returns the elements of the List mistake.replacements for each "mistake"
Anyone know how I can fix it?
Below I put the code I created, which works fine until I put the Listview builder inside the first ListView builder.
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
List<WritingMistake> mistakes = [];
for (var m in correction) {
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return Column(
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),
);
}
}
I hope I was clear and that someone can help me.
Thank you :)
You cannot give a listview-builder as a child for a column try changing the Column widget to a ListView and set its shrinkWrap property to true.
ListView(
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context, int mistakeIdIndex) {
return ListView(
shrinkWrap:true,
children: [
Text(snapshot
.data[mistakeIdIndex].issueDescription),
// this is where the problems begin
ListView.builder(
shrinkWrap:true,
itemCount: snapshot.data[mistakeIdIndex]
.replacements.length,
scrollDirection: Axis.horizontal,
itemBuilder:
(BuildContext context, int index) {
return Text(snapshot.data[mistakeIdIndex]
.replacements[index]);
}),
],
);
}),
);
}
}),
],
),
),
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.