The method 'getProducts' was called on null - flutter

i am trying to retrieve data using rest api from woocommerce website using flutter
this is the api for retrieve json data
Future<List<Product>> getProducts(String tagId) async {
List<Product> data = new List<Product>();
try {
String url = Config.url +
Config.productsURL +
"?consumer_key=${Config.key}&consumer_secret=${Config.secret}&tag=$tagId";
var response = await Dio().get(url,
options: new Options(
headers: {HttpHeaders.contentTypeHeader: "application/json"}));
if (response.statusCode == 200) {
data = (response.data as List).map((i) => Product.fromJson(i),).toList();
}
} on DioError catch (e) {
print(e.response);
}
return data;
}
this is the widget to handle the data to the mobile app
class WidgetHomeProducts extends StatefulWidget {
WidgetHomeProducts({Key key, this.labelName, this.tagId}) : super(key : key);
String labelName;
String tagId;
#override
_WidgetHomeProductsState createState() => _WidgetHomeProductsState();
}
class _WidgetHomeProductsState extends State<WidgetHomeProducts> {
APIServices apiServices;
#override
void initState() {
apiServices = new APIServices();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
color: const Color(0xffF4F7FA),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 16, top: 4),
child: Text(
this.widget.labelName,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
Padding(
padding: EdgeInsets.only(left: 16, top: 4),
child: FlatButton(
onPressed: () {},
child: Text(
'View All',
style: TextStyle(color: Colors.blueAccent),
),
),
),
],
),
_productList(),
],
),
);
}
Widget _productList(){
return new FutureBuilder(
future: apiServices.getProducts(this.widget.tagId),
builder: (BuildContext context, AsyncSnapshot<List<Product>> model){
if(model.hasData){
return _buildList(model.data);
}if(model.hasError){
print("error");
}
return Center(child: CircularProgressIndicator(),);
});
}
i got The method error message that says
'getProducts' was called on null.
Receiver: null
Tried calling: getProducts("971")
can anyone help me to fix this?

Related

Expected a value of type (String, dynamic) => MapEntry<dynamic, dynamic>, but got one of type(dynamic) => Builder

I'm trying to to call HTTP API to show the information of products on the Carousel Slider but it gets the error:
The following TypeErrorImpl was thrown building FutureBuilder<void>(dirty, state:
_FutureBuilderState<void>#bd897):
Expected a value of type '(String, dynamic) => MapEntry<dynamic, dynamic>', but got one of type
'(dynamic) => Builder'
The relevant error-causing widget was: FutureBuilder<void>
Could anyone help me to solve it, many thanks!
class HomePage extends StatefulWidget {
final List<String> items;
const HomePage({
Key? key,
required this.items,
}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int activeIndex = 0;
setActiveDot(index) {
setState(() {
activeIndex = index;
});
}
var data;
Future<void> getData() async {
final response = await http
.get(Uri.parse('https://berequirement.herokuapp.com/products'));
if (response.statusCode == 200) {
data = jsonDecode(response.body);
} else {}
}
List<Information> postList = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [Expanded(
child: FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: Text('Loading ...'));
} else {
return Stack(
children: <Widget>[
SizedBox(
height: 500,
width: 400,
// width: MediaQuery.of(context).size.width,
child: CarouselSlider(
options: CarouselOptions(
autoPlayCurve: Curves.fastLinearToSlowEaseIn,
autoPlayAnimationDuration:
const Duration(seconds: 1),
viewportFraction: 1.0,
onPageChanged: (index, ActiveDot) {
setActiveDot(index);
},
),
items: data.map((item) {
return Builder(
builder: (BuildContext context) {
return Column(
children: [
Text(
data['data'][item]['name'],
style: GoogleFonts.poppins(
fontWeight: FontWeight.bold,
fontSize: 22,
),
),
const SizedBox(height: 10),
Text(
"Item No . ${data['data'][item]['code']}",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
// fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.grey),
),
const SizedBox(height: 5),
Text(
"Item Type . ${data['data'][item]['type']}",
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
// fontWeight: FontWeight.bold,
fontSize: 13,
color: Colors.black),
),
Text(
("\$ ${data['data'][item]['price']}"),
style: GoogleFonts.poppins(
textStyle: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 22),
),
),
],
);
},
);
}).toList(),
),
),
Positioned(
left: 0,
right: 0,
bottom: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: List.generate(data.length, (idx) {
return activeIndex == idx
? const ActiveDot()
: const InactiveDot();
}
)
),
)
],
);
}
}),
),
])}
Looked a bit through the updated post and noticed the Future<void> return value of getData():
Future<void> getData() async {
final response = await http
.get(Uri.parse('https://berequirement.herokuapp.com/products'));
if (response.statusCode == 200) {
data = jsonDecode(response.body);
} else {}
}
To be able to use this in a FutureBuilder, I recommend that you return a value at the end of the funtion. For example:
Future<Map<String, dynamic>> getData() async {
final response = await http
.get(Uri.parse('https://berequirement.herokuapp.com/products'));
if (response.statusCode == 200) {
var thisData = jsonDecode(response.body);
return thisData;
} else {
return {};
}
}
And as such, modify the data in your FutureBuilder with snapshot.data.
EDIT: You can read more about FutureBuilders here.
EDIT2: Looked more through your code and encountered this:
...
data.map((item) {
return Builder(
builder: (BuildContext context)
...
There is no need for the Builder() widget. You can return the Column(). This should explain your error:
Expected a value of type '(String, dynamic) => MapEntry<dynamic, dynamic>', but got one of type
'(dynamic) => Builder'

Add new items in list without rerendering old items

I 'am writing a message application whenever I insert a new message at the 0th index in the list all the messages within the offset are rerendered even though their properties have not been changed. How can I stop this?
Below in the code, you can see The ChatBook class which renders MessageList which is a a ScrollablePositionedListView.
I opted this in order to reach tagged messages instantly. As soon as I press the send button which is in InputBar The onPressSend Method is called which adds new message in the _messages array also I have provided _messages array inside InheritedWidget so that i can inherit it within the child since it required at multiple places. Whenever i add a new message simple a textmessage it re renders all the previous messages even though there properties have not been changed. Below are the required code snippets.
ChatBook class
/// This is the entry point of the [ChatBook] package.
class ChatBook extends StatefulWidget {
const ChatBook({
Key? key,
required this.author,
required this.onSendMessage,
this.theme = const DefaultChatTheme(),
this.giphyApiKey,
}) : super(key: key);
/// List of messages from which messagefor [Ayesavi ChatBook] will be retrieved by default this field is provided [DefaultChatTheme]
/// Theme assigned to ChatBook for info see [ChatTheme]
final ChatTheme theme;
/// callback for onPress event on sendMessageButton in inputbar [InputBar]
final void Function(Message currentMessage) onSendMessage;
/// GiphyApiKey required to retrieve giphy from servers.
final String? giphyApiKey;
final User author;
#override
State<ChatBook> createState() => _ChatBookState();
}
class _ChatBookState extends State<ChatBook> {
final List<Message> _messages = [];
/// Initialising ItemScrollController
/// It is needed for jumpTo and scrollTo methods to reach any particular item
final ItemScrollController itemScrollController = ItemScrollController();
/// Initialising ItemPositionListner
/// Listens for the position of any item.
final ItemPositionsListener itemPositionsListener =
ItemPositionsListener.create();
final TagMessageHelper _tagMessageHelper = TagMessageHelper();
/// It is for holding the selectedGif after onPressedEvent on Gif
/// GiphyCient is later initilising the [GiphyClient] in the [initState] method.
late GiphyClient client;
/// System used to generate random id for the user at the runtime by default initialised with aan empty String
String randomId = "";
/// For holding giphy Api Key
//! Store API keys in the env mode separate it is advised so to keep your API keys private to you only
#override
void initState() {
super.initState();
if (widget.giphyApiKey != null) {
client = GiphyClient(apiKey: widget.giphyApiKey!, randomId: '');
WidgetsBinding.instance.addPostFrameCallback((_) {
client.getRandomId().then((value) {
setState(() {
randomId = value;
});
});
});
}
}
void onPressSend(
Message sendingMessage,
) {
setState(() {
_messages.insert(0, sendingMessage);
});
widget.onSendMessage.call(sendingMessage.copyWith(
self: false,
));
}
#override
Widget build(BuildContext context) {
return GiphyGetWrapper(
/// GiphyGetWrapper is used to get giphys where ever called in the application.
giphy_api_key: widget.giphyApiKey!,
builder: (stream, giphyGetWrapper) {
stream.listen((gif) {
// widget.onGiphyPressed?.call(gif);
onPressSend(GifMessage(
author: const User(id: ""),
id: '',
gif: gif,
createdAt: DateTime.now().millisecondsSinceEpoch,
self: true,
status: Status.seen));
});
return InheritedProperties(
tagHelper: _tagMessageHelper,
theme: widget.theme,
giphyGetWrapper: giphyGetWrapper,
author: widget.author,
//* Needed for inheriting acess of messages to all its child widgets.
child: InheritedMessagesWidget(
messages: _messages,
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 18.00,
),
child: MessageList(
controller: itemScrollController,
positionsListener: itemPositionsListener,
),
),
),
const SizedBox(
height: 10,
),
// RepaintBoundary(
// child:
ValueListenableBuilder(
valueListenable: _tagMessageHelper.tagNotifier,
builder: (_, value, __) {
if (value == null) return const SizedBox();
return TaggedMessageIndicator(
message: value as Message);
}),
// ),
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 150, minHeight: 60),
child: InputBar(
giphyGetWrapper: giphyGetWrapper,
onSendMessage: onPressSend,
))
],
),
),
);
});
}
}
MessageList
class MessageList extends StatefulWidget {
const MessageList(
{Key? key, required this.controller, required this.positionsListener})
: super(key: key);
final ItemScrollController controller;
final ItemPositionsListener positionsListener;
#override
State<MessageList> createState() => _MessageListState();
}
class _MessageListState extends State<MessageList> {
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return ScrollablePositionedList.builder(
reverse: true,
itemScrollController: widget.controller,
itemPositionsListener: widget.positionsListener,
initialScrollIndex: 0,
itemCount: InheritedMessagesWidget.of(context).messages.length,
itemBuilder: (_, index) {
return MessageBuilder(
message: InheritedMessagesWidget.of(context).messages[index],
prevMessage:
index != InheritedMessagesWidget.of(context).messages.length - 1
? InheritedMessagesWidget.of(context).messages[index + 1]
: null,
);
},
scrollDirection: Axis.vertical,
);
}
}
MessageBuilder
class MessageBuilder extends StatefulWidget {
const MessageBuilder({Key? key, required this.message, this.prevMessage})
: super(key: key);
final Message message;
final Message? prevMessage;
#override
State<MessageBuilder> createState() => _MessageBuilderState();
}
class _MessageBuilderState extends State<MessageBuilder> {
#override
Widget build(BuildContext context) {
return Column(
children: [
_dateProvider(widget.message, widget.prevMessage),
Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Container(
width: double.infinity,
alignment:
widget.message.self != null && widget.message.self == true
? Alignment.centerRight
: Alignment.centerLeft,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
LimitedBox(
maxWidth: 6.5 / 10 * (MediaQuery.of(context).size.width),
child: IntrinsicWidth(
child: RepaintBoundary(
child: Swipeable(
maxOffset: .7,
movementDuration: const Duration(milliseconds: 500),
background: const Align(
alignment: Alignment.centerLeft,
child: Icon(
Icons.share,
size: 20,
color: Colors.white,
)),
direction: SwipeDirection.startToEnd,
onSwipe: (dir) {
if (SwipeDirection.startToEnd == dir) {
logger.log("swiped");
InheritedProperties.of(context)
.tagHelper
.tagNotifier
.value = widget.message;
}
},
confirmSwipe: (direction) async {
logger.log((SwipeDirection.startToEnd == direction)
.toString());
return SwipeDirection.startToEnd == direction;
},
allowedPointerKinds: const {
PointerDeviceKind.mouse,
PointerDeviceKind.stylus,
PointerDeviceKind.touch,
PointerDeviceKind.trackpad,
PointerDeviceKind.unknown,
},
key: const ValueKey(1),
child: Bubble(
padding: const BubbleEdges.all(0),
showNip: _nipGiver(widget.message, widget.prevMessage),
nip: widget.message.self == true
? BubbleNip.rightTop
: BubbleNip.leftTop,
color: _bubbleColorGiver(widget.message),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_messageProviderWidget(widget.message)!,
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
DateFormat.jm().format(
DateTime.fromMillisecondsSinceEpoch(
widget.message.createdAt!)),
style: widget.message.self == true
? InheritedProperties.of(context)
.theme
.sentTimeTextStyle
: InheritedProperties.of(context)
.theme
.receivedTimeTextStyle),
const SizedBox(
width: 5,
),
if (widget.message.self == true)
_statusProvider(widget.message),
]),
),
],
),
),
),
)),
),
],
),
),
),
],
);
}
Widget? _messageProviderWidget(Message message) {
MessageType type = message.type;
switch (type) {
case MessageType.text:
return TextMessageWidget(
message: message as TextMessage,
);
case MessageType.gif:
return GifMessageWidget(message: message as GifMessage);
case MessageType.audio:
message as AudioMessage;
return AudioMessageWidget(message: message);
default:
return null;
}
}
bool? _nipGiver(Message currentMessage, Message? prevMessage) {
if (prevMessage != null &&
sameDay(currentMessage.createdAt!, prevMessage.createdAt) == true &&
currentMessage.self == prevMessage.self) {
return false;
} else {
return true;
}
}
Widget _dateProvider(Message currentMessage, Message? prevMessage) {
if (prevMessage != null &&
sameDay(currentMessage.createdAt!, prevMessage.createdAt) == false) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Text(
DateFormat('dd MMM yyyy').format(
DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
),
);
} else if (prevMessage == null) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
DateFormat('dd MMM yyyy').format(
DateTime.fromMicrosecondsSinceEpoch(currentMessage.createdAt!)),
style: InheritedProperties.of(context).theme.dateHeaderTextStyle,
),
);
} else {
return const SizedBox();
}
}
Widget _statusProvider(Message message) {
switch (message.status) {
case Status.seen:
return SvgPicture.asset(
'assets/double_tick.svg',
color: Colors.blue,
height: 10,
width: 10,
);
case Status.delivered:
return SvgPicture.asset(
'assets/double_tick.svg',
color: Colors.grey,
height: 10,
width: 10,
);
case Status.error:
return const Icon(Icons.error_outline, color: Colors.red, size: 18);
case Status.sending:
return const SizedBox(
height: 10, width: 10, child: CupertinoActivityIndicator());
case Status.sent:
return SvgPicture.asset(
'asset/single_tick.svg',
color: Colors.grey,
height: 10,
width: 10,
);
default:
return const SizedBox();
}
}
Color _bubbleColorGiver(Message message) {
if (["text", 'audio', 'video'].contains(message.type.name)) {
return widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBubbleColor
: InheritedProperties.of(context).theme.receivedMessageBubbleColor;
} else if (message.type.name == 'gif') {
return Colors.transparent;
} else {
return Colors.transparent;
}
}
}
TextMessageWidget
class TextMessageWidget extends StatefulWidget {
const TextMessageWidget({
Key? key,
required this.message,
}) : super(key: key);
final TextMessage message;
#override
State<TextMessageWidget> createState() => _TextMessageWidgetState();
}
class _TextMessageWidgetState extends State<TextMessageWidget> {
#override
Widget build(BuildContext context) {
final bodyTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageTextStyle
: InheritedProperties.of(context).theme.receivedMessageTextStyle;
final boldTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBoldTextStyle
: InheritedProperties.of(context).theme.receivedMessageBoldTextStyle;
final codeTextStyle = widget.message.self == true
? InheritedProperties.of(context).theme.sentMessageBodyCodeTextStyle
: InheritedProperties.of(context)
.theme
.receivedMessageBodyCodeTextStyle;
RegExp exp = RegExp(r'(?:(?:https?|ftp):)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
String? _urlGiver(String text) {
String? urlText;
Iterable<RegExpMatch> matches = exp.allMatches(text);
for (var match in matches) {
urlText = (text.substring(match.start, match.end));
}
return urlText;
}
_urlGiver(widget.message.text);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_urlGiver(widget.message.text) != null)
AnyLinkPreview.builder(
placeholderWidget: const SizedBox(
height: 0,
width: 0,
),
errorWidget: const SizedBox(
height: 0,
width: 0,
),
link: _urlGiver(widget.message.text)!,
itemBuilder: (_, metadata, image) {
return GestureDetector(
onTap: () {
if (metadata.url != null) {
launchUrl(Uri.parse(metadata.url!));
}
},
child: Card(
clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (image != null) ...[
ClipRRect(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(5),
bottomLeft: Radius.circular(5)),
child: Image(image: image),
),
],
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
metadata.title!,
style: const TextStyle(
fontWeight: FontWeight.bold),
),
if (metadata.desc != null &&
metadata.desc != '' &&
metadata.desc !=
'A new Flutter project.') ...[
const SizedBox(height: 10),
Text(metadata.desc!)
]
],
),
),
],
),
),
);
}),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ParsedText(
selectable: true,
text: widget.message.text,
style: bodyTextStyle,
parse: [
MatchText(
onTap: (mail) async {
final url = Uri(scheme: 'mailto', path: mail);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
},
pattern:
r'([a-zA-Z0-9+._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)',
style: const TextStyle(
decoration: TextDecoration.underline,
),
),
MatchText(
onTap: (urlText) async {
final protocolIdentifierRegex = exp;
if (!urlText.startsWith(protocolIdentifierRegex)) {
urlText = 'https://$urlText';
}
{
final url = Uri.tryParse(urlText);
if (url != null && await canLaunchUrl(url)) {
await launchUrl(
url,
mode: LaunchMode.externalApplication,
);
}
}
},
pattern:
r'((http|ftp|https):\/\/)?([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,#?^=%&:/~+#-]*[\w#?^=%&/~+#-])?',
style: const TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
MatchText(
pattern: PatternStyle.bold.pattern,
style: boldTextStyle ??
bodyTextStyle.merge(PatternStyle.bold.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.bold.from,
PatternStyle.bold.replace,
),
},
),
MatchText(
pattern: PatternStyle.italic.pattern,
style: bodyTextStyle.merge(PatternStyle.italic.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.italic.from,
PatternStyle.italic.replace,
),
},
),
MatchText(
pattern: PatternStyle.lineThrough.pattern,
style:
bodyTextStyle.merge(PatternStyle.lineThrough.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.lineThrough.from,
PatternStyle.lineThrough.replace,
),
},
),
MatchText(
pattern: PatternStyle.code.pattern,
style: codeTextStyle ??
bodyTextStyle.merge(PatternStyle.code.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.code.from,
PatternStyle.code.replace,
),
},
),
MatchText(
pattern: PatternStyle.at.pattern,
style: codeTextStyle ??
bodyTextStyle.merge(PatternStyle.at.textStyle),
renderText:
({required String str, required String pattern}) => {
'display': str.replaceAll(
PatternStyle.code.from,
PatternStyle.code.replace,
),
},
),
],
)
// SelectableLinkify(text: widget.message.text,onOpen: (element){
// _launchInBrowser(Uri.parse(element.url));
// },),
),
],
);
}
}

Failed assertion: line 1916 pos 14: 'children != null': is not true.?

I have been trying to run a flutter app and it keeps giving me Failed assertion error. below is my code. I just want to show the list the second code
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
location: doc['location'],
description: doc['description'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
// if no likes, return 0
if (likes == null) {
return 0;
}
int count = 0;
// if the key is explicitly set to true, add a like
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
location: this.location,
description: this.description,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
Int likeCount;
Map likes;
_PostState({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
this.likeCount,
});
Widget postingHeading() {
return FutureBuilder(
future:
FirebaseFirestore.instance.collection('User').doc(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return ListTile(
onTap: () => print('go check the url'),
leading: CircleAvatar(
backgroundImage:
NetworkImage("${snapshot.data.data()['photoUrl']}"),
),
title: Text('${snapshot.data.data()['username']}'),
subtitle: Text(location),
trailing: IconButton(
onPressed: () => print('deleting post'),
icon: Icon(Icons.more_vert),
),
);
});
}
Widget postImagePicture() {
return Stack(
alignment: Alignment.center,
children: [
Image.network(mediaUrl),
],
);
}
Widget postDetailsComment() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {}),
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {})
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$likeCount likes",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
children: [
Text(username),
SizedBox(
width: 1,
),
Flexible(child: Text(description))
],
)
],
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
postingHeading(),
postImagePicture(),
postDetailsComment(),
],
);
}
}
here is where I convert it to List... I don't even no what is wrong.... please help.... thanks in advance
class Profile extends StatefulWidget {
final String currentUser;
Profile({this.currentUser});
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final _firestore = FirebaseFirestore.instance;
int postLenght;
List<Post> post;
bool pleaseWait;
#override
void initState() {
super.initState();
getUsersPicsDetails();
}
getUsersPicsDetails() async {
setState(() {
pleaseWait = false;
});
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
}
fellowEditButtton({String test, Function press}) {
return TextButton(
onPressed: press,
child: Container(
alignment: Alignment.center,
height: 25,
width: 250,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(),
borderRadius: BorderRadius.circular(8.0)),
child: Text(
test,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
)),
);
}
gettingUserprofile() {
return StreamBuilder(
stream: _firestore.collection('User').doc(widget.currentUser).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState != ConnectionState.active) {
return Text('this stuff en');
}
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) => Container(
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FullScreenWidget(
child: Hero(
tag: 'smallImage',
child: CircleAvatar(
backgroundImage: NetworkImage(
'${snapshot.data.data()['photoUrl']}'),
radius: 50.0,
),
),
),
Expanded(
flex: 1,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
topColum(buttom: 'Post', number: 1),
topColum(buttom: 'Fellowers', number: 2),
topColum(buttom: 'Following', number: 0),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0,
),
child: widget.currentUser ==
_auth.currentUser.uid
? fellowEditButtton(
test: 'Edit Profile',
press: profileEditingButton)
: fellowEditButtton(
test: 'Fellow', press: () {})),
],
),
],
),
),
],
),
Divider(),
//! this is i call the list
//getUserPicture(),
getUserPicture(),
],
),
));
},
);
}
profileEditingButton() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EditProfile(
userProfile: widget.currentUser,
)));
}
Widget topColum({int number, String buttom}) {
return Column(
children: [
Text(
'$number',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Text(
buttom,
style: TextStyle(
fontSize: 20,
),
),
],
);
}
//! this is the where i return the list
Widget getUserPicture() {
if (pleaseWait = false) {
return CircularProgressIndicator();
}
return Column(
children: post,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, titleString: "Profile"),
body:
//Text('${widget.currentUser}')
gettingUserprofile());
// userPictursPost(),
}
}
I want to the get the post at the bottom... thanks in advance
below is the error image
enter image description here
When you get an error, please post it along with your question. When you are getting an error, it means that something is wrong with your code,and most likely not the flutter engine. Both are important for debugging, the error+your code.
Try changing this
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
into this:
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
if(_getPost.docs.isNotEmpty){
List<Post> tempPost = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
setState(() {
pleaseWait = true;
post =tempPost
print(postLenght);
});
}else{
print('The List is empty);}
You are not checking if the Query result has data or not. If it's empty, you will pass an empty List post down your tree, and you will get the error you are having.
For people facing similar issues, let me tell what I found in my code:
The error says that the children is null, not empty !
So if you are getting the children for the parent widget like Row or Column from a separate method, just check if you are returning the constructed child widget from the method.
Row(
children: getMyRowChildren()
)
.
.
.
getMyRowChildren(){
Widget childWidget = ... //constructed
return childWidget; //don't forget to return!
}
Else it would return null, which results in the children being null and we get the mentioned above error!

Flutter Argument passing - Getting a null from routeArgs

I have the following setup of my flutter app:
HomeScreen.Dart
Widget build(BuildContext context) {
return Scaffold(
appBar: homeAppBar(context),
bottomNavigationBar: BottomNavBar(),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CategoryList(),
ItemList(),
],
),
));
}
}
The CategoryList class contains a list view of category items. When a category item is clicked, the associated products should be displayed for that item. The way I do that is by passing the category name to the product item list class. Please see below:
CategoryItem.dart
class CategoryItemN extends StatelessWidget {
final String title;
const CategoryItemNew({
#required this.title,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => selectCategory(context),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Column(
children: <Widget>[
Text(
title,
style: isActive
? TextStyle(
color: kTextColor,
fontWeight: FontWeight.bold,
)
: TextStyle(fontSize: 12),
),
if (isActive)
Container(
margin: EdgeInsets.symmetric(vertical: 5),
height: 3,
width: 22,
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius: BorderRadius.circular(10),
),
),
],
),
),
);
}
void selectCategory(BuildContext ctx) {
Navigator.of(ctx).pushNamed(
ItemList.routeName,
arguments: {
'title': title ==== > This is the category name
},
);
}
}
ItemList.dart
class _ItemListState extends State<ItemList> {
var _isInit = true;
var _isLoading = false;
var title = '';
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
title = routeArgs['title']; ===================> This is where the error occurs!
Provider.of<Products>(context).fetchProducts(title, true).then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
Here's the error message:
The following NoSuchMethodError was thrown building Listener: The method '[]' was called on null. Receiver: null Tried calling []("title")
You can get arguments through 'ModalRoute.of(context).settings.arguments' in the build().
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
There is a official example about 'Pass arguments to a named route'.
https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments

Unhandled Exception: Bad state: Cannot add new events after calling close?

I'm making a login feature, I'm making fire responses to find out whether responses were successful or failed. can see my BLOC coding. the login process was successful but when I want to return to the login page after logging out the error appears Unhandled Exception: Bad state: Cannot add new events after calling close. how can I handle it?
API RESPONSES :
class ApiResponse<T> {
Status status;
T data;
String message;
ApiResponse.loading(this.message) : status = Status.LOADING;
ApiResponse.completed(this.data) : status = Status.COMPLETED;
ApiResponse.error(this.message) : status = Status.ERROR;
// #override
// String toString() {
// return "Status : $status \n Message : $message \n Data : $data";
// }
}
enum Status { LOADING, COMPLETED, ERROR }
BLOC :
class LoginBloc extends Object with Validators{
final _repository = EresidenceRepository();
final _userid = BehaviorSubject<String>();
final _password = BehaviorSubject<String>();
final _imei = BehaviorSubject<String>();
final _coordinate = BehaviorSubject<String>();
final BehaviorSubject<ApiResponse<login_responses>> _subject = BehaviorSubject<ApiResponse<login_responses>>();
Function(String) get userid => _userid.sink.add;
Function(String) get password => _password.sink.add;
Function(String) get imei => _imei.sink.add;
Function(String) get coordinate => _coordinate.sink.add;
Stream<String> get useridValidation => _userid.stream.transform(useridValidator);
Stream<String> get passwordValidation => _password.stream.transform(passwordValidator);
Stream<bool> get submitCheck => Rx.combineLatest2(useridValidation, passwordValidation, (e,p) => true);
login() async {
_subject.sink.add(ApiResponse.loading("Logging In..."));
try {
login_responses response = await _repository.login(
_userid.value, _password.value, _imei.value, _coordinate.value);
prefsBloc.changePrefsLogin(
PrefsState(false, response.data.userid, response.data.password, _imei.value, _coordinate.value, "")
);
_subject.sink.add(ApiResponse.completed(response));
print(response);
} catch (e) {
_subject.sink.add(ApiResponse.error(e.toString()));
print(e);
}
}
dispose(){
_userid.close();
_password.close();
_imei.close();
_coordinate.close();
_subject.close();
}
BehaviorSubject<ApiResponse<login_responses>> get subject => _subject;
}
final login = LoginBloc();
UI :
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver{
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
FocusNode passwordFocusNode, useridFocusNode;
#override
void initState() {
super.initState();
prefsBloc.checkLoginPref(context);
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.dark,
child: Scaffold(
backgroundColor: Colors.white,
body: StreamBuilder(
stream: login.subject,
builder: (context, AsyncSnapshot<ApiResponse<login_responses>> snapshot){
if(snapshot.hasData) {
print(snapshot.data.status);
switch (snapshot.data.status) {
case Status.LOADING:
_onWidgetDidBuild((){
Scaffold.of(context).showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(snapshot.data.message),
CircularProgressIndicator(),
],
),
backgroundColor: Colors.black,
),
);
});
break;
case Status.COMPLETED:
login_responses result = snapshot.data.data;
if(result.data.bit70 == "000") {
_onWidgetDidBuild((){
login.dispose();
AppRoutes.replace(context, LoginVerifyPage());
});
}else{
_onWidgetDidBuild((){
login.dispose();
AppRoutes.replace(context, MainApp());
});
}
break;
case Status.ERROR:
_onWidgetDidBuild(() {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('${snapshot.data.message}'),
backgroundColor: Colors.red,
));
});
break;
}
}
return _formLogin();
}
),
)
);
}
_formLogin() {
return SafeArea(
child: Container(
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.symmetric(horizontal: SizeConfig.widthMultiplier * 1, vertical: SizeConfig.heightMultiplier * 1),
child: CachedNetworkImage(
imageUrl: "https://images.glints.com/unsafe/1024x0/glints-dashboard.s3.amazonaws.com/company-logo/68545821966f833d182f98775c73c7ae.png",
errorWidget: (context, url, error) => Icon(Icons.broken_image),
fit: BoxFit.fill,
),
)
),
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.all(SizeConfig.heightMultiplier * 2),
child: Column(
children: <Widget>[
Container(
child: StreamBuilder<String>(
stream: login.useridValidation,
builder: (context, snapshot) => DataTextField(
errorText: snapshot.error,
hintText: "No Handphone",
textInputAction: TextInputAction.next,
icon: Icons.phone,
onSubmitted: () => FocusScope.of(context).requestFocus(passwordFocusNode),
onChanged: login.userid,
keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true),
),
)
),
Container(
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2),
child: StreamBuilder<String>(
stream: login.passwordValidation,
builder: (context, snapshot) => PasswordTextField(
errorText: snapshot.error,
hintText: "Password",
textInputAction: TextInputAction.done,
onSubmitted: () {
FocusScope.of(context).requestFocus(FocusNode());
},
onChanged: login.password,
focusNode: passwordFocusNode,
),
)
),
Container(
width: SizeConfig.screenWidth,
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 2.5),
child: GestureDetector(
onTap: () => AppRoutes.push(context, ForgotPasswordPage()),
child: Text(
Strings.titleForgotPass+" ?",
style: AppTheme.styleSubTitlePurpel,
textAlign: TextAlign.right,
),
)
),
Container(
width: SizeConfig.screenWidth,
margin: EdgeInsets.only(top: SizeConfig.heightMultiplier * 5),
child: StreamBuilder<bool>(
stream: login.submitCheck,
builder: (context, snapshot) => AppButton(
onPressed: snapshot.hasData ? () => login.login() : null,
text: Strings.signin
),
)
)
],
)
)
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(bottom: SizeConfig.heightMultiplier * 2.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
Strings.dontAccount,
style: AppTheme.styleSubTitleBlackSmall,
textAlign: TextAlign.right,
),
Container(
margin: EdgeInsets.only(left: SizeConfig.widthMultiplier * 1),
child: InkWell(
onTap: () => AppRoutes.push(context, RegistrationPage()),
child: Text(
Strings.registration,
style: AppTheme.styleSubTitlePurpel,
textAlign: TextAlign.right,
),
)
)
],
)
)
)
],
),
),
);
}
void _onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
}
The problem is that you have created an instance of your bloc globally (which is not a good practice), and after the login process is done you have called login.dispose() which closes all of the streams in your LoginBloc, and you can't add new events to closed streams.
You'd better create an instance of your LoginBloc in your LoginPage initState method, and close it in the dispose method.
This way, whenever you navigate to login page, a new bloc is created and it would work as expected.
UPDATE:
A simple example:
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
LoginBloc _loginBloc;
#override
void initState() {
super.initState();
_loginBloc = LoginBloc();
}
#override
void dispose() {
_loginBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container();
}
}