Good morning, all, I've a problem. I've a text Form Field in my application when I tapped on it to type, the keyboard didn't show this video explain what happened exactly
I read questions that have same problem and try to do its answer but no way
solutions that I tried it.
1- convert the stateless to stateful and the oppsite
2- declare controller global (after imports - in cubit file - in shared file)
3-don't use Form Widget
4- don't use onFieldSubmitted properity
5- try to run flutter run --release but nothing shown in terminal
6- check crashes in google play console
probably I tried most of answers, but no one is working for me,
this is the code of my search screen
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SearchScreen extends StatefulWidget {
const SearchScreen({Key? key}) : super(key: key);
#override
State<SearchScreen> createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var width = size.width;
return BlocConsumer<HomeCubit, HomeStates>(
listener: (context, state) {},
builder: (context, state) {
var cubit = HomeCubit.get(context);
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
children: [
// which zone text
SizedBox(
width: width * .30,
child: FittedBox(child: Text('اختر المنطقة',style: TextStyle(fontSize: 24,fontWeight:FontWeight.normal))))
const SizedBox(width: 25.0),
Expanded(
child: Center(
child: DropdownButton(
alignment: Alignment.center,
value: cubit.zonePopupValue,
hint: const Text('كل المناطق', textAlign: TextAlign.center),
style: TextStyle(
color: HexColor('#ECB365'),
fontSize: 24,
fontWeight: FontWeight.normal,
),
items: cubit.list,
onChanged: (value) => cubit.changePopupZoneValue(int.parse(value.toString())),
),
),
),
],
)
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: TextFormField(
textInputAction: TextInputAction.search,
onFieldSubmitted: (_) async => cubit.search(),
controller: cubit.searchController,
keyboardType: TextInputType.text,
maxLines: 1,
maxLength: 100,
textAlign: TextAlign.right,
textDirection: TextDirection.rtl,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(right: 20, left: 10),
labelText: "إبحث",
alignLabelWithHint: true,
labelStyle: TextStyle(
color: HexColor('#ECB365'),
fontSize: 24,
fontWeight: FontWeight.normal,
),
suffixIcon: cubit.isLoadingAuth? CircularProgressIndicator(color: HexColor('#ECB365')): IconButton(
onPressed: () => cubit.search(),
icon: Icon(Icons.search,color: HexColor('#ECB365')),
iconSize: 35,
color: HexColor('#081C31'),
),
border: OutlineInputBorder(borderSide:BorderSide(color: HexColor('#ECB365'))),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: HexColor('#ECB365'))),
enabledBorder: OutlineInputBorder(borderSide:BorderSide(color: HexColor('#ECB365'))),
fillColor: HexColor('#ECB365')
),
)
),
Container(
margin: const EdgeInsets.only(left: 10.0, right: 20.0),
child: const Divider(color: Colors.black,height: 36)
),
cubit.responseBodySearch == null
? const Center(child: Text("أبدأ البحث"))
: cubit.responseBodySearch.isEmpty
? const Center(child: Text("غير متوفر حاليا"))
: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: cubit.responseBodySearch!.length,
itemBuilder: (context, index) => buildSearchItem(cubit, context, index)
)
],
),
),
)
);
}
);
}
}
this is the code of shared component
class HomeCubit extends Cubit<HomeStates> {
HomeCubit() : super(HomeInitState());
static HomeCubit get(context) => BlocProvider.of(context);
String api = 'https://hadayekhof.com/dalel/api/';
var searchController = TextEditingController();
int? zonePopupValue;
void changePopupZoneValue(int value) {
zonePopupValue = value;
getCategoriesData(zoneIds[value].toString());
emit(HomeChangePopupButtonState());
}
var responseBodySearch;
Future search() async {
emit(HomeSearchLoadingState());
responseBodySubCategories = null;
var data = {
'zone_id': zonePopupValue == null ? '' : zoneIds[zonePopupValue!].toString(),
// 'parent_cat': categoryPopupValue == null ? '' : categoryIds[categoryPopupValue!].toString(),
'term': searchController.text.toString()
};
var uri = Uri.parse('${api}stores/searchStores');
var header = {'Authorization': 'Bearer $ciphertext'};
if (searchController.text == '') {
Fluttertoast.showToast(msg: 'من فضلك اكتب شئ');
} else {
await http.post(uri, body: data, headers:header ).then((value) async {
responseBodySearch = await jsonDecode(value.body);
if (value.body.toString().contains('[') == false) {
responseBodySearch = null;
Fluttertoast.showToast(msg: 'no stores found');
}
emit(HomeSearchSuccessState());
}).catchError((error) {
debugPrint("search error is : $error");
emit(HomeSearchErrorState());
});
}
}
}
how can I solve this problem?
finally, I solve It by write this code in the path android\app\src\main\AndroidManifest.xml
<application
...
android: labelwareAccelerated="true"
...>
and this
<activity
...
android:hardwareAccelerated="true"
...>
It's working now
Related
While using ImagePicker https://pub.dev/packages/image_picker, picking the image works.
But when I put this into the sink it crashes with the following error:
LateInitializationError: Field '_textSeparators#411091221' has already been initialized.
When I remove the FormBuilder it works. So maybe has to do something with the FormBuilder.
Would be great if anyone can point me into the right direction here.
Widget:
class CreatePost extends StatelessWidget {
final GlobalKey<FormBuilderState> _formkey =
GlobalKey<FormBuilderState>(debugLabel: 'GlobalFormKey #SignIn ');
const CreatePost({required Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocBuilder<PostBloc, PostState>(
builder: (context, state) {
if (state is AddImagePost) {
return Scaffold(
appBar: AppBar(
iconTheme: const IconThemeData(color: Colors.black,),
),
body: GestureDetector(
onTap: () {
ImagePicker().pickImage(
source: ImageSource.gallery,)
.then((im) =>context.read<PostBloc().add(SetImagePost(im!)));
},
child:
Column(
children: [
FormBuilder(key: _formkey, "content")
_imageWidget(state.memoryImage),
)],
),);
}
},
);
}
}
Event:
class SetImagePost extends PostEvent {
const SetImagePost(this.image);
final XFile image;
}
State:
class AddImagePost extends PostState {
final MemoryImage memoryImage;
AddImagePost(this.image) : super();
}
BloC:
class PostBloc extends Bloc<PostEvent, PostState> {
final XFile image;
PostBloc({required this.image})
:super(CreatePostInit()){
on<SetImagePost>(_setImagePost);
}
void _setImagePost(SetImagePost event, Emitter<PostState> emit) async {
var memoryImage await apiRepository.postImage(event.image);
emit(AddImagePost(memoryImage));
}
}
Adding the different widgets to the FormBuilder step by step, it turned out that exactly the following widget is causing the error:
TextFieldTags(
textfieldTagsController: tagsControllerAnswers,
initialTags: const ["yes","no","maybe"],
textSeparators: const [' ', ','],
letterCase: LetterCase.small,
validator: (String tag) {
if (tagsController.getTags!.contains(tag)) {
return 'you already entered that';
}
if(tagsControllerAnswers.getTags!.length > 4){
return 'Please enter no more than 5 tags.';
}
return null;
},
inputfieldBuilder:
(context, tec, fn, error, onChanged, onSubmitted) {
return ((context, sc, tags, onTagDelete) {
return TextField(
controller: tec,
focusNode: fn,
decoration: InputDecoration(
isDense: true,
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(color: askTextFieldBorderFocused, width: 3.0),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(color: askTextFieldBorder, width: 2.0),
),
errorText: error,
prefixIconConstraints:
BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.74),
prefixIcon: tags.isNotEmpty
? SingleChildScrollView(
controller: sc,
scrollDirection: Axis.horizontal,
child: Row(
children: tags.map((String tag) {
return Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(3.0),
),
color: tagColor,
),
margin: const EdgeInsets.symmetric(
horizontal: 5.0),
padding: const EdgeInsets.symmetric(
horizontal: 5.0, vertical: 5.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
child: Text(
tag,
style: CustomTextStyle.bodyText1(context)?.copyWith(color: tagFont ),
),
onTap: () {
// print("$tag selected");
},
),
const SizedBox(width: 3.0),
InkWell(
child: const Icon(
Icons.close_rounded,
size: 20.0,
color: tagFont,
),
onTap: () {
onTagDelete(tag);
},
)
],
),
);
}).toList()),
)
: null,
),
onChanged: onChanged,
onSubmitted: onSubmitted,
);
});
},
),
Moving the "TextFieldTags" out of the FormBuilder solved the issue :)
I'm emulating this search and filter github here and the codes are almost the same but the filtered results do not update instantly while I type and also I faced the following issues:
I will have to press enter on my laptop to finally get the filtered list
When I hit the close icon(which is to clear all the words), I will have to tap the searchbar again so that all my listtile are back on the listview.
Here's my code:
class _CurrencySelectState extends State<CurrencySelect> {
late List<Currency> resCur;
String query = '';
#override
void initState() {
super.initState();
resCur = currencyList;
}
void searchCur(String query) {
final List<Currency> filteredCur = currencyList.where((cur) {
final symbolLower = cur.symbol.toLowerCase(); // Search using symbol
final nameLower = cur.country.toLowerCase(); // Search using country
final searchLower = query.toLowerCase();
return symbolLower.contains(searchLower) ||
nameLower.contains(searchLower);
}).toList();
setState(() {
this.query = query;
resCur = filteredCur;
});
}
#override
Widget build(BuildContext context) {
Widget buildCur(Currency cur) => ListTile(
leading: Padding(
padding: EdgeInset.all(5)
child: SizedBox(
child: Column(
children: <Widget>[
SvgPicture.asset(
cur.assetPath,
),
]),
),
),
title: Column(
children: [
Text(
cur.symbol,
style: TextStyle(
...
),
Text(
cur.name,
style: TextStyle(
...
),
],
),
trailing: Text(
"0.25",
style: TextStyle(
...
),
);
return TextButton(
onPressed: () async {
showModalBottomSheet(
enableDrag: false,
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return DraggableScrollableSheet(
expand: false,
builder: (context, scrollController) {
return Column(
children: <Widget>[
SearchWidget(
text: query,
onChanged: searchCur,
hintText: "Enter symbol or country"
),
Expanded(
child: ListView.builder(
controller: scrollController,
itemCount: resCur.length,
itemBuilder: (context, int index) {
final cur = resCur[index];
return buildCur(cur);
},
),
)
],
);
},
);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
...
),
SvgPicture.asset(
...
)
],
));
}
}
Searchwidget code:
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
final String text;
final ValueChanged<String> onChanged;
final String hintText;
const SearchWidget({
Key? key,
required this.text,
required this.onChanged,
required this.hintText,
}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
final styleActive = TextStyle(color: Colors.black);
final styleHint = TextStyle(color: Colors.black54);
final style = widget.text.isEmpty ? styleHint : styleActive;
return Container(
height: 42,
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(color: Colors.black26),
),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: controller,
decoration: InputDecoration(
icon: Icon(Icons.search, color: style.color),
suffixIcon: widget.text.isNotEmpty
? GestureDetector(
child: Icon(Icons.close, color: style.color),
onTap: () {
controller.clear();
widget.onChanged('');
FocusScope.of(context).requestFocus(FocusNode());
},
)
: null,
hintText: widget.hintText,
hintStyle: style,
border: InputBorder.none,
),
style: style,
onChanged: widget.onChanged,
),
);
}
}
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];
....
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.
i want to change the indexvalue (pictogramindex) of one page when we click nextbutton on another screen.I will explain briefly , I have 2 screens in my scenario the first screen contains an image and it's name , a textfield and nextbutton (i have provided a dummy data contains a list of image and it's names) the logic behind this is , when we complete the textfield box and click next button(after validate) the textfield value checks with the correctvalue which i was given in the dummy data and show it's synonym which also provided. when we click the next button we will go to another page which contains the correct answer(passed from first page) and a textfield in this the user can write about the correct answer ( validated) when click next button in this page (till this my applicationworks perfectly) i want to load the first page with it's index updated (+1) which i initialised as 0 (var pictogramindex=0). But in my case when coming back to first page the index is not updating it will automatically stores the initialised value. what i want is i want to update index on the first page when i click next button in the Second page .
my source code of first screen is shown here
class Pictogramscreen extends StatefulWidget {
final int length;
const Pictogramscreen({Key key, this.length}) : super(key: key);
#override
_PictogramscreenState createState() => _PictogramscreenState();
}
class _PictogramscreenState extends State<Pictogramscreen> {
#override
final _Key = GlobalKey<FormState>();
Color defaultcolor = Colors.blue[50];
Color trueColor = Colors.green;
Color falseColor = Colors.red;
Widget defcorrect = Text('');
var pictogramindex = 0;
TextEditingController usertitleInput = TextEditingController();
nextPictogram() {
setState(() {
pictogramindex++;
});
}
fillColor() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defaultcolor = trueColor
: defaultcolor = falseColor;
});
}
correctText() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defcorrect = Text(pictdata[pictogramindex]['pictsynonym'])
: defcorrect = Text(pictdata[pictogramindex]['pictcorrectword']);
});
}
reset() {
setState(() {
defaultcolor = Colors.blue[50];
defcorrect = Text('');
usertitleInput.clear();
});
}
void description(BuildContext ctx) {
Navigator.of(context).pushNamed('/user-description', arguments: {
'id': pictdata[pictogramindex]['pictid'],
'word': pictdata[pictogramindex]['pictcorrectword']
});
}
Widget build(BuildContext context) {
int length = pictdata.length;
return Scaffold(
body: pictogramindex < pictdata.length
? ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20),
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Card(
margin: EdgeInsets.only(top: 20),
child: Image.network(
pictdata[pictogramindex]['pictimg']),
),
SizedBox(
height: 10,
),
Text(
pictdata[pictogramindex]['pictword'],
style: TextStyle(
fontSize: 25,
),
),
SizedBox(
height: 10,
),
//Card(
//color: Colors.blue,
// child: TextField(
// decoration: InputDecoration.collapsed(
// hintText: 'type here'),
//textAlign: TextAlign.center,
// onSubmitted: (value) {
// usertitleInput = value;
// print(usertitleInput);
// },
// ),
//),
Form(
key: _Key,
child: TextFormField(
controller: usertitleInput,
validator: (usertitleInput) {
if (usertitleInput.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
fillColor: defaultcolor,
),
onFieldSubmitted: (value) {
usertitleInput.text = value;
fillColor();
correctText();
print(usertitleInput.text);
}),
),
SizedBox(
height: 10,
),
defcorrect,
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Key.currentState.validate()) {
description(context);
// nextPictogram();
reset();
}
//
//if (_Key.currentState.validate() == correctText()) {
// nextPictogram;
// }
},
child: Text('Next'),
)
],
),
),
],
)
: Center(
child: Text('completed'),
));
}
}
my source code of the second screen is show here
class Userinputscreen extends StatefulWidget {
final String id;
final String word;
const Userinputscreen({Key key, this.id, this.word}) : super(key: key);
#override
_UserinputscreenState createState() => _UserinputscreenState();
}
class _UserinputscreenState extends State<Userinputscreen> {
final _Keey = GlobalKey<FormState>();
TextEditingController userdescription = TextEditingController();
var pictogramindex;
void nextpict(BuildContext context) {
Navigator.of(context).pushNamed('/main-screen');
}
// void nextpict(BuildContext context, int index) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (ctx) => Pictogramscreen(
// index: i = 0,
// )));
// }
#override
Widget build(BuildContext context) {
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final correctWord = routeArgs['word'];
return MaterialApp(
home: Scaffold(
body: ListView(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.only(top: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
correctWord,
style: TextStyle(fontSize: 26),
),
SizedBox(
height: 10,
),
Form(
key: _Keey,
child: TextFormField(
controller: userdescription,
validator: (userdescription) {
if (userdescription.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
),
onFieldSubmitted: (value) {
userdescription.text = value;
print(userdescription.text);
}),
),
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Keey.currentState.validate()) {
nextpict(context);
}
},
child: Text('Next'),
)
],
),
),
),
),
])),
);
}
}
If I get it right, you basically want to tell the initial page that it's state is updated(the index) elsewhere. You basically need to make your app "reactive".
As is said in Google Developers Tutorial:
One of the advantages of Flutter is that it uses reactive views, which you can take to the next level by also applying reactive principles to your app’s data model.
Use some sort of state management. You need to choose from and use either Bloc, InheritedWidget and InheritedModel, Provider(ScopedModel), or the like.
Check this article on flutter about state management, or this for a complete list of approaches