Flutter error involving operator not defined - flutter

I'm new to flutter and I'm following this tutorial but I'm currently getting this error
error: The operator '[]' isn't defined for the type 'Map<String, dynamic> Function()'. (undefined_operator at [chat_app_tutorial] lib\views\search.dart:33)
Please I've been having this bug for over 2days now. I've checked here and I've been researching online but I've not seen anything that has worked so far. This is the line of code I'm getting the error from.
I'm not sure what part of my code is needed to be able to help me so I'll just put everything.
import 'package:chat_app_tutorial/widgets/widget.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController searchTextEditingController = new TextEditingController();
QuerySnapshot searchSnapshot;
initiateSearch(){
databaseMethods.getUserByUsername(searchTextEditingController.text).then((val){
setState(() {
searchSnapshot = val;
});
});
}
Widget searchList(){
return searchSnapshot != null ? ListView.builder(
itemCount: searchSnapshot.docs.length,
shrinkWrap: true,
itemBuilder: (context, index){
return SearchTile(
userName: searchSnapshot.docs[0].data["name"],
userEmail: searchSnapshot.docs[0].data["email"],
);
}
) : Container();
}
#override
void initState() {
initiateSearch();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context),
body: SingleChildScrollView(
child: Container(
child: Column(
children: [
Container(
color: Color(0x54ffffff),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchTextEditingController,
style: TextStyle(
color: Colors.white
),
decoration: InputDecoration(
hintText: "Search username",
hintStyle: TextStyle(
color: Colors.white54
),
border: InputBorder.none
),
),
),
GestureDetector(
onTap: (){
initiateSearch();
},
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0x36ffffff),
const Color(0x0fffffff)
]
),
borderRadius: BorderRadius.circular(40)
),
padding: EdgeInsets.all(12),
child: Image.asset("assets/images/search_white.png")
),
)
],
),
),
searchList()
],
),
),
),
);
}
}
class SearchTile extends StatelessWidget {
final String userName;
final String userEmail;
SearchTile({this.userName, this.userEmail});
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Column(
children: [
Text(userName, style: simpleTextStyle(),),
Text(userEmail, style: simpleTextStyle(),)
],
),
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(30)
),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text("Message"),
)
],
),
);
}
}```

The error is complaining about:
return SearchTile(
userName: searchSnapshot.docs[0].data["name"],
userEmail: searchSnapshot.docs[0].data["email"],
);
searchSnapshot.docs[0].data is a function that returns a Map. You need to call that function first.
You probably want searchSnapshot.docs[0].data()['name'] and searchSnapshot.docs[0].data()['email'].
Even better would be to avoid calling data() multiple times:
var data = searchSnapshot.docs[0].data();
return SearchTile(userName: data['name'], userEmail: data['email']);

Related

Unable to display the messages received

I am unable to display the messages that i receive i am using firebase firestore. i am trying to get all the messages with the stream builder and displaying it.
Stream Builder Class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:instagram_clone/Models/user_model.dart';
import 'package:instagram_clone/Provider/user_provider.dart';
import 'package:instagram_clone/utils/circular_progress_indicator.dart';
import 'package:instagram_clone/utils/colors.dart';
import 'package:instagram_clone/utils/utils.dart';
import 'package:instagram_clone/widgets/message_bubbles.dart';
import 'package:provider/provider.dart';
import '../resources/Firestore_methods.dart';
class MessagesScreen extends StatefulWidget {
String recieveruid;
MessagesScreen({Key? key,required this.recieveruid}) : super(key: key);
#override
State<MessagesScreen> createState() => _MessagesScreenState();
}
class _MessagesScreenState extends State<MessagesScreen> {
String recieverName=' ';
String reciverDp=' ';
bool isFetched=false;
TextEditingController messagecontoller=TextEditingController();
void sendMessage()async{
if(messagecontoller.text.isNotEmpty){
String res=await FireStoreMethods().sendMessage(
senderUid: FirebaseAuth.instance.currentUser!.uid,
recieverUid: widget.recieveruid,
message: messagecontoller.text);
if(res!='success'){
Utils().showSnackBar(context, 'Unable to send message kindly check your connection', Colors.red);
}
}
}
void getRecieverDetails()async{
DocumentSnapshot ref= await FirebaseFirestore.instance.collection('users').doc(widget.recieveruid).get();
recieverName=ref['username'];
reciverDp=ref['photourl'];
setState(() {
isFetched=true;
});
}
#override
void initState() {
print(widget.recieveruid);
getRecieverDetails();
// TODO: implement initState
super.initState();
}
#override
void dispose() {
messagecontoller.dispose();
// TODO: implement dispose
super.dispose();
}
bool _isReciever=true;
#override
Widget build(BuildContext context) {
UserModel user=Provider.of<UserProvider>(context).getUser;
return !isFetched?Center(child: CircularIndicator,):Scaffold(
appBar: AppBar(
title: Text(recieverName),
backgroundColor: mobileBackgroundColor,
centerTitle: true,
),
body: Stack(
children: [
Container(
height: MediaQuery.of(context).size.height*0.8,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chats')
.where('senderuid',isEqualTo: FirebaseAuth.instance.currentUser!.uid )
.where('recieveruid',isEqualTo: widget.recieveruid)
.snapshots(),
builder: (context,AsyncSnapshot<QuerySnapshot<Map<String,dynamic>>>snapshot){
if(!snapshot.hasData || snapshot.connectionState==ConnectionState.waiting){
return Center(child: CircularIndicator);
}
else{
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context,index){
if(snapshot.data!.docs[index]['senderuid']==FirebaseAuth.instance.currentUser!.uid){
return MessageBubbles(profilePic: user.photourl,
message: snapshot.data!.docs[index]['message'],
date: snapshot.data!.docs[index]['datetime'],
isReciever: false);
}
else{
return MessageBubbles(
profilePic: reciverDp,
message: snapshot.data!.docs[index]['message'],
date:snapshot.data!.docs[index]['datetime'],
isReciever: true);
}
});
}
}
),
),
Align(
alignment: Alignment.bottomCenter,child: Container(
child: Row(
children: [
Expanded(
child: TextFormField(
controller: messagecontoller,
maxLines: null,
decoration: InputDecoration(
hintText: 'Message...',
contentPadding: EdgeInsets.symmetric(horizontal: 8),
border: InputBorder.none
),
),
),
TextButton(onPressed: ()async{
sendMessage();
messagecontoller.clear();
}, child: Text('Send',style: TextStyle(
color: Colors.blueAccent,
fontWeight: FontWeight.bold,
fontSize: 15
),))
],
),
decoration: BoxDecoration(
border: Border.all(color: secondaryColor),
borderRadius: BorderRadius.circular(20),
color: mobileBackgroundColor,
),
),),
],
),
);
}
}
Here is Message Bubble Class
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../utils/colors.dart';
class MessageBubbles extends StatefulWidget {
String profilePic;
String message;
final date;
bool isReciever;
MessageBubbles({Key? key,
required this.profilePic,
required this.message,
required this.date,
required this.isReciever
}) : super(key: key);
#override
State<MessageBubbles> createState() => _MessageBubblesState();
}
class _MessageBubblesState extends State<MessageBubbles> {
#override
Widget build(BuildContext context) {
return widget.isReciever?Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CircleAvatar(
backgroundImage: NetworkImage(widget.profilePic),
),
FittedBox(
child: Container(
width: MediaQuery.of(context).size.width*0.6,
constraints: BoxConstraints(
maxWidth: double.infinity,
maxHeight: double.infinity
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only
(topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
color: Colors.grey.withOpacity(0.3),
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(widget.message),
),
),
),
Text(DateFormat.yMMMd().format(widget.date.toDate()),style: TextStyle(
color: secondaryColor,
fontSize: 12
),),
],
),
],
),
):
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(DateFormat.yMMMd().format(widget.date.toDate()).toString(),style: TextStyle(
color: secondaryColor,
fontSize: 12
),),
FittedBox(
child: Container(
width: MediaQuery.of(context).size.width*0.6,
constraints: BoxConstraints(
maxWidth: double.infinity,
maxHeight: double.infinity
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only
(topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10),
),
color: Colors.blueAccent,
),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(widget.message),
),
),
),
CircleAvatar(
backgroundImage: NetworkImage(widget.profilePic),
),
],
),
],
),
);
}
}
Here is the output snippet
Here is the firebase collection snippet
current stream's snapshot is List Type of Recieveruid // senderuid>recieveruid>snapshots()
and you get the user id from senderuid’s snapshots() ? feel something wrong....
I think you should set up a separate listener for senderuid

There is a problem that is rebuild when I click TextField() in Flutter

My code includes FutureBuilder(), which get data from Firestore, and its child widgets include GridView.builder and TextField widgets etc.
When I click on a TexField(focus), the codes in FutureBuilder are rebuild.
The following is the test code for this.
Can you tell me the cause and solution of this problem?
class TestRoom extends StatefulWidget {
TestRoom({Key? key}) : super(key: key);
#override
State<TestRoom> createState() => _TestRoomState();
}
class _TestRoomState extends State<TestRoom> {
List<RoomModel> _roomModels = [];
TextEditingController _textEditingController = TextEditingController();
bool _isTablet = false;
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
if (size.width >= 800) {
_isTablet = true;
} else {
_isTablet = false;
}
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Test",
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 20,
),
FutureBuilder(
future: _getAllRoom(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Expanded(
child: TestList1(
isTablet: _isTablet,
roomModels: _roomModels,
isListStyle1: true,
));
},
),
],
),
),
);
}
// get user's models from firestore
Future _getAllRoom() async {
_roomModels.clear();
_roomModels.addAll(await RoomService().getAllRoomModel("userName"));
}
}
//
//
class TestList1 extends StatefulWidget {
final isTablet;
final List<RoomModel> roomModels;
final bool isListStyle1;
TestList1({
Key? key,
required this.isTablet,
required this.roomModels,
required this.isListStyle1,
}) : super(key: key);
#override
State<TestList1> createState() => _TestList1State();
}
class _TestList1State extends State<TestList1> {
TextEditingController _textEditingController = TextEditingController();
double _paddingSize = 40.0;
#override
void dispose() {
// _textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.isTablet ? 6 : 3,
childAspectRatio: 1 / 1.5,
mainAxisSpacing: _paddingSize,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) {
return _buildMyRooms(widget.roomModels[index], index);
},
itemCount: widget.roomModels.length,
);
}
Widget _buildMyRooms(RoomModel roomModel, int index) {
return Column(
children: [
InkWell(
onTap: () {},
child: Container(
width: 120,
height: 168,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.blue,
width: 2.0,
),
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
),
),
SizedBox(
height: sm_padding,
),
PopupMenuButton<int>(
color: Colors.grey[100],
itemBuilder: (context) => _fileMenuItemLust(roomModel),
onSelected: _onSeletedFileMenu,
child: Column(
children: [
Container(
width: 130,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
roomModel.roomName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
color: Colors.blue,
fontSize: 15,
),
),
),
const Icon(
Icons.arrow_drop_down_outlined,
color: Colors.blue,
),
],
),
),
],
),
)
],
);
}
// 파일 메뉴 아이템
List<PopupMenuEntry<int>> _fileMenuItemLust(RoomModel roomModel) {
_textEditingController.text = roomModel.roomName;
return [
// 파일명
PopupMenuItem(
enabled: false,
// TODO textfield
child: TextField(
controller: _textEditingController,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
maxLines: 1,
decoration: InputDecoration(
border: _textFieldBorder(),
enabledBorder: _textFieldBorder(),
disabledBorder: _textFieldBorder(),
focusedBorder: _textFieldBorder(),
focusColor: Colors.white60,
filled: true,
fillColor: Colors.grey.withOpacity(0.3),
isDense: true, // padding 조절을 위해 추가
contentPadding: EdgeInsets.all(sm_padding),
)),
),
const PopupMenuDivider(),
PopupMenuItem(
value: 0,
child: Row(
children: [
const Icon(
Icons.copy,
),
SizedBox(
width: sm_padding,
),
const Text(
"Menu Item1",
),
],
),
),
];
}
void _onSeletedFileMenu(value) {}
OutlineInputBorder _textFieldBorder() {
return OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide(
color: Colors.grey.withOpacity(0.3),
width: 1,
),
);
}
}
// ======================
I modified the code based on Yeasin Sheikh's answer.
The existing problem has been solved, but a new problem has arisen.
I'm going to update the Firestore using onSubmitted() of TextField and then update my list using stream builder and future builder.
This is a modified part of the code above to solve an existing problem
class _TestRoomState extends State<TestRoom> {
late final myFuture = _getAllRoom(); // new
FutureBuilder(
future: myFuture, //_getAllRoom(), // change
The problem of constantly rebuilding the text field according to the focus has disappeared, but there is a problem that getAllRoom() is called only at the first time and cannot be called afterwards, so the new room list cannot be updated.
Here future: _getAllRoom() calls the api on every state changes.
Create a state variable for future
late final myFuture = _getAllRoom();
#override
Widget build(BuildContext context) {
And use
FutureBuilder(
future:myFuture ,
You can check Fixing a common FutureBuilder and StreamBuilder problem

Flutter - facing issue on defined context, build context

I'm new to Flutter for a month. Currently I'm facing below issue where showing undefined name 'context'. I have follow the suggestion to import 'dart:js' and it show error "The argument type 'JsObject' can't be assigned to the parameter type 'BuildContext'."
import 'package:dparking/reusable_widgets/reusable_widget.dart';
import 'package:dparking/screens/signup_screen.dart';
import 'package:flutter/material.dart';
class SignInScreen extends StatefulWidget {
const SignInScreen({Key? key}) : super(key: key);
#override
State<SignInScreen> createState() => _SignInScreenState();
}
class _SignInScreenState extends State<SignInScreen> {
final TextEditingController _passwordTextController = TextEditingController();
final TextEditingController _emailTextController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: const BoxDecoration(
gradient: LinearGradient(colors: [
Colors.blue,
Colors.green,
], begin: Alignment.topCenter, end: Alignment.bottomCenter)),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(
20, MediaQuery.of(context).size.height * 0.2, 20, 0),
child: Column(
children: <Widget>[
logoWidget("assets/images/DParking.png"),
const SizedBox(
height: 30,
),
reusableTextField("Enter UserName", Icons.person_outline, false,
_emailTextController),
const SizedBox(
height: 20,
),
reusableTextField("Enter Password", Icons.lock_outline, true,
_passwordTextController),
const SizedBox(
height: 20,
),
signInSignUpButton(context, true, () {}),
signUpOption()
],
),
),
),
),
);
}
}
Row signUpOption() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Don't have account?",
style: TextStyle(color: Colors.white70)),
GestureDetector(
onTap: () {
Navigator.push(context, //<- error on this line
MaterialPageRoute(builder: (context) => SignUpScreen()));
},
child: const Text(
" Sign Up",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
)
],
);
}
Please give me some advice on how can I solve this issue. thank you.
You should pass the context as the signUpOption argument.
signUpOption(context)
],
...
Row signUpOption(BuildContext context) {
...
}
Or move the function into the _SignInScreenState class for using the context of the State.

Flutter - Chat Screen built with a StreamBuilder showing messages multiple times

I am struggling with this Chat Screen. The app is meant to ask questions (not part of the below code) and the user either selects answers or types them. When the user types a first answer everything goes according to the plan and a first message is displayed. However the app then goes on displaying the second answer twice, the third one three times and so on.
I have been facing this issue for a few days and I cannot figure out why the app behaves the way it does. Could you please take a look at the code and suggest a way to fix this?
To give you some background information, this Chat Screen is part of a larger application. It should subscribe to a stream when the user opens the app. Then each message is pushed to the stream, whether it is a question asked by the bot or an answer given by the User. The system listens to the stream and displays a new message each time the stream broadcasts something, in our case the latest user input.
I am using a list of message models built from the stream to display the messages. For the purpose of asking this question I simplified the model to the extreme but in practice it has 23 fields. Creating this list of messages is the best solution I managed to think of but there may be a better way to handle this situation. Feel free to let me know if you know of any.
Here is the code that I am running.
import 'package:flutter/material.dart';
import 'dart:async';
StreamController<ChatMessageModel> _chatMessagesStreamController = StreamController<ChatMessageModel>.broadcast();
Stream _chatMessagesStream = _chatMessagesStreamController.stream;
const Color primaryColor = Color(0xff6DA7B9);
const Color secondaryColor = Color(0xffF0F0F0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat Screen',
home: ChatScreen(),
);
}
}
class ChatMessageModel {
final String message;
const ChatMessageModel({
this.message,
}
);
factory ChatMessageModel.turnSnapshotIntoListRecord(Map data) {
return ChatMessageModel(
message: data['message'],
);
}
#override
List<Object> get props => [
message,
];
}
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen9';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _messageTextController = TextEditingController();
String _userInput;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: secondaryColor,
appBar: AppBar(
title: Row(
children: [
Container(
padding: EdgeInsets.all(8.0),
child: Text('Chat Screen',
style: TextStyle(color: Colors.white,),
),
)
],
),
backgroundColor: primaryColor,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: primaryColor,
width: 1.0,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _messageTextController,
onChanged: (value) {
_userInput = value;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
hintText: 'Type your answer here',
// border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_messageTextController.clear();
debugPrint('Adding a ChatMessageModel with the message $_userInput to the Stream');
ChatMessageModel chatMessageModelRecord = ChatMessageModel(message: _userInput);
_chatMessagesStreamController.add(chatMessageModelRecord,);
},
child: Text(
'OK',
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatelessWidget {
List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
}
);
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(chatMessageModelRecord: _allMessagesContainedInTheStream[index]);
}
},
),
);
},
);
}
}
class UserChatBubble extends StatelessWidget {
final ChatMessageModel chatMessageModelRecord;
const UserChatBubble({
Key key,
#required this.chatMessageModelRecord,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5,),
child: Container(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 7 / 10,),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(15.0),
topLeft: Radius.circular(15.0),
),
color: primaryColor,
),
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 20,),
child: Text(chatMessageModelRecord.message,
style: TextStyle(
fontSize: 17,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
);
}
}
First of all, thank you for the interesting problem and functioning example provided. I had to do some small changes to convert it to "null-safety", but my code should work on your computer too.
The only problem you had initialization of _chatMessagesStream listener. You should do it only once and ideally in initState, to call it only once.
So here is the fix for you:
class MessagesStream extends StatefulWidget {
#override
_MessagesStreamState createState() => _MessagesStreamState();
}
class _MessagesStreamState extends State<MessagesStream> {
final List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
void initState() {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(
chatMessageModelRecord:
_allMessagesContainedInTheStream[index],
);
} else {
print(snapshot.connectionState);
return Container();
}
},
),
);
},
);
}
}
Also providing full code for null-safety just in case!
import 'package:flutter/material.dart';
import 'dart:async';
final StreamController<ChatMessageModel> _chatMessagesStreamController =
StreamController<ChatMessageModel>.broadcast();
final Stream<ChatMessageModel> _chatMessagesStream =
_chatMessagesStreamController.stream;
const Color primaryColor = Color(0xff6DA7B9);
const Color secondaryColor = Color(0xffF0F0F0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat Screen',
home: ChatScreen(),
);
}
}
class ChatMessageModel {
final String? message;
const ChatMessageModel({
this.message,
});
factory ChatMessageModel.turnSnapshotIntoListRecord(Map data) {
return ChatMessageModel(
message: data['message'],
);
}
List<Object> get props => [
message!,
];
}
class ChatScreen extends StatefulWidget {
static const String id = 'chat_screen9';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _messageTextController = TextEditingController();
String? _userInput;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: secondaryColor,
appBar: AppBar(
title: Row(
children: [
Container(
padding: EdgeInsets.all(8.0),
child: Text(
'Chat Screen',
style: TextStyle(
color: Colors.white,
),
),
)
],
),
backgroundColor: primaryColor,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MessagesStream(),
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: primaryColor,
width: 1.0,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _messageTextController,
onChanged: (value) {
_userInput = value;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
hintText: 'Type your answer here',
// border: InputBorder.none,
),
),
),
TextButton(
onPressed: () {
_messageTextController.clear();
debugPrint(
'Adding a ChatMessageModel with the message $_userInput to the Stream');
ChatMessageModel chatMessageModelRecord =
ChatMessageModel(message: _userInput);
_chatMessagesStreamController.add(
chatMessageModelRecord,
);
},
child: Text(
'OK',
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
);
}
}
class MessagesStream extends StatefulWidget {
#override
_MessagesStreamState createState() => _MessagesStreamState();
}
class _MessagesStreamState extends State<MessagesStream> {
final List<ChatMessageModel> _allMessagesContainedInTheStream = [];
#override
void initState() {
_chatMessagesStream.listen((streamedMessages) {
// _allMessagesContainedInTheStream.clear();
debugPrint('Value from controller: $streamedMessages');
_allMessagesContainedInTheStream.add(streamedMessages);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<ChatMessageModel>(
stream: _chatMessagesStream,
builder: (context, snapshot) {
return Expanded(
child: ListView.builder(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
itemCount: _allMessagesContainedInTheStream.length,
itemBuilder: (BuildContext context, int index) {
if (snapshot.hasData) {
return UserChatBubble(
chatMessageModelRecord:
_allMessagesContainedInTheStream[index],
);
} else {
print(snapshot.connectionState);
return Container();
}
},
),
);
},
);
}
}
class UserChatBubble extends StatelessWidget {
final ChatMessageModel chatMessageModelRecord;
const UserChatBubble({
Key? key,
required this.chatMessageModelRecord,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 5,
),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * 7 / 10,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(15.0),
topLeft: Radius.circular(15.0),
),
color: primaryColor,
),
padding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 20,
),
child: Text(
"${chatMessageModelRecord.message}",
style: TextStyle(
fontSize: 17,
// fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
);
}
}

FLUTTER : How to make container size of children

By definition, a container with children grows enough to show them. In this example that I am developing, I am not able to make the container fit the size of the children, I have to hardcode the Weight and Height, both, otherwise the container disappear (is the one with a red background, I put the whole code so you can copy-paste but it is only that one that I can not control the behaviour).
import 'package:flutter/material.dart';
import 'package:littlebusiness/logic/Category.dart';
import 'package:hive/hive.dart';
class FormCategoryPage extends StatefulWidget {
#override
_FormCategoryPageState createState() => _FormCategoryPageState();
}
class _FormCategoryPageState extends State<FormCategoryPage> {
final _formKey = GlobalKey<FormState>();
List<RadioModel> sampleData = new List<RadioModel>();
#override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(new RadioModel(true, 'A', 0xffe6194B));
sampleData.add(new RadioModel(false, 'B', 0xfff58231));
sampleData.add(new RadioModel(false, 'C', 0xffffe119));
sampleData.add(new RadioModel(false, 'D', 0xffbfef45));
sampleData.add(new RadioModel(true, 'A', 0xffe6194B));
sampleData.add(new RadioModel(false, 'B', 0xfff58231));
}
String _name;
Color _color;
String _selectedValue;
void addCategory(Category cat) {
Hive.box('categories').add(cat);
}
void getColor(String value) {
_selectedValue = value;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PERFORMANCE'),
),
body: Center(
child: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
// hintText: 'Enter your email',
labelText: 'Name',
),
onSaved: (value) => _name = value,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 100,
width: 320,
color: Colors.red,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
setState(() {
sampleData.forEach(
(element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: RadioItem(sampleData[index]),
);
},
),
),
],
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.red)),
child: Text('Add New Contact'),
color: Colors.teal,
textColor: Colors.white,
onPressed: () {
_formKey.currentState.save();
// final newContact = Contact(_name, int.parse(_age));
// addContact(newContact);
},
),
],
),
),
),
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(15.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 35.0,
width: 35.0,
alignment: Alignment.center,
child: Container(
height: 25.0,
width: 25.0,
decoration: BoxDecoration(
color: Color(_item.colorCode),
borderRadius:
const BorderRadius.all(const Radius.circular(15)),
)),
decoration: BoxDecoration(
color: Colors.transparent,
border: Border.all(
width: 3.0,
color: _item.isSelected
? Color(_item.colorCode)
: Colors.transparent),
borderRadius: const BorderRadius.all(const Radius.circular(25)),
),
),
Container(margin: EdgeInsets.only(left: 20.0))
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
final int colorCode;
RadioModel(this.isSelected, this.buttonText, this.colorCode);
}
This is the actuar result:
Anyone knows why it is happening that? I am lost, and giving a with of double.infinity does not work...
Thanks!
instead of using fixed values on wisth and height , you can use relative values to device by using
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height
you can also use them like
MediaQuery.of(context).size.width * 0.5
which means 50% of the device screen
hope it will help
You have to specified both width and height because your Listview is a child of a Column and a Row
You can replace your Listview by a Row of RadioItem in a SingleChildScrollView
Container(
color: Colors.red,
child: Builder(
builder: (context) {
var childs = <Widget>[];
for (var item in sampleData) {
childs.add(RadioItem(item));
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: childs,
),
);
},
),
),