ViewModelBuilder returning variable null. Not retrieving state - flutter

I am refactor app to use stacked but have issue. State not seem to be pass correct between widget.
I added ViewModelBuilder at top of the widget tree that I want to affect.
return ViewModelBuilder<vehicleData>.reactive(
builder: (context, model, child){
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: InkWell(
splashColor: Colors.blueGrey,
child: Card(
//color: Colors.blueGrey,
child: ListTile(
leading: AspectRatio(
aspectRatio: 0.8,
child: Image.network(
//data.images,
snapshot.data.documents[index]['icon'],
height: 500,
//width: 300,
fit: BoxFit.contain,
)
),
title: Text(
//data.make,
snapshot.data.documents[index]['make'],
textAlign: TextAlign.start,
style: TextStyle(fontSize: 18.0,
fontWeight: FontWeight.bold,),
),
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context) =>
//ProformaScreen(data.make),
ProformaScreen(snapshot.data.documents[index]['make']),
),
);
model.setModel(snapshot.data.documents[index]['model']);
} ,
),
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.blueGrey),
borderRadius: BorderRadius.circular(10),
),
),
),
);
},
viewModelBuilder: ()=> vehicleData(),
);
This calls model.setModel and assign the model based on the selected car.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:core';
import 'package:stacked/stacked.dart';
class vehicleData extends BaseViewModel{
String _make;
String _images;
var _model;
setModel(var model){
_model = model;
print ('This is the Model of the Car \n');
print (_model);
notifyListeners();
}
setMake(String make){
_make = make;
notifyListeners();
}
setImages(String images){
_images = images;
notifyListeners();
}
get getModel => print('This is within the Get ' + _model);
get getMake => _make;
get getImage => _images;
}
Both print statement return correct model I still can see them.
But when I try call using ViewModelBuilder in another page model return null:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:gerente_loja/core/services/vehicle_data.dart';
import 'package:stacked/stacked.dart';
class menuModel extends StatefulWidget {
#override
_menuModelState createState() => _menuModelState();
}
class _menuModelState extends State<menuModel> {
var carModel;
#override
Widget build(BuildContext context) {
String selectModel;
return ViewModelBuilder<vehicleData>.reactive(
builder: (context, modelView, child){
return Padding(
padding: const EdgeInsets.only(left: 16.0, right: 16.0),
child: Container(
decoration: BoxDecoration(border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(25.0)
),
padding: EdgeInsets.only(left: 50.0, right: 16.0),
child: DropdownButton(
hint: Text('Ano'),
dropdownColor: Colors.transparent,
elevation: 5,
icon: Icon(Icons.arrow_drop_down),
isExpanded: true,
iconSize: 36.0,
value: selectModel,
onChanged: (value){
setState(() {
print (modelView.getModel);
selectModel = value;
});
},
items: carModel.map((value) {
return DropdownMenuItem (
value: value,
child: Text(value));
}).toList(),
),
),
);
},
viewModelBuilder: ()=> vehicleData(),
);
}
}
print (modelView.getModel); is null.
Anyone know why model return null? I have look hard to solve but no idea why.

Related

How to solve: type 'List<dynamic>' is not a subtype of type 'String' [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I want to update image from listofTaskNotApprove class which it will pass the object of documentsnapshot into the EditTaskNotApprove. Before I update the image, I need to display specific of image where the user will be select specific info from listofTaskNotApprove. The problem is how to display the current index of image into the new screen?
ListOfTaskNotAccepted class.
import 'package:carousel_pro/carousel_pro.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:fyp/screen/RecordOfficer/EditTaskNotApprove.dart';
import 'package:fyp/shared/Loading.dart';
import 'package:google_fonts/google_fonts.dart';
class ListOfTaskNotAccepted extends StatefulWidget {
#override
_ListOfTaskNotAcceptedState createState() => _ListOfTaskNotAcceptedState();
}
final FirebaseAuth auth = FirebaseAuth.instance;
Stream<QuerySnapshot> getUser(BuildContext context) async* {
final FirebaseUser rd = await auth.currentUser();
yield* Firestore.instance.collection("Task").where('uid',isEqualTo: rd.uid).where("verified", isEqualTo: 'TidakSah').snapshots();
}
class _ListOfTaskNotAcceptedState extends State<ListOfTaskNotAccepted> {
List<NetworkImage> _listOfImages = <NetworkImage>[];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Aduan Tidak Diterima"),
backgroundColor: Colors.redAccent,
),
body: Container(
child: StreamBuilder(
stream: getUser(context),
builder: (context, snapshot){
if (snapshot.hasError || !snapshot.hasData) {
return Loading();
} else{
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index){
DocumentSnapshot da = snapshot.data.documents[index];
_listOfImages =[];
for(int i =0; i <da['url'].length; i++){
_listOfImages.add(NetworkImage(da['url'][i]));
}
return Card(
child:ListTile(
title: Container(
alignment: Alignment.centerLeft,
child: Column(
children: <Widget>[
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Sumber Aduan: ", style: GoogleFonts.asap(fontWeight: FontWeight.bold)),
Text(da['sumberAduan'], style: GoogleFonts.asap(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Nombor Aduan: ", style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
Text(da['noAduan'], style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Lokasi: ", style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
Text(da['kawasan'] + " " + da['naJalan'], style: GoogleFonts.lato(fontWeight: FontWeight.bold)),
],
),
),
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Kategori: ", style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
Text(da['kategori'], style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
],
),
),
Column(
children: [
Container(
margin: EdgeInsets.all(10.0),
height: 200,
decoration: BoxDecoration(
color: Colors.white
),
width: MediaQuery.of(context).size.width,
child: Carousel(
boxFit: BoxFit.cover,
images: _listOfImages,
autoplay: false,
indicatorBgPadding: 5.0,
dotPosition: DotPosition.bottomCenter,
animationCurve: Curves.fastLinearToSlowEaseIn,
animationDuration: Duration(milliseconds: 2000),
),
)
],
)
],
),
),
subtitle: Container(
child: Column(
children: [
SizedBox(height: 5.0),
Container(alignment: Alignment.centerLeft,
child: Row(
children: [
Text("Catatan: ", style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
Text(da['comments'], style: GoogleFonts.arimo(fontWeight: FontWeight.w500)),
],
),
),
],
),
),
onTap: () {Navigator.push(context, MaterialPageRoute(builder: (context) => EditTask(da:da)));}
)
);
});
}
}),
)
);
}
}
Here is EditTask class which I need to display current index of image that selected by user.
import 'package:carousel_pro/carousel_pro.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class EditTask extends StatefulWidget {
final DocumentSnapshot da;
const EditTask({Key key, this.da}) : super(key: key);
#override
_EditTaskState createState() => _EditTaskState(da);
}
class _EditTaskState extends State<EditTask> {
DocumentSnapshot da;
_EditTaskState(DocumentSnapshot da){
this.da = da;
}
TextEditingController _noAduan;
TextEditingController _sumberAduan;
TextEditingController _kategori;
DateTime myDateTime = DateTime.now();
#override
void initState(){
super.initState();
_noAduan = TextEditingController(text: widget.da.data['noAduan']);
_sumberAduan =TextEditingController(text: widget.da.data['sumberAduan']);
_kategori = TextEditingController(text: widget.da.data['kategori']);
myDateTime = (da.data['date']).toDate();
_listOfImages = NetworkImage(da.data['url']) as List<NetworkImage>; // this line show the error
}
List <String> sumber = <String> ['Sistem Aduan MBPJ', 'Sistem Aduan Waze', 'Sistem Aduan Utiliti'];
List <String> kate = <String> ['Segera', 'Pembaikan Biasa'];
String kategori;
String sumberAduan;
List <NetworkImage> _listOfImages = <NetworkImage>[];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Kemaskini Aduan"),
backgroundColor: Colors.redAccent,
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SizedBox(height: 10.0),
TextFormField(
decoration:InputDecoration(
hintText: myDateTime.toString(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5))),
onChanged: (value){
setState(() {
myDateTime = value as DateTime;
print(myDateTime);
});
},
),
SizedBox(height: 10.0),
TextFormField(
decoration:InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5))),
controller: _noAduan,
),
SizedBox(height: 10.0),
DropdownButtonFormField(
hint:Text(widget.da.data['sumberAduan']),
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_contact_calendar),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5))),
isExpanded: true,
value: sumberAduan,
onChanged: (newValue) {
setState(() {
sumberAduan = newValue;
_sumberAduan.text = sumberAduan;
});
},
items: sumber.map((sum){
return DropdownMenuItem(
value: sum,
child: new Text(sum),
);
}).toList(),
),
SizedBox(height: 10.0),
DropdownButtonFormField(
hint:Text(widget.da.data['kategori']),
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_contact_calendar),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5))),
isExpanded: true,
value: kategori,
onChanged: (newValue) {
setState(() {
kategori = newValue;
_kategori.text = kategori;
});
},
items: kate.map((ka){
return DropdownMenuItem(
value: ka,
child: new Text(ka),
);
}).toList(),
),
Container(
margin: EdgeInsets.all(10.0),
height: 200,
decoration: BoxDecoration(
color: Colors.white
),
width: MediaQuery.of(context).size.width,
child: Carousel(
boxFit: BoxFit.cover,
autoplay: false,
//images: _listOfImages,
indicatorBgPadding: 5.0,
dotPosition: DotPosition.bottomCenter,
animationCurve: Curves.fastLinearToSlowEaseIn,
animationDuration: Duration(milliseconds: 2000),
),
)
],
),
),
)
);
}
}
the image that need to display for update image
This is how I want to display image in class EditTask when the user want to update information from ListOfTaskNotApprove
The error show that "type 'List' is not a subtype of type 'String'"
Can someone help me? because I had tried many method to solve this problem but it didn't work for me.
You say:
_listOfImages = NetworkImage(da.data['url']) as List<NetworkImage>; // this line show the error
Yes, you can't cast a NetworkImage to a List<NetworkImage>. You probably meant:
_listOfImages = [ NetworkImage(da.data['url']) ] as List<NetworkImage>;
The thing is your are getting a list of url's from Firebase storage which are Strings but you are adding this strings into a list of type Network Image which is wrong. As a list of string cannot be converted to a list of Network Image.
There are 2 ways to resolve this-
Change your list type to List and then wherever you show this image use
list data as an argument for Network Image.
List<String> urls=new List();
//suppose list is not empty
....
return NetworkImage(urls[i]);
....
While adding url's to your list add an Network Image object and directly use the list item while showing images.
List<NetworkImage> list=new List();
list.add(NetworkImage('some url'));
....
return list[i];
....

GlobalKey<FormState>().currentState.save() is falling when I submit a form in Flutter

Using bloc from rxdart: ^0.24.1
I am trying to save object on mysql. The first try the object get saved succefully, the second try, with a new object, it falling on formKey.currentState.save(). I am using GlobalKey<FormState>() in order to validate the form with Stream
My code is
class DetailGamePage extends StatefulWidget {
#override
_DetailGameState createState() => _DetailGameState();
}
class _DetailGameState extends State<DetailGamePage> {
final formKey = GlobalKey<FormState>();
GameBloc gameBloc;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (gameBloc == null) {
gameBloc = Provider.gameBloc(context);
}
}
#override
Widget build(BuildContext context) {
Game _game = ModalRoute.of(context).settings.arguments;
if (_game == null) {
_game = Game(
color: "#000000",
description: "",
env: "",
isBuyIt: false,
isOnBacklog: false);
}
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
title: Text(
"Add Game",
style: TextStyle(color: Colors.black),
),
actions: [
FlatButton(
onPressed: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
Fluttertoast.showToast(msg: "Game saved");
setState(() {
gameBloc.saveOrUpdate(_game, gameBloc.name,
gameBloc.description, "listGame");
});
Navigator.pushReplacementNamed(context, "home");
}
},
child: Text(
(StringUtils.isNullOrEmpty(_game.id)) ? "Add" : "Update",
style: TextStyle(color: HexColor(_game.color), fontSize: 20),
))
],
),
body: Form(
key: formKey,
child: Stack(children: <Widget>[
_createBackground(context, _game),
_createFormGame(context, _game, gameBloc)
]),
));
}
Widget _createBackground(BuildContext context, Game game) {
final size = MediaQuery.of(context).size;
final gradientTop = Container(
height: size.height, //* 0.4,
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[HexColor(game.color), Colors.white])),
);
final circule = Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.1)),
);
return Stack(
children: <Widget>[
gradientTop,
Positioned(
child: circule,
top: 90,
left: 50,
),
Positioned(
child: circule,
top: -40,
right: -30,
),
Container(
padding: EdgeInsets.only(top: 80),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
width: double.infinity,
),
],
),
)
],
);
}
Widget _createFormGame(BuildContext context, Game game, GameBloc gameBloc) {
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(
child: Container(
height: 80.0,
)),
Container(
width: size.width * 0.85,
padding: EdgeInsets.symmetric(vertical: 50.0),
margin: EdgeInsets.symmetric(vertical: 30.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black26,
blurRadius: 3.0,
offset: Offset(0.0, 5.0),
spreadRadius: 3.0)
]),
child: Column(
children: <Widget>[
Text("Foto", style: TextStyle(fontSize: 20.0)),
SizedBox(
height: 50.0,
),
_createNameImput(gameBloc, game),
_createDescriptionImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createWasGameImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createToTheBacklogImput(gameBloc, game),
SizedBox(height: 60),
_createDeleteButton(gameBloc, game),
SizedBox(height: 60),
],
))
],
),
);
}
#override
void dispose() {
gameBloc?.dispose();
super.dispose();
}
Widget _createWasGameImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("Do you have it?"),
value: game.isBuyIt,
onChanged: (bool value) {
setState(() {
game.isBuyIt = value;
});
},
secondary: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createToTheBacklogImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("To the backlog?"),
value: game.isOnBacklog,
onChanged: (bool value) {
setState(() {
game.isOnBacklog = true;
});
},
secondary: IconButton(
icon: Icon(Icons.list),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createNameImput(GameBloc gamebloc, Game game) {
return Column(children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.name,
onSaved: (value) {
gameBloc.setName(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Name",
icon: Icon(
Icons.games,
color: HexColor(game.color),
)),
),
),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
]);
}
Widget _createDescriptionImput(GameBloc gameBloc, Game game) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.description,
onSaved: (value) {
gameBloc.setDescription(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Description",
icon: Icon(
Icons.description,
color: HexColor(game.color),
)),
),
);
}
Widget _createDeleteButton(GameBloc gameBloc, Game game) {
if (StringUtils.isNotNullOrEmpty(game.id)) {
return FlatButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("Do you wan to remove the game"),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
gameBloc.remove(game, "listGame");
});
Navigator.pop(context);
Navigator.pop(context);
},
child: Text("Yes")),
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("No"))
],
);
});
},
child: Text("Remove Game"));
} else {
return Container();
}
}
}
This is the bloc
class GameBloc extends Validators {
//Controller
final _allDataGames = BehaviorSubject<List<Game>>();
final _descriptionController = BehaviorSubject<String>();
final _nameController = BehaviorSubject<String>();
final _allMyListGamesByNameController = BehaviorSubject<List<Game>>();
//Services
GameService gameService = GameService();
//get Data from streams
Stream<List<Game>> get allGameData => _allDataGames.stream;
Stream<List<Game>> get allGameByNameList =>
_allMyListGamesByNameController.stream;
Stream<String> get getDescriptionStream =>
_descriptionController.stream.transform(validateDescription);
Stream<String> get getNameStream =>
_nameController.stream.transform(validName);
//Observable
Stream<bool> get validateDescriptionStream =>
Rx.combineLatest([getDescriptionStream], (description) => true);
Stream<bool> get validateNameStream =>
Rx.combineLatest([getNameStream], (name) => true);
//Set Stream
Function(String) get setDescription => _descriptionController.sink.add;
Function(String) get setName => _nameController.sink.add;
//Get Stream
//From repo
void allGames() async {
List<Game> games = await gameService.getAllDataGames();
_allDataGames.sink.add(games);
}
//From my setting
void allMyListGamesByName(String listName) async {
List<Game> games = await gameService.allMyListGamesByName(listName);
_allMyListGamesByNameController.sink.add(games);
}
void saveOrUpdate(
Game game, String name, String description, String listGame) {
game.name = name;
game.description = description;
if (StringUtils.isNullOrEmpty(game.id)) {
game.id = Uuid().v1();
gameService.add(game, listGame);
} else {
gameService.update(game);
}
}
void remove(Game game, String listGame) {
gameService.remove(game, listGame);
}
//Get Lastest stream value
String get name => _nameController.value;
String get description => _descriptionController.value;
dispose() {
_descriptionController?.close();
_allMyListGamesByNameController?.close();
_allDataGames?.close();
_nameController?.close();
}
}
The provider:
class Provider extends InheritedWidget {
static Provider _imstance;
final _gameBloc = GameBloc();
factory Provider({Key key, Widget child}) {
if (_imstance == null) {
_imstance = new Provider._internal(key: key, child: child);
}
return _imstance;
}
Provider._internal({Key key, Widget child}) : super(key: key, child: child);
static GameBloc gameBloc(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider)
._gameBloc;
}
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
The error is:
════════ Exception caught by gesture ═══════════════════════════════════════════
Bad state: Cannot add new events after calling close
When I evaluate formKey.currentState.save(); I got:
formKey.currentState.save()
Unhandled exception:
Bad state: Cannot add new events after calling close
#0 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
#1 Subject._add (package:rxdart/src/subjects/subject.dart:141:17)
#2 Subject.add (package:rxdart/src/subjects/subject.dart:135:5)
#3 _StreamSinkWrapper.add (package:rxdart/src/subjects/subject.dart:167:13)
I was reading about this error, it mention the error is on Bloc singleston scope or dispose method.
What is happen?
When you navigate to home with Navigator.pushReplacementNamed(context, "home"), the _DetailGamePage<State> is being disposed, calling gameBloc?.dispose. This leaves _gameBloc instantiated with all streams closed.
As you are using a Singleton Provider, when you navigate back to DetailGamePage, your save is trying to write to the closed streams.
What you need to do is move the closure of the streams farther up the widget tree so as not to close them before you are done with them, perhaps at the app level OR re-instantiate _gameBloc if the streams are closed, loading the data from the repo again.

The method 'substring' was called on null. Receiver: null Tried calling: substring(0, 1)

I'm programming a chat application using flutter and firebase. Whenever I click on "Message" button to go to Chat Room screen following error appears:
The method 'substring' was called on null.
Receiver: null
Tried calling: substring(0, 1)
Here's the code of search view. Currently , I'm working to get ChatRoomID by a function to implement chatroom screen but it is showing an error that the substring was null.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:howdy_do/View/chatroomscreen.dart';
import 'package:howdy_do/View/conversation_screen.dart';
import 'package:howdy_do/helper/constants.dart';
import 'package:howdy_do/helper/helperfunctions.dart';
import 'package:howdy_do/services/database.dart';
import 'package:howdy_do/widgets/widget.dart';
class Search extends StatefulWidget {
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController searchTextEditingController = new TextEditingController();
QuerySnapshot searchResultSnapshot;
Widget searchList(){
return searchResultSnapshot != null ? ListView.builder(
itemCount: searchResultSnapshot.docs.length ,
shrinkWrap: true,
itemBuilder: (context, index) {
return SearchTile(
userName: searchResultSnapshot.docs[index].data()["name"],
userEmail: searchResultSnapshot.docs[index].data()["email"],
);
}) : Container();
}
initiateSearch(){
DatabaseMethods().getUserByUsername(searchTextEditingController.text)
.then((val){
setState(() {
searchResultSnapshot = val;
print("$searchResultSnapshot");
});
});}
Widget SearchTile( {String userName, String userEmail}){
return Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userName, style: simpleTextStyle(),),
Text(userEmail, style: simpleTextStyle(),)
],
),
Spacer(),
GestureDetector(
onTap:() {
createChatroomAndStartConversation( **// Error appears here**
userName: userName
);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text("Message", style: mediumTextStyle(),),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: LinearGradient(
colors:[
const Color(0xffF7A1A0),
const Color(0xffF8A3A2)
]
),
)
),
)
],
),
);
}
createChatroomAndStartConversation({ String userName}){
print("${Constants.myName}");
if(userName != Constants.myName) {
List<String> users = [userName,Constants.myName];
String chatRoomId = getChatRoomId(userName,Constants.myName); **// Error appears here**
Map<String,dynamic> chatRoomMap= {
"users": users,
"chatRoomId" : chatRoomId
};
DatabaseMethods().createChatRoom(chatRoomId, chatRoomMap) ;
Navigator.push(context, MaterialPageRoute(
builder: (context) => ConversationScreen(chatRoomId)
));
}else {
print("You can't send text to yourself");
}
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context),
body:Container(
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 16),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchTextEditingController,
style: TextStyle(
color: const Color(0xffa1a0ba),
fontFamily: 'Poppins',
),
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xffffffff),
border: UnderlineInputBorder(
borderRadius:BorderRadius.circular(30.0)),
hintText: "search username...",
hintStyle: TextStyle(
color: Colors.black12,
fontFamily: 'Poppins',
),
),
)
),
GestureDetector(
onTap: (){
initiateSearch();
},
child: Container(
height: 40,
width: 40,
padding: EdgeInsets.all(8),
child:Image.asset("assets/images/search_white.png")
) ),
],
),
),
searchList() ],), ), ); } }
getChatRoomId(String a, String b) {
if(a.substring(0, 1).codeUnitAt(0)> b.substring(0, 1).codeUnitAt(0)){ **//And error appears here too**
return "$b\_$a";
} else {
return "$a\_$b";
}
}
What's happening is that you are passing getChatRoomId a null value so when you call the substring method it gives you an error because it cannot process a null value. Most probably you are unable to read data from firebase. I suggest you try to print the chatRoodID to make sure you are not getting a null value.

How to pass data back from a widget?

I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.

flutter radiovalue not changing

I'm trying to get a very simple set of radiobuttons up, which is why it's so frustrating that they aren't working. I've tried setting this up in a similar class, and it's worked. I know for a fact that setstate is being called, but for some reason it's not updating the individual radio button. Which makes me think that this is some weird issue related to state.
Anyways, all help would be appreciated. My main class is the second part of the code below.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/thembloc.dart';
import './components/textfield.dart';
class SignUp extends StatefulWidget {
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
/*
ui for signup
includes multiple textfields.
includes all of the information that we'll need
to collect for an user to register an account.
todo: wrap everything in a form, encrypt it and send it to a private server.
*/
#override
Widget build(BuildContext context) {
double _height = MediaQuery.of(context).size.height;
double _width = MediaQuery.of(context).size.width;
final double _margin = 16.0;
final double _promptWidth = _width - 32.0;
final double _promptHeight = _height - 32.0;
final double _textFieldWidth = _promptWidth - 32.0;
int subscriberValue;
void switchSubscriber(int value) {
setState(() {
subscriberValue = value;
});
}
return BlocBuilder(
bloc: BlocProvider.of<ThemeBloc>(context),
builder: (context, ThemeState state) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
appBar: AppBar(
centerTitle: true,
title: Text(
"smartmoney",
style: BlocProvider.of<ThemeBloc>(context).currentState.themedata.primaryTextTheme.display2,
),
// appbar
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(8.0),
bottomRight: Radius.circular(8.0))),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: BlocProvider.of<ThemeBloc>(context).currentState.themedata.buttonColor,
),
onPressed: () {
print("going back");
},
),
backgroundColor: BlocProvider.of<ThemeBloc>(context).currentState.themedata.canvasColor,
),
body: Container(
height: _height,
width: _width,
color: BlocProvider.of<ThemeBloc>(context).currentState.themedata.backgroundColor,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: _margin),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
color: BlocProvider.of<ThemeBloc>(context).currentState.themedata.canvasColor,
boxShadow: [
BoxShadow(
spreadRadius: 0.0,
color: Colors.black38,
blurRadius: 6.0,
offset: Offset(0.0, 3.0)),
]),
width: _promptWidth,
height: _promptHeight - 48 - _margin,
child: Column(
children: <Widget>[
Text("Let's get started",
style: BlocProvider.of<ThemeBloc>(context).currentState.themedata.primaryTextTheme.display2,
),
Text("Enter your information to create an account",
style: BlocProvider.of<ThemeBloc>(context).currentState.themedata.primaryTextTheme.subtitle,
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: StyledTextField(
textFieldWidth: _textFieldWidth,
helperText: "First name",
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: StyledTextField(
textFieldWidth: _textFieldWidth,
helperText: "Last name",
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: StyledTextField(
textFieldWidth: _textFieldWidth,
helperText: "Email",
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: StyledTextField(
textFieldWidth: _textFieldWidth,
helperText: "Password",
),
),
Padding(
padding: EdgeInsets.only(top: 8.0),
child: StyledTextField(
textFieldWidth: _textFieldWidth,
helperText: "Phone number",
),
),
Text("Subscriber type",
style: BlocProvider.of<ThemeBloc>(context).currentState.themedata.primaryTextTheme.display1,
),
Radio(
groupValue: subscriberValue,
value: 0,
onChanged: (int value) => switchSubscriber(value),
),
Radio(
groupValue: subscriberValue,
value: 1,
onChanged: (int value) => switchSubscriber(value),
)
],
),
),
)
],
),
),
);
});
}
}
import 'package:flutter/material.dart';
import './bloc/thembloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'ui/signin.dart';
import 'ui/signup.dart';
import 'ui/onboarding.dart';
import './ui/testing/whatthefuck.dart';
void main() {
runApp(
MaterialApp(
home: SmartMoney(),
)
// SmartMoney()
);
}
class SmartMoney extends StatefulWidget {
#override
_SmartMoneyState createState() => _SmartMoneyState();
}
class _SmartMoneyState extends State<SmartMoney> {
final _themeBloc = ThemeBloc();
#override
Widget build(BuildContext context) {
return BlocProvider(
bloc: _themeBloc,
child: SignUp(),
);
}
}
The problem is because you defined your variable subscriberValue inside your build method. You're using setState calls that recall build method and in every recall of build you're losing the value of subscriberValue. I advise you to always use variables that will control the state of your widget as class members.
class _SignUpState extends State<SignUp> {
// HAS TO BE CLASS MEMBER AND IT'S GOOD AN INITIAL VALUE TOO..
int subscriberValue =1; // asuming that 1 is default radio button option
#override
Widget build(BuildContext context) {
//... some codes ...
//int subscriberValue; REMOVE THIS LINE. YOU'RE LOSING THE VALUE IN EVERY setState call
//You can define this method outside from build too.
void switchSubscriber(int value) {
setState(() {
subscriberValue = value;
});
}
}