'A non-null String must be provided to a Text widget' (Flutter) - firebase

I've tried to fix this with the following code but I'm afraid the error is still prevalent:
The following assertion was thrown building:
A non-null String must be provided to a Text widget.
'package:flutter/src/widgets/text.dart':
Failed assertion: line 360 pos 10: 'data != null'
child: Row(
children: [
Text(
(contact.data()['rating'] == null)
? "n/a"
: contact.data()['rating'].toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.tealAccent)),
Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
radius: 25,
backgroundImage: AssetImage("assets/girl2.jpg"),
),
),
Spacer(),
Text(
contact.data()['name'],
style: TextStyle(
fontWeight: FontWeight.w400, color: Colors.tealAccent),
),
Spacer(),
Text(
contact.data()['location'],
style: TextStyle(
letterSpacing: 1,
fontSize: 10,
fontWeight: FontWeight.w300,
color: Colors.tealAccent),
),
],
),
How would one go about solving this?

You can just add:
contact.data()['rating'] ?? "empty"
The above will check if the expression on the left is not null, if it is then "empty" will be added to the Text widget. You have to add the condition to the other Text widgets also.

Related

How to navigate to a specific route in a list full of the same widget?

I have a an app which the user is able to add in books and it upload to the firestore database, and then I use a streamBuilder() to recieve all the data and display it in a list of GestureDetector() with the card() as a child.
Here is the code for the
final bookWidget = GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
print(title);
},
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 100,
height: 140,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(bookCoverURL)),
),
),
Container(
width: 230,
//color: Colors.red,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
category.toString(),
style: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
title.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
(author),
style:
TextStyle(color: Colors.grey),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black,
size: 15,
)
],
),
],
),
//color: Colors.yellowAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
margin: EdgeInsets.all(10),
),
);
bookWidgets.add(bookWidget);
And then I display all my code in the screen by :
Column(
children: bookWidgets,
)
But how can I make the app navigate to a new screen when the user pressed on a specific card()? Since all of them are the same widget and have no unique identifier?
Any help is greatly appreciated! Thank you!
GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
Navigator.pushNamed(
context,
'/routeNameHere',
arguments: id,
);
},
)
In parameter arguments: ID - you can change the id by the id of the card that you want to navigate to.
You must create a page for the display detail of the card, which just displays a card with same id in the argument. So on your new page, create a variable for receiving the argument, e.g.:
final cardId = ModalRoute.of(context).settings.arguments as String;
So the page can display the card depending on the cardId.
You will need to create an identifier to pass (in this case “book”). In Flutter, these are necessary when calling or passing an object.
However, in regards to using Named Routes , this can allow you to send the data without the identifier but is considered bad practice in coding as you would not have a reference to the call or be able to back trace through the data.
Keeping the above in mind, it is possible to use the OnTap() inside each child card container so that on handle clicks or tap gestures, the user is taken to a different screen using routes as it is part of the GestureDetector class.

How to display my text on multiple lines?

I'm trying to figuring out how I can show my text on multiple lines. Here's my code:
title: Row(
children: [
Text(
_snapshot.data['username'],
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w700),
),
SizedBox(width: 5.0),
Text(
"${comment.data()['comment']}",
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w500),
)
],
),
But when the text is too long I get this error:
When I wrapped second text with expanded it looks like that
I want something like this
I think you can try use a RichText, see this code:
RichText(
text: TextSpan(
text: 'Title ', // _snapshot.data['username']
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
children: <TextSpan>[
TextSpan(//"${comment.data()['comment']}"
text: 'this is a very long text text text'
'text text text text text text text text text text'
'text text text text text text text text text',
style: TextStyle(
fontWeight: FontWeight.normal,
),
)
],
),
),
Using "maxLines" (Text widget) property should work, It should wrap the text.
This looks like a use-case for RichText because the only difference between the two Text widgets is the font-weight.
You can update your code to use this instead and add as much space as you want before ${comment.data()['comment']}:
title: RichText(
text: TextSpan(
title: _snapshot.data['username'],
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w700),
children: <TextSpan>[
TextSpan(text: " ${comment.data()['comment']}", style: TextStyle(fontWeight: FontWeight.w500)),
],
),
)
I use this way.
https://i.stack.imgur.com/6WrQi.png
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: ListView(
children: const <Widget>[
Card(
child: ListTile(
leading: FlutterLogo(size: 56.0),
title: Text('Two-line ListTile Two-line ListTile Two-line ListTile'),
subtitle: Text('Here is a second line'),
),
),
Card(
child: ListTile(
leading: FlutterLogo(size: 72.0),
title: Text('Three-line ListTile'),
subtitle: Text(
'A sufficiently long subtitle warrants three lines.'
),
isThreeLine: true,
),
),
],
),
),
);
}
}
Just add your Text widget inside a Sizedbox or Container with fixed width, and it will flow into multiline.
SizedBox(
width: 200,
child: Text("Some long text",
maxLines: 3,
style: const TextStyle(fontSize: 14, color: colorWhite))
)
You can use maxLine and overflow properties to control how much lines the text can run through, and what happens when the maximum line is exceeded

How to know which card is tapped in flutter

I am making my first flutter app (A coffee shop), in this I have accessed data from Firebase Cloudstore and displayed it in the form of card.
Now I have provided the user with the feature of customizing order
for that I have to know which item he is customizing and hence I need to know which card the user has tapped.
Here is my code snippet:
child: GestureDetector(
onTap: (){
return showModalBottomSheet<void>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(40.0),
topLeft: Radius.circular(40.0),
)
),
context: context,
builder: (BuildContext context) {
return Container(
child: CustomOrder(),
height: 400.0,
width: 350.0,
);
},
);
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image(
image: AssetImage('assets/coff1.PNG'),
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["item_name"]}',
style: TextStyle(
fontSize: 38.0,
fontFamily: 'GrandHotel',
fontWeight: FontWeight.bold,
color: Colors.brown[800]
),
// textAlign: TextAlign.left
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["price"]}',
style: TextStyle(
fontSize: 22.0,
fontFamily: 'GrandHotel',
color: Colors.brown[800]
),
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
'${itemslist[index]["desc"]}',
style: TextStyle(
fontSize: 22.0,
fontFamily: 'GrandHotel',
color: Colors.brown[800]
),
),
),
SizedBox(height: 10.0),
FlatButton.icon(
color: Colors.brown[400],
onPressed: (){},
icon: Icon(Icons.add),
label: Text(
"Add to cart",
style: TextStyle(
fontSize: 20.0,
color: Colors.brown[800]
),
),
),
]
),
),
),
),
It appears that you are using a listView.builder but didn't attach it to your code properly, but you are accessing this [index] in your code.
Therefore, in your onpressed button in the bottom,
onPressed: (){
print(itemslist[index]["item_name"]);} // should print your tapped item's name, and then you can do whatever you want if you have reached this far.
I would wrap each separate card with their own GestureDetector. If you need more instruction for implementing this, just let me know in the comments.

How to add a date header for my chat module in flutter with firebase?

I want to add a sticky date header for my chat module just like how WhatsApp has. I tried sticky headers dependency, but it didn't work out.
Chat Page Code:
Column(
crossAxisAlignment:
snapshot['senderUid'] == _senderuid
? CrossAxisAlignment.end
: CrossAxisAlignment.end,
children: [
new Text(
snapshot['message'],
style: TextStyle(
color: Colors.white, fontSize: 15.0),
),
Padding(
padding: const EdgeInsets.only(top: 5),
child: new Text(
snapshot['timestamp']
.toDate()
.toString()
.substring(10, 16),
style: TextStyle(
color: Colors.white.withOpacity(0.5),
fontSize: 9.0),
),
),
],
),
You can try with this package
https://pub.dev/packages/flutter_sticky_header
Or with Slivers and SliverPersistentHeaderDelegate

How do I generate a list of widgets from firebase firestore database?

I'm trying to figure out how to iterate through documents in firestore and create a text widget for each one.
I've figured out how to access those elements. I used debugPrint() and got the results I'm expecting, but I can't get it to display below my other widgets. I get a red screen with a ton of errors (on phone).Below is my code for what I've tried so far.
QuerySnapshot querySnapshot = await
Firestore.instance.collection("users").document(user.uid).collection("trails").getDocuments();
var list = querySnapshot.documents;
list.forEach((doc) => debugPrint(doc.data["Trail Name"].toString()));//works
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//these widgets are generating correctly
Text(
"First Name: $firstName",
style: TextStyle(color: Colors.white, ),
),
Text(
"Last Name: $lastName",
textAlign: TextAlign.left,
style: TextStyle(color: Colors.white,),
),
Text(
"Email: $email",
style: TextStyle(color: Colors.white, ),
),
//these ones are causing my errors.
list.forEach((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),)
],
);
final topContent = Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.40,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
return Scaffold(
body: Column(
children: <Widget>[topContent, bottomContent],
),
);
The screen on my device lights up red with errors on creating child widgets, when I'm expecting it to display the Trail Name(s) below the other info. I only included necessary code, but could include more (such as the widget's build method) if needed. Thanks for any assistance. I'm new to flutter.
Try using the map function:
List<Widget> _widgets = list.map((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),).toList();
And then for the children of the column, just add that list to the list you specify, like:
children: [ ... ] + _widgets;

Resources