Data loading issue on Flutter - json

I have wrote a flutter code to load questions using a json. This works but when we go to the next question json load again. How to load once.
Other question is I'm loading answers to buttons and when click the answer I need to change that buttons color. Can some one help me.
Thanks
Code
import 'package:flutter/material.dart';
import 'show_question_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class ShowQuestions extends StatefulWidget {
const ShowQuestions({super.key});
#override
State<ShowQuestions> createState() => _ShowQuestionsState();
}
class _ShowQuestionsState extends State<ShowQuestions> {
List<Question> questionList = [];
int currentQuestionIndex = 0;
int score = 0;
Future getQuestionData() async {
var response = await http
.get(Uri.http("www.ananmanan.lk", "app/getQuestionList.php"));
var jsonData = jsonDecode(response.body);
List<Question> questions = [];
for (var u in jsonData) {
Question question = Question(
u['ques'],
u['ans1'],
u['ans2'],
u['ans3'],
u['ans4'],
u['correct_ans'],
);
questions.add(question);
}
return questions;
}
//Answer? selectedAnswer;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Question Paper'),
centerTitle: true,
),
body: Container(
child: FutureBuilder(
future: getQuestionData(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
),
);
} else {
questionList = snapshot.data;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_questionWidget(),
_answerList(),
_nextButton(),
],
),
);
}
},
)),
);
}
_questionWidget() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Question ${currentQuestionIndex + 1}/5}',
style: const TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w600,
),
),
const SizedBox(
height: 20.0,
),
Container(
width: double.infinity,
alignment: Alignment.center,
padding: const EdgeInsets.all(32.0),
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(16)),
child: Text(
questionList[currentQuestionIndex].ques,
style: const TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.w600,
),
),
),
],
);
}
_answerList() {
bool isCorrectAns = false;
bool isAns1 = false;
bool isAns2 = false;
bool isAns3 = false;
bool isAns4 = false;
//bool isSelected = answer == selectedAnswer;
// ignore: unrelated_type_equality_checks
// if (questionList[currentQuestionIndex].correctanswer == 1) {
// isCorrectAns = true;
// } else {
// isCorrectAns = false;
// }
if (questionList[currentQuestionIndex].correct_ans == '1') {
isAns1 = true;
} else if (questionList[currentQuestionIndex].correct_ans == '2') {
isAns2 = true;
} else if (questionList[currentQuestionIndex].correct_ans == '3') {
isAns3 = true;
} else if (questionList[currentQuestionIndex].correct_ans == '4') {
isAns4 = true;
}
return Container(
// width: double.infinity,
// margin: const EdgeInsets.symmetric(vertical: 2),
// height: 48,
child: Column(children: [
_answerButton(questionList[currentQuestionIndex].ans1, isAns1),
_answerButton(questionList[currentQuestionIndex].ans2, isAns2),
_answerButton(questionList[currentQuestionIndex].ans3, isAns3),
_answerButton(questionList[currentQuestionIndex].ans4, isAns4),
]),
);
}
Widget _answerButton(String ansText, bool correctans) {
//bool isSelected = answer == selectedAnswer;
bool click = false;
if (ansText != '') {
return Container(
width: double.infinity,
margin: const EdgeInsets.symmetric(vertical: 2),
height: 48,
child: ElevatedButton(
child: Text(ansText),
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: click == true ? Colors.orangeAccent : Colors.white,
onPrimary: click == true ? Colors.white : Colors.black,
),
onPressed: () {
if (correctans) {
score++;
}
},
),
);
} else {
return SizedBox.shrink();
}
}
_nextButton() {
bool isLastQuestion = false;
if (currentQuestionIndex == questionList.length - 1) {
isLastQuestion = true;
}
return Container(
width: MediaQuery.of(context).size.width * 0.5,
height: 48,
child: ElevatedButton(
child: Text(isLastQuestion ? 'Submit' : 'Next'),
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
primary: Colors.blueAccent,
onPrimary: Colors.white,
),
onPressed: () {
if (isLastQuestion) {
showDialog(context: context, builder: (_) => _showScoreDialog());
} else {
setState(() {
//selectedAnswer = null;
currentQuestionIndex++;
});
}
},
),
);
}
_showScoreDialog() {
bool isPassed = false;
if (score >= questionList.length * 0.6) {
isPassed = true;
}
String title = isPassed ? 'Passed' : 'Failed';
return AlertDialog(
title: Text(
title + ' | Score is $score',
style: TextStyle(color: isPassed ? Colors.green : Colors.redAccent),
),
content: ElevatedButton(
child: const Text('Restart'),
onPressed: () {
Navigator.pop(context);
setState(() {
currentQuestionIndex = 0;
score = 0;
//selectedAnswer = null;
});
},
),
);
}
}
Stop loading the json multiple times
Change the color of the button that selected

Related

flutter Exception caught by widgets library, IDE says getter was called on null even it isn't null

I am writing a mobile app in flutter, I get a json and I convert it into an object as in the following class:
class UserProfile {
String role;
List<Town> interestingTown;
String sId;
String uid;
Town myTown;
int iV;
UserProfile(
{this.role,
this.interestingTown,
this.sId,
this.uid,
this.myTown,
this.iV});
UserProfile.fromJson(Map<String, dynamic> json) {
role = json['role'];
if (json['interestingTown'] != null) {
interestingTown = new List<Town>();
json['interestingTown'].forEach((v) {
interestingTown.add(new Town.fromJson(v));
});
}
sId = json['_id'];
uid = json['uid'];
myTown = json['myTown'] != null
? new Town.fromJson(json['myTown'])
: null;
iV = json['__v'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['role'] = this.role;
if (this.interestingTown != null) {
data['interestingTown'] =
this.interestingTown.map((v) => v.toJson()).toList();
}
data['_id'] = this.sId;
data['uid'] = this.uid;
if (this.myTown != null) {
data['myTown'] = this.myTown.toJson();
}
data['__v'] = this.iV;
return data;
}
static Resource<myTown> get profile {
return Resource(
url: Constants.HEADLINE_GET_PROFILO,
parse: (response) {
UserProfile profile = UserProfile.fromJson(jsonDecode(response.body));
return profile;
});
}
}
class Town{
bool enabled;
List<String> categories;
String sId;
String nameTown;
int iV;
Town(
{this.enabled,
this.categories,
this.sId,
this.nameTown,
this.iV});
Town.fromJson(Map<String, dynamic> json) {
enabled = json['enabled'];
categories = json['categories'].cast<String>();
sId = json['_id'];
nameTown = json['nameTown'];
iV = json['__v']; }
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['enabled'] = this.enabled;
data['categories'] = this.categories;
data['_id'] = this.sId;
data['nameTown'] = this.nameTown;
data['__v'] = this.iV;
return data; }
}
After, when I run the class below to load a screen, I get this error (with the emulator screen red for almost 5 seconds):
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building MyAccountsPage(dirty, state: MyAccountsPageList#4dae7):
The getter 'myTown' was called on null.
Receiver: null
Tried calling: myTown
The relevant error-causing widget was:
MyAccountsPage file:///C:/Users/StudioProjects/my_project/lib/bloc.navigation_bloc/navigation_bloc.dart:36:15
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 MyAccountsPageList.build (package:civic_points/pages/myaccountspage.dart:145:35)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4612:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4495:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4667:11)
...
====================================================================================================
After the almost five seconds, the screen loads but it doesn't load right because the boolean variables are not set correctly.
But, the problem is that I can print the profile.myTown.nameTown, so myTown isn't null.
The same error happens if I try to print profile.interestingTown[0].nameTown.
Before, I have tried to set the variables using some if statements, but I had the same error and the screen stays red forever, without load after a few seconds.
I can't fix the error, so I post all the code of the classes, hoping someone could help me in solving the issue.
class MyAccountsPage extends StatefulWidget with NavigationStates {
#override
createState() => MyAccountsPageList();
}
class MyAccountsPageList extends State<MyAccountsPage> {
UserProfile profile;
bool boolinterestingTown = true;
bool boolmyTown = true;
int modify;
int myIndex;
bool add = false;
var deleteTown;
#override
void initState() {
super.initState();
_getProfile();
try {
print (profile.myTown.nameTown);
} catch (e) {
boolmyTown = false;
try {
print(profile.interestingTown[0].nameTown);
} catch (e) {
boolinterestingTown = false;
}
}
}
void _getProfile() {
WebserviceToken().load(UserProfile.profile).then((getProfile) => {
setState(() => {this.profile = getProfile}),
});
}
void deleteTownMe() async {
var headers = {'Content-Type': 'application/json',
'Authorization': 'Bearer ${token}'};
var request = http.Request(
'DELETE', Uri.parse('http:..........'));
request.body = '''{
"town": "${deleteTown}"
}''';
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 201) {
print(response.statusCode);
print(await response.stream.bytesToString());
} else {
print(response.reasonPhrase);
}
}
Widget _buildItemsForTownListView(BuildContext context, int index) {
final town = profile.interestingTown[index].nameTown;
return new Container(
child: Column(
children: <Widget>[
Text(
town,
style: TextStyle(
fontSize: 20,
color: Colors.blueGrey,
fontWeight: FontWeight.bold),
),
ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: RaisedButton(
child: Text("Modify"),
textColor: Colors.white,
color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
onPressed: () {
modify = 2;
myIndex = index;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => citySelection(
profileParameters:
new ProfileParameters(modify, myIndex, add, town),
)));
},
),
),
SizedBox(width: 10),
Flexible(
child: RaisedButton(
child: Text("Delete"),
textColor: Colors.white,
color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
onPressed: () {
deleteTown = town;
deleteTownMe();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => citySelection()));
},
),
),
],
),
),
SizedBox(height: 20),
],
),
);
}
#override
Widget build(BuildContext context) {
var myTown = profile.myTown.nameTown;
print(profile.myTown.nameTown);
return Scaffold(
appBar: AppBar(
titleSpacing: 50.0,
title: new Text("My profile"),
automaticallyImplyLeading: false,
),
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.fromLTRB(16, 16, 0, 0),
width: double.infinity,
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(height: 40),
CircleAvatar(
backgroundImage: NetworkImage(
imageUrl,
),
radius: 48,
backgroundColor: Colors.transparent,
),
SizedBox(height: 48),
Text(
'User',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
Text(
name,
style: TextStyle(
fontSize: 20,
color: Colors.blueGrey,
fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
if (boolmyTown)
Text(
'My town',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
if (boolmyTown)
Text(
myTown,
style: TextStyle(
fontSize: 20,
color: Colors.blueGrey,
fontWeight: FontWeight.bold),
),
if (boolmyTown)
RaisedButton(
child: Text("Modify"),
textColor: Colors.white,
color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
onPressed: () {
modify = 1;
myIndex = 0;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => citySelection(
profileParameters:
new ProfileParameters(modify, myIndex, add, ''),
)));
},
),
if (boolinterestingTown && boolmyTown)
SizedBox(height: 20),
if (boolinterestingTown && boolmyTown)
Text(
'Interesting towns',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
if (boolinterestingTown && boolmyTown)
ListView.builder(
itemCount: profile.interestingTowns.length,
shrinkWrap: true,
itemBuilder: _buildItemsForTownListView,
),
if (boolmyTown && !boolinterestingTown)
SizedBox(height: 20),
if (boolmyTown)
RaisedButton(
child: Text("Add ineresting town"),
textColor: Colors.white,
color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
onPressed: () {
modify = 2;
myIndex = profile.interestingTown.length;
add = true;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => citySelection(
profileParameters:
new ProfileParameters(modify, myIndex, add, ''),
)));
},
),
if (!boolmyTown)
RaisedButton(
child: Text("Add your town"),
textColor: Colors.white,
color: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
onPressed: () {
modify = 1;
myIndex = 0;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => citySelection(
profileParameters:
new ProfileParameters(modify, myIndex, add, ''),
)));
},
),
],
),
),
),
),
);
}
}
You need to confirm variable profile is null or not.
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_getProfile();
});
}
#override
Widget build(BuildContext context) {
var myTown = (profile == null) ? null : profile.myTown.nameTown;
return Scaffold(
appBar: AppBar(
titleSpacing: 50.0,
title: new Text("My profile"),
automaticallyImplyLeading: false,
),
body: (profile == null)
? Center(child: CircularProgressIndicator(),)
: SingleChildScrollView(child: Container(),),
);
}

Access function from another class

Hello guys I'm trying to access function from another class becuse this function establish call and navigat to call page so for sure there will be setState function to update the UI but here is the problem whene the UI got updated from another class the app will go out of the widget tree so i have to use callBack function which is i dont know how pleass help
this is the perant class:
import 'dart:async';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:flutter_social_messenger/src/modules/auth/domain/entities/user.dart';
import 'package:flutter_social_messenger/src/modules/auth/export.dart';
import 'package:flutter_social_messenger/src/modules/notifications/domain/entities/call_msg.dart';
import 'package:flutter_social_messenger/src/modules/notifications/domain/entities/notification.dart';
import 'package:flutter_social_messenger/src/modules/notifications/domain/repository/repo.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../export.dart';
import './call.dart';
class IndexPage extends StatefulWidget {
String userId;
IndexPage(String this.userId);
#override
State<StatefulWidget> createState() => IndexState();
}
class IndexState extends State<IndexPage> {
/// create a channelController to retrieve text value
final _channelController = TextEditingController();
/// if channel textField is validated to have error
bool _validateError = false;
ClientRole _role = ClientRole.Broadcaster;
User currentUser = Modular.get<AuthController>().currentUser;
#override
void dispose() {
// dispose input controller
_channelController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
print("target : " + widget.userId.toString() ) ;
return Scaffold(
appBar: AppBar(
title: Text('Make a Call'),
),
body: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
height: 400,
child: Column(
children: <Widget>[
/*
Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _channelController..text = 'call-' + currentUser.id + "-" + widget.userId ,
decoration: InputDecoration(
errorText:
_validateError ? 'Channel name is mandatory' : null,
border: UnderlineInputBorder(
borderSide: BorderSide(width: 1),
),
hintText: 'call-' + widget.userId + "-" + currentUser.id ,
),
))
],
),
Column(
children: [
ListTile(
title: Text(ClientRole.Broadcaster.toString()),
leading: Radio(
value: ClientRole.Broadcaster,
groupValue: _role,
onChanged: (ClientRole value) {
setState(() {
_role = value;
});
},
),
),
ListTile(
title: Text(ClientRole.Audience.toString()),
leading: Radio(
value: ClientRole.Audience,
groupValue: _role,
onChanged: (ClientRole value) {
setState(() {
_role = value;
});
},
),
)
],
),
*/
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child:
Row( children: <Widget>[
// Expanded(child: Text('call-' + currentUser.id + "-" + widget.userId ) ),
Expanded( child:
RawMaterialButton(
onPressed: () => onJoin(widget.userId,context,currentUser.id) ,
child: Icon(
Icons.call_end,
color: Colors.white,
size: 35.0,
),
shape: CircleBorder(),
elevation: 2.0,
fillColor: Colors.green,
padding: const EdgeInsets.all(15.0),
)
// RaisedButton(
// onPressed: onJoin,
// child: Text('Dial'),
// color: Colors.blueAccent,
// textColor: Colors.white,
// ),
) ], ),
)
],
),
),
),
);
}
DateTime get now => DateTime.now().toUtc();
Future<void> sendFollowNotification() {
String sendToId = widget.userId;
final followerId = currentUser.id;
print("trying to send call notification Id : " + "call-$followerId-$sendToId") ;
final not = CallNotification(
id: "call-$followerId-$sendToId",
sendTo: followerId,
senderId: sendToId,
senderName: currentUser.name,
time: now,
);
return repository.sendNotificaion(not);
}
Future<void> sendCallAlert() async {
String sendToId = widget.userId;
final followerId = currentUser.id;
print("trying to send call notification Id : " + "call-$followerId-$sendToId") ;
final not = CallNotification(
id: "call-$followerId-$sendToId",
sendTo: followerId,
senderId: sendToId,
senderName: currentUser.name,
time: now,
);
await Modular.get<NotificationsHelper>()
.registerNotification(followerId, true);
_callDocument("call-$followerId-$sendToId").setData(not.toJson());
// return _callCollection.document(currentUser.id).setData(not.toJson()) ;
// return repository.sendNotificaion(not);
}
final repository = Modular.get<NotificationReposity>();
final appStore = Modular.get<AppStore>();
final _firestore = Firestore.instance;
DocumentReference _callDocument(String callId) =>
_callCollection.document(callId);
CollectionReference get _callCollection => _firestore.collection("calls");
Future<void> onJoin(String userId,BuildContext context,final _followerId) async {
// print("qqqq") ;
String sendToId = userId;
final followerId = _followerId;
// print(userId+" i'm");
// print(_followerId+" his");
_channelController.text = "call-$followerId-$sendToId" ;
print("diallllll : " + _channelController.text ) ;
// /*
// update input validation
setState(() {
_channelController.text.isEmpty
? _validateError = true
: _validateError = false;
});
sendFollowNotification();
sendCallAlert();
if (_channelController.text.isNotEmpty) {
print('hiiiiissss');
// await for camera and mic permissions before pushing video page
await _handleCameraAndMic();
// push video page with given channel name
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CallPage(
channelName: _channelController.text,
role: _role,
),
),
);
}
// */
}
Future<void> _handleCameraAndMic() async {
// if (kIsWeb || !Platform.isAndroid) return true;
if (!await Permission.camera.isGranted && !await Permission.microphone.isGranted) {
var rr = ((await Permission.camera.request()).isGranted && (await Permission.microphone.request()).isGranted) ;
print("rr : " + rr.toString()) ;
}
}
// Future<bool> checkStoragePermission() async {
// if (kIsWeb || !Platform.isAndroid) return true;
// if (!await Permission.storage.isGranted) {
// return (await Permission.storage.request()).isGranted;
// }
// return true;
// }
}
and this is the child:
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../../../../chat/presentation/calling/index.dart';
import '../../../../../chat/presentation/calling/index.dart';
import '../../../../../chat/presentation/calling/index.dart';
import '../../../../../chat/presentation/calling/index.dart';
import '../../../../../chat/presentation/calling/index.dart';
import '../../../../export.dart';
import '../controller.dart';
import '../../widgets/profile_avatar.dart';
import 'package:flutter_social_messenger/src/modules/chat/presentation/calling/index.dart';
import 'package:flutter_social_messenger/src/modules/chat/presentation/calling/call.dart';
class OProfileHeader extends StatelessWidget implements PreferredSizeWidget {
final OtherUserProfileController controller;
OProfileHeader({Key key, #required this.controller}) : super(key: key);
User get user => controller.otherUser;
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
print("OProfileHeader : " + controller.isFollowing.toString() ) ;
return Stack(
children: <Widget>[
Positioned(
bottom: 200,
left: 0,
right: 0,
child: SizedBox(height: 200)
.decorated(gradient: AppTheme.primaryGradient),
),
Positioned(
bottom: 150,
left: 1,
right: 1,
child: Center(
child: Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5.0,
color: theme.scaffoldBackgroundColor,
child: Column(
children: <Widget>[
Observer(
builder: (_) => Text(
user.name ?? "",
style: GoogleFonts.basic().copyWith(fontSize: 28),
maxLines: 1,
),
).padding(top: 40),
Text(user.status ?? "").padding(bottom: 10),
],
),
).width(MediaQuery.of(context).size.width * 4 / 5),
),
),
Positioned(
left: 0,
right: 0,
bottom: 300,
child: AppBar(
backgroundColor: Colors.transparent,
iconTheme: IconThemeData(),
elevation: 0,
),
),
Positioned(
left: 0,
right: 0,
bottom: 210,
child: Center(
child: Material(
elevation: 5.0,
shape: CircleBorder(),
child: Observer(
builder: (_) => ProfileAvatarWidget(
photoUrl: user.photoUrl,
// isLoading: state is LoadingProfilePic,
),
),
),
),
),
Positioned(
right: 200,
left: 0,
bottom: 60,
child: Center(
child: Observer(
builder: (_) {
controller.currentUser.following;
return RaisedButton(
color: controller.isFollowing
? Colors.black
: theme.primaryColor,
onPressed: () => controller.followUser(user.id),
child: Text(
controller.isFollowing ? "UnFollow" : "Follow",
style: GoogleFonts.alike().copyWith(
fontSize: 16,
color: Colors.white,
),
textAlign: TextAlign.center,
).width(70),
);
},
),
),
),
Positioned(
right: 0,
left: 200,
bottom: 60,
child: Center(
child: Observer(
builder: (_) {
controller.currentUser.following;
return RaisedButton(
color: Colors.green,
onPressed: ()=> {
IndexState().onJoin(user.id,context,controller.currentUser.id),
print(controller.currentUser.id+" "+controller.currentUser.phoneNumber),
print(user.id+" "+user.phoneNumber)
},
child: Text(
"Call",
style: GoogleFonts.alike().copyWith(
fontSize: 16,
color: Colors.white,
),
textAlign: TextAlign.center,
).width(70),
);
},
),
),
),
],
).height(500).backgroundColor(theme.scaffoldBackgroundColor);
}
#override
Size get preferredSize => Size.fromHeight(400);
//----------------------------------------------------
// */
}
I dont understand exactly what you want, but if its how to call a method from a parent widget, if would be something like this:
class ParentWidget extends StatelessWidget{
#override
Widget build(BuildContext context) {
return ChildWidget(onAction: (resultValue){
//Do something with the value
});
}
}
class ChildWidget extends StatelessWidget{
final Function onAction;
ChildWidget({Key key, #required this.onAction}) : super(key: key);
#override
Widget build(BuildContext context) {
return Inkwell(
onTap: (){
onAction(someValue);
}
child: AnyOtherWidget
);
}
}

Exception has occurred. _TypeError (type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>')

I have problem with getruangfasiliti method. Is there any way to fix this error? I have been working this for weeks.
Tried this but it is not what i want. I want to get the every data of the object
Future<List> getruangfasiliti() async {
final response = await http.get(BaseUrl.lihatruangfasiliti(widget.barcode));
return jsonDecode(response.body);
}
So, below is my code and how i can fix the error. I do not know where my wrong is
modelAduan.dart
class Aduan {
final String aduan_id;
final String tarikhaduan;
final String ruang_id;
final String fasiliti_id;
final String maklumat;
final String gambaraduan;
final String status;
final String idpengguna;
final String namaruang;
final String namafasiliti;
Aduan({
this.aduan_id,
this.tarikhaduan,
this.ruang_id,
this.fasiliti_id,
this.maklumat,
this.gambaraduan,
this.status,
this.idpengguna,
this.namaruang,
this.namafasiliti
});
factory Aduan.fromJson(Map<String, dynamic> json) {
return Aduan(
aduan_id: json['aduan_id'],
tarikhaduan: json['tarikhaduan'],
ruang_id: json['ruang_id'],
fasiliti_id: json['fasiliti_id'],
maklumat: json['maklumat'],
gambaraduan: json['gambaraduan'],
status: json['status'],
idpengguna: json['idpengguna'],
namaruang: json['namaruang'],
namafasiliti: json['namafasiliti'],
);
}
}
aduan.dart
import 'dart:convert';
import 'package:eaduanfsktm/api.dart';
import 'package:eaduanfsktm/sejarahaduan.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
import 'package:eaduanfsktm/model/modelRuangFasiliti.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
import 'dart:math' as Math;
import 'package:image/image.dart' as Img;
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:async/async.dart';
import 'package:path/path.dart';
class BorangAduan extends StatefulWidget {
final String idpengguna, barcode;
BorangAduan(this.idpengguna, this.barcode);
#override
_BorangAduanState createState() => _BorangAduanState();
}
class _BorangAduanState extends State<BorangAduan> {
final _key = new GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
RuangFasiliti ruangfasiliti;
TextEditingController controllerruang_id;
TextEditingController controllerfasiliti_id;
TextEditingController controller_namaruang;
TextEditingController controller_namafasiliti;
TextEditingController controllermaklumat = new TextEditingController();
File _image;
Future<RuangFasiliti> getruangfasiliti() async {
final response = await http.get(BaseUrl.lihatruangfasiliti(widget.barcode));
if (response.statusCode == 200) {
setState(() {
ruangfasiliti = RuangFasiliti.fromJson(jsonDecode(response.body));
});
setState(() {
controllerruang_id =
new TextEditingController(text: "${ruangfasiliti.ruang_id}");
controllerfasiliti_id =
new TextEditingController(text: "${ruangfasiliti.fasiliti_id}");
controller_namaruang =
new TextEditingController(text: " ${ruangfasiliti.namaruang}");
controller_namafasiliti =
new TextEditingController(text: " ${ruangfasiliti.namafasiliti}");
});
return ruangfasiliti;
}
}
// #override
// void dispose() {
// controllerruang_id.dispose();
// controllerfasiliti_id.dispose();
// controller_namaruang.dispose();
// controllerruang_id.dispose();
// controller_namafasiliti.dispose();
// super.dispose();
// }
#override
void initState() {
getruangfasiliti();
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Borang Aduan"),
),
body: ListView(
padding: EdgeInsets.all(2.0),
children: <Widget>[
FutureBuilder<RuangFasiliti>(
future: getruangfasiliti(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return aduanbox();
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Center(child: CircularProgressIndicator());
},
),
SizedBox(height: 10.0),
],
),
),
);
}
Widget aduanbox() {
return Center(
child: Form(
key: _key,
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
child: new TextFormField(
controller: controllerfasiliti_id,
readOnly: true,
decoration: InputDecoration(
labelText: "KOD FASILITI",
),
),
),
flex: 2,
),
SizedBox(width: 10.0),
Expanded(
child: Container(
child: new TextFormField(
controller: controller_namafasiliti,
readOnly: true,
decoration: InputDecoration(
labelText: "NAMA FASILITI",
),
),
),
flex: 2,
),
],
),
new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
child: new TextFormField(
controller: controllerruang_id,
readOnly: true,
decoration: InputDecoration(
labelText: "KOD LOKASI",
),
),
),
flex: 2,
),
SizedBox(width: 10.0),
Expanded(
child: Container(
child: new TextFormField(
controller: controller_namaruang,
readOnly: true,
decoration: InputDecoration(
labelText: "LOKASI",
),
),
),
flex: 2,
),
],
),
TextFormField(
controller: controllermaklumat,
validator: (value) {
if (value.isEmpty) {
return 'Masukkan Maklumat Kerosakan';
}
return null;
},
decoration: InputDecoration(
labelText: "MAKLUMAT",
hintText: "Masukkan maklumat kerosakan"),
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
RaisedButton(
child: Icon(Icons.image),
onPressed: getImageGallery,
),
SizedBox(width: 5.0),
RaisedButton(
child: Icon(Icons.camera_alt),
onPressed: getImageCamera,
),
],
),
SizedBox(height: 10.0),
Container(
alignment: Alignment.centerLeft,
child: _image == null
? new Text("Tiada imej !")
: new Image.file(_image),
),
SizedBox(height: 10.0),
Container(
height: 45.0,
child: GestureDetector(
onTap: () {
if (_key.currentState.validate()) {
tambahaduan(_image);
}
},
child: Material(
borderRadius: BorderRadius.circular(10.0),
color: Colors.blueAccent,
elevation: 7.0,
child: Center(
child: Text(
'HANTAR',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
),
),
),
),
],
),
),
),
),
);
}
Future getImageGallery() async {
var imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
int rand = new Math.Random().nextInt(100000);
Img.Image image = Img.decodeImage(imageFile.readAsBytesSync());
Img.Image smallerImg = Img.copyResize(image, width: 500);
var compressImg = new File("$path/image_$rand.jpg")
..writeAsBytesSync(Img.encodeJpg(smallerImg, quality: 85));
setState(() {
_image = compressImg;
});
}
Future getImageCamera() async {
var imageFile = await ImagePicker.pickImage(source: ImageSource.camera);
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
int rand = new Math.Random().nextInt(100000);
Img.Image image = Img.decodeImage(imageFile.readAsBytesSync());
Img.Image smallerImg = Img.copyResize(image, width: 500);
var compressImg = new File("$path/image_$rand.jpg")
..writeAsBytesSync(Img.encodeJpg(smallerImg, quality: 85));
setState(() {
_image = compressImg;
});
}
Future tambahaduan(File _image) async {
var stream = new http.ByteStream(DelegatingStream.typed(_image.openRead()));
var length = await _image.length();
var uri = Uri.parse((BaseUrl.tambahaduan()));
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile("aduanimages", stream, length,
filename: basename(_image.path));
request.fields['fasiliti_id'] = controllerfasiliti_id.text;
request.fields['ruang_id'] = controllerruang_id.text;
request.fields['maklumat'] = controllermaklumat.text;
request.fields['idpengguna'] = widget.idpengguna;
request.files.add(multipartFile);
var response = await request.send();
if (response.statusCode == 200) {
print("Image Uploaded");
setState(
() {
Fluttertoast.showToast(
msg: "Aduan Berjaya",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 18.0,
);
Navigator.of(this.context).push(CupertinoPageRoute(
builder: (BuildContext context) => SejarahAduan()));
},
);
} else {
print("Upload Failed");
}
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}
}
this is my error
List<RuangFasiliti> ruangfasiliti;
Future<RuangFasiliti> getruangfasiliti() async {
final response = await http.get(BaseUrl.lihatruangfasiliti(widget.barcode));
if (response.statusCode == 200) {
var body = jsonDecode(response.body);
setState(() {
if(body is List) //check if it's a List
ruangfasiliti = List<RuangFasiliti>.from(body.map(map) => RuangFasiliti.fromJson(map));
if(body is Map) //check if it's a Map
ruangfasiliti = [RuangFasiliti.fromJson(body)];
controllerruang_id =
new TextEditingController(text: "${ruangfasiliti.ruang_id}");
controllerfasiliti_id =
new TextEditingController(text: "${ruangfasiliti.fasiliti_id}");
controller_namaruang =
new TextEditingController(text: " ${ruangfasiliti.namaruang}");
controller_namafasiliti =
new TextEditingController(text: " ${ruangfasiliti.namafasiliti}");
});
return ruangfasiliti;
}
}
This code allows you to save a List ruangfasiliti, checks if the decoded json is a map or a list and saves it accordingly, but as the other comments said, you should provide and check the form of your JSON file to avoid this types of problems. If your JSON always returns a list it will be easy to just create a List body variable without checking the type of object

Ontap is not working on Google Maps marker in flutter

I am working on a project, i am using google_maps_flutter to show maps and markers on the screen & search_map_place to search places on the map, i have provided the source code below, there is a bug in the source code, whole project is control through bottom navigation, when first time i run the app, i navigate to the map screen, it displays markers from database and onTap also works perfectly, but when i navigate to any other screen and navigate back to map screen again, onTap doesn't work on the markers.
displaying markers on the map:
_onMapCreated() async {
// print(customerInfo);
for (var item in ud.customerInfo) {
if (item['customer_latitude'] == "" && item['customer_longitude'] == "") {
continue;
} else {
final MarkerId markerId = MarkerId((markers.length + 1).toString());
LatLng markerPos = LatLng(double.parse(item['customer_latitude']),
double.parse(item['customer_longitude']));
final Marker marker = Marker(
infoWindow: InfoWindow(
title: item['customer_name'],
snippet: item['customer_desc'],
),
// onTap: () => _onMarkerTapped(markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(
item['customer_status'] == 'red'
? BitmapDescriptor.hueRed
: item['customer_status'] == 'yellow'
? BitmapDescriptor.hueYellow
: BitmapDescriptor.hueGreen),
markerId: markerId,
consumeTapEvents: true,
position: markerPos,
onTap: () {
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context) {
return CustomerDetail(
item['customer_id'],
item['customer_name'],
item['customer_email'],
item['customer_desc'],
item['customer_phone'],
item['customer_status']);
},
fullscreenDialog: true,
));
});
_markers = null;
markers[markerId] = marker;
}
}
setState(() {});
}
complete code:
String _mapStyle;
GoogleMapController controller;
List<Map> customerInfo;
class MapS extends StatefulWidget {
#override
_MapSState createState() => _MapSState();
}
class _MapSState extends State<MapS> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Search Map Place Demo',
home: MainScreen(),
);
}
}
MapType _mapType = MapType.normal;
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
class MapSample extends StatefulWidget {
#override
State<MapSample> createState() => MapSampleState();
}
class MapSampleState extends State<MapSample> {
#override
void initState() {
super.initState();
getCustomerInfo();
rootBundle.loadString('assets/map_style.txt').then((string) {
_mapStyle = string;
});
getCurrentLocation();
setCustomMapPin();
// _onMapCreated(controller);
setState(() {
_onMapCreated();
});
}
Future getCustomerInfo() async {
// getting customer info using saleManagerID
var customerInfoUrl = "http://.../calender/getCustomerInfo.php";
customerInfoResponse = await http.post(customerInfoUrl, body: {
"sm_id": sm.sm_id,
});
if (jsonDecode(customerInfoResponse.body) == "No Customers") {
String noCustomers = jsonDecode(customerInfoResponse.body);
ud.customerInfo = [];
} else {
ud.customerInfo = jsonDecode(customerInfoResponse.body);
print('user data got!');
}
// print(ud.customerInfo);
}
Widget _mapTypeSwitcher() => Align(
alignment: const Alignment(0.95, -0.7),
child: FloatingActionButton(
mini: true,
heroTag: null,
tooltip: 'Map Type',
child: const Icon(Icons.layers),
onPressed: () {
final MapType nextType =
MapType.values[_mapType.index == 2 ? 1 : 2];
setState(() {
print(nextType.toString());
_mapType = nextType;
});
},
),
);
_onMapCreated() async {
// print(customerInfo);
for (var item in ud.customerInfo) {
if (item['customer_latitude'] == "" && item['customer_longitude'] == "") {
continue;
} else {
final MarkerId markerId = MarkerId((markers.length + 1).toString());
LatLng markerPos = LatLng(double.parse(item['customer_latitude']),
double.parse(item['customer_longitude']));
final Marker marker = Marker(
infoWindow: InfoWindow(
title: item['customer_name'],
snippet: item['customer_desc'],
),
// onTap: () => _onMarkerTapped(markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(
item['customer_status'] == 'red'
? BitmapDescriptor.hueRed
: item['customer_status'] == 'yellow'
? BitmapDescriptor.hueYellow
: BitmapDescriptor.hueGreen),
markerId: markerId,
consumeTapEvents: true,
position: markerPos,
onTap: () {
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context) {
return CustomerDetail(
item['customer_id'],
item['customer_name'],
item['customer_email'],
item['customer_desc'],
item['customer_phone'],
item['customer_status']);
},
fullscreenDialog: true,
));
});
_markers = null;
markers[markerId] = marker;
}
}
setState(() {});
}
void getCurrentLocation() async {
Position res = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(() {
position = res;
// _child = mapWidget();
print(res);
mapToggle = true;
});
}
Completer<GoogleMapController> mapController = Completer();
Set<Marker> _markers = Set();
// custom marker
BitmapDescriptor pinLocationIcon;
void setCustomMapPin() async {
pinLocationIcon = await BitmapDescriptor.fromAssetImage(
ImageConfiguration(devicePixelRatio: 4.5), 'assets/red_marker.png');
}
IconData fontAwesomeIconFromString(String name) {
switch (name) {
case 'website':
return Icons.web;
case 'phone_no':
return Icons.phone_android;
case 'desc':
return Icons.lock_outline;
case 'place':
return Icons.place;
case 'city':
return Icons.location_city;
case 'country':
return Icons.my_location;
}
}
String iconfromApi;
// textfield in bottomsheet
Widget _entryField(String title, TextEditingController tcont, String ic) {
iconfromApi = ic;
return Container(
margin: EdgeInsets.symmetric(vertical: 5),
padding: new EdgeInsets.fromLTRB(2.0, 2.0, 2.0, 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: tcont,
// obscureText: isPassword,
style: TextStyle(color: Colors.black, fontFamily: 'Montserrat'),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: title,
prefixIcon: Icon(fontAwesomeIconFromString(iconfromApi)),
labelStyle: TextStyle(fontSize: 15)),
)
],
),
);
}
Future addData(String phone, web) async {
var url = "http://.../addData.php";
var response = await http.post(url, body: {
"name": userD.name,
"desc": userD.desc,
"status": userD.status,
"latitude": userD.latitide,
"longitude": userD.longitude,
"phone": phone != null ? phone : "n/a",
"web": web != null ? web : "n/a",
"sm_id": sm.sm_id
});
// print('Response status: ${response.statusCode}');
// print('Response body: ${response.body}');
response.body == '1' ? confirmDialog(context) : errorDialog(context);
}
Future<void> confirmDialog(BuildContext context) {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Response'),
content: const Text('Record Added!'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
},
),
],
);
},
);
}
Future<void> errorDialog(BuildContext context) {
return showDialog<void>(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Response'),
content: const Text('Record submission failed'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
},
),
],
);
},
);
}
Widget _submitButton() {
return InkWell(
onTap: () {
print(ud.phone);
print(ud.web);
addData(ud.phone, ud.web);
},
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Hexcolor("#2a7837"), Hexcolor("#2a7837")])),
child: Text(
'Save In Database',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
),
);
}
Widget addButon = Container();
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
return Stack(
children: <Widget>[
GoogleMap(
myLocationEnabled: true,
myLocationButtonEnabled: true,
compassEnabled: false,
tiltGesturesEnabled: false,
mapType: _mapType,
initialCameraPosition: CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 15),
onMapCreated: (controller) {
mapController.complete(controller);
_onMapCreated();
// add things here on map, markers, design etc
controller.setMapStyle(_mapStyle);
},
markers:
_markers == null ? Set<Marker>.of(markers.values) : _markers
// markers: Set<Marker>.of(markers.values),
),
_mapTypeSwitcher(),
Positioned(
top: 60,
left: MediaQuery.of(context).size.width * 0.05,
// width: MediaQuery.of(context).size.width * 0.9,
child: SearchMapPlaceWidget(
apiKey: apiKEY,
location: CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 18)
.target,
radius: 30000,
onSelected: (place) async {
// declaring some variables
final geolocation = await place.geolocation;
// final js = geolocation.fullJSON;
controller = await mapController.future;
// get latLong in a
var latLong = geolocation.coordinates;
// to save latLong in array for db
var latLongList = new List(2);
// to save place, city & country in array for db
var loctionList = new List();
controller.animateCamera(
CameraUpdate.newLatLng(geolocation.coordinates));
controller.animateCamera(
CameraUpdate.newLatLngBounds(geolocation.bounds, 0));
print(place.fullJSON);
// converting LatLong to string and then array
latLong = latLong.toString().replaceAll(RegExp("[a-zA-Z]"), '');
latLong =
latLong.toString().replaceAll(RegExp("[\\[\\](){}]"), '');
ud.customerDetials = place.fullJSON['place_id'];
latLongList = latLong.split(', ');
print(latLongList[0]);
print(latLongList[1]);
// print(prettyJson(place.fullJSON, indent: 2));
// print(a.runtimeType);
// adding retrived values to object
userD.name = place.fullJSON['structured_formatting']['main_text'];
// place.fullJSON['structured_formatting']['secondary_text'] == null
// ? userD.desc = c_desc.text
// : userD.desc =
// place.fullJSON['structured_formatting']['secondary_text'];
// c_phone_no.text = c_place.text =
// c_city.text = c_country.text = c_desc.text = '';
// userD.place = c_place.text;
// userD.city = c_city.text;
// userD.country = c_country.text;
userD.phone = ud.phone;
userD.web = ud.web;
userD.latitide = latLongList[0];
userD.longitude = latLongList[1];
userD.status = "red";
setState(() {
markers = <MarkerId, Marker>{};
_markers = new Set();
_markers.add(
Marker(
markerId: MarkerId(place.fullJSON['place_id']),
position: geolocation.coordinates,
icon: pinLocationIcon,
infoWindow: InfoWindow(
title: place.fullJSON['structured_formatting']
['main_text'],
snippet: place.fullJSON['structured_formatting']
['secondary_text'])),
);
// markers = null;
});
// print(js);
// savejson(js);
addButon = new RawMaterialButton(
onPressed: () async {
var responseDetails =
await getLocationDetails(place.fullJSON['place_id']);
ud.phone =
responseDetails.data["result"]["formatted_phone_number"];
ud.web = responseDetails.data["result"]["website"];
showStopper(
context: context,
stops: [0.5 * height, height],
builder:
(context, scrollController, scrollPhysics, stop) {
return Padding(
padding: const EdgeInsets.all(18.0),
child: ListView(
controller: scrollController,
physics: scrollPhysics,
children: [
// show location name
Text(
userD.name,
style: TextStyle(
color: Colors.black,
fontSize: 30,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 50,
),
// location desc
Text(
'Description:',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 10,
),
userD.desc == null
? _entryField("Description", c_desc, 'desc')
: Text(
userD.desc,
style: TextStyle(
color: Colors.black,
fontSize: 15,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 20,
),
// add website, phone, place, city, country
Text(
ud.phone != null ? ud.phone : "phone: n/a"),
Text(ud.web != null ? ud.web : "website: n/a"),
// _userInfoWidget(),
// _entryField("Website", c_websitee, 'website'),
SizedBox(
height: 20,
),
_submitButton()
]),
);
});
},
child: new Icon(
Icons.add_location,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
);
},
),
),
// 2 floating buttons
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
addButon,
new RawMaterialButton(
onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute<Null>(
// builder: (BuildContext context) {
// return MyApp();
// },
// fullscreenDialog: true,
// ));
_markers = null;
_onMapCreated();
},
child: new Icon(
Icons.person,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
),
// feedback button
new RawMaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context) {
return FeedBackModal();
},
fullscreenDialog: true,
));
},
child: new Icon(
Icons.feedback,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
),
],
)
],
),
)
],
);
}
Future getLocationDetails(placeId) async {
try {
String requestUrl =
"https://maps.googleapis.com/maps/api/place/details/json?place_id=$placeId&fields=website,formatted_phone_number&key=$apiKEY";
Response placeDetails = await Dio().get(requestUrl);
return placeDetails;
} catch (e) {
print(e);
}
// ud.phone= placeDetails.data["result"]["formatted_phone_number"];
// ud.web= placeDetails.data["result"]["web"];
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
// bottom navigation variables
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
List<Widget> _widgetOptions = <Widget>[
Kpi(),
Customers(),
MapSample(),
AddMeeting(),
Customers(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
fd.indexForFeedback = _selectedIndex;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(fd.indexForFeedback),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.white,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.black,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
title: Text('KPI'),
),
BottomNavigationBarItem(
icon: Icon(Icons.people),
title: Text('Customers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.add_location),
title: Text('Map'),
),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today),
title: Text('Calender'),
),
BottomNavigationBarItem(
icon: Icon(Icons.group_add),
title: Text('Quotes'),
),
],
currentIndex: fd.indexForFeedback,
selectedItemColor: Hexcolor('#2a7837'),
onTap: _onItemTapped,
),
);
}
}
Replace
child: _widgetOptions.elementAt(fd.indexForFeedback),
With
child: IndexedStack(
children: _widgetOptions,
index: fd.indexForFeedback,
),
It prevents from redrawing the map every time you switch between tabs, and avoid memory leaks.
See also: https://www.youtube.com/watch?v=_O0PPD1Xfbk

issues with input length and buttonclicks

I'm working on a simple math-app for my daughter. Below is some code that you can use if you like (it may not be the most beautiful code, but it works ok and maybe it helps someone).
My issues are:
1) To limit the number of characters (numbers) that the user can type. I only find solutions about this when it comes to textFields (such as maxLength).
2) My refresh-button ("NEXT") is not working at all. The idea is to give the user a new random math-task from the previous arithmetic choice.
3) Currently you can type the wrong answer, click ok and then correct your answer to get a "Correct". The plan is that once you've clicked ok, you won't be able to change your answer. You will only be able to click NEXT. (I plan to implement a counter to this later, that will return the number of correct and false answers after x numbers of tasks).
Any help is highly appreciated (code or what I shall look at). Thank you.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) => runApp(HomePage()));
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'input',
theme: ThemeData(primarySwatch: Colors.purple),
home: FirstClass(),
);
}
}
class FirstClass extends StatefulWidget {
#override
_FirstClassState createState() => _FirstClassState();
}
class _FirstClassState extends State<FirstClass> {
final random = Random();
int a, b, c, sum;
String output;
void changeData(String buttonName) {
setState(() {
a = random.nextInt(10);
b = random.nextInt(10);
if (buttonName == '+') {
sum = a + b;
output = '$a+$b= ';
} else if (buttonName == '-') {
if (a >= b) {
sum = a - b;
output = '$a-$b= ';
} else if (b > a) {
sum = b - a;
output = '$b-$a= ';
}
}
print(sum.toString());
Navigator.of(context).popUntil(ModalRoute.withName('/'));
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => SecondClass(
sum: sum,
refresh: changeData,
output: output,
buttonName: buttonName,
)));
});
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.deepPurple),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0.0,
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
title: Text(
'MATH',
style: TextStyle(fontSize: 25.0),
),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
child: Text('+', style: TextStyle(fontSize: 24.0)),
onPressed: () => changeData('+')),
RaisedButton(
child: Text('-', style: TextStyle(fontSize: 24.0)),
onPressed: () => changeData('-')),
],
),
),
),
);
}
}
class SecondClass extends StatefulWidget {
final int sum;
final String output;
final String buttonName;
final Function refresh;
SecondClass({this.sum, this.refresh, this.buttonName, this.output});
#override
_SecondClassState createState() => _SecondClassState();
}
class _SecondClassState extends State<SecondClass> {
String output = "";
String _output = "";
String output2 = "";
#override
void initState() {
super.initState();
}
buttonPressed(String buttonText) {
if (buttonText == "<-") {
_output = "";
} else if (buttonText == "OK") {
if (output.isNotEmpty) {
if (output == widget.sum.toString()) {
setState(() {
output2 = 'Correct';
});
} else {
setState(() {
output2 = 'False';
});
}
} else if (buttonText == "NEXT") {
widget.refresh(widget.buttonName);
}
} else {
_output = _output + buttonText;
}
setState(() {
output = _output;
});
print(buttonText);
}
Widget buildButton(String buttonText) {
return Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
child: OutlineButton(
color: Colors.white,
child: Text(
buttonText,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold),
),
onPressed: () => buttonPressed(buttonText)),
),
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.purple),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 40.0, left: 20.0, right:
20.0),
child: Container(
height: 60.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0)),
child: Center(
child: Text(
widget.output + output,
style: TextStyle(
color: Colors.black,
fontSize: 48.0,
fontWeight: FontWeight.bold),
),
)),
),
Padding(
padding: const EdgeInsets.only(top: 20.0, left: 20.0, right:
20.0),
child: Container(
height: 60.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0)),
child: Text(
output2,
style: TextStyle(
color: Colors.black,
fontSize: 48.0,
fontWeight: FontWeight.bold),
),
),
),
Expanded(child: Divider()),
Column(
children: <Widget>[
Row(
children: <Widget>[
buildButton('1'),
buildButton('2'),
buildButton('3'),
],
),
Row(
children: <Widget>[
buildButton('4'),
buildButton('5'),
buildButton('6'),
],
),
Row(
children: <Widget>[
buildButton('7'),
buildButton('8'),
buildButton('9'),
],
),
Row(
children: <Widget>[
buildButton('<-'),
buildButton('0'),
buildButton('OK'),
],
),
Row(
children: <Widget>[
buildButton('NEXT'),
],
),
],
),
],
),
);
}
}
Well It's working. It's not the best way to do this but I tried keep your old code base.I really advise make use of some patterns like BLoC to manage the state of the widgets and avoid setState calls.
Was needed split your code in some more classes.
What I have done here??
Create a enum called MathOperation. This enum is used to identify what kind of arithmetic operation the user selected.
The older FirstClass is now OperationSelectorScreen
The older SecondClass is now QuestionAndAnswerScreen
I've created CustomTextField class to implement a simple 'text field' with a max number of characters.
I've implmented MathOperationTask class. This class is an abstraction for arithmetics operations like sum and subtraction. This class holds the numbers involved in operation, his symbol "+" or "-" and the operation results.
There is some comments in source code to guide you, if you need to ask for something feel free to do I will answer when I can. I hope it helps.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// enum used to identify math operation types
enum MathOperation { SUM, SUBTRACTION }
void main() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) => runApp(HomePage()));
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'input',
theme: ThemeData(primarySwatch: Colors.purple),
home: OperationSelectorScreen(),
);
}
}
/// old FirstClass
class OperationSelectorScreen extends StatefulWidget {
#override
_OperationSelectorScreenState createState() => _OperationSelectorScreenState();
}
class _OperationSelectorScreenState extends State<OperationSelectorScreen> {
MathOperation _userSelectedOperation;
void changeData(String buttonName) {
setState(() {
if (buttonName == '+') {
_userSelectedOperation = MathOperation.SUM;
} else if (buttonName == '-') {
_userSelectedOperation = MathOperation.SUBTRACTION;
}
Navigator.of(context).popUntil(ModalRoute.withName('/'));
/// we only need pass to next screen what kind of operation
/// was selected by user
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => QuestionAndAnswerScreen(
operationType: _userSelectedOperation,// operation selected by user ( '+' or '-' )
)));
});
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.deepPurple),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0.0,
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
title: Text(
'MATH',
style: TextStyle(fontSize: 25.0),
),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
child: Text('+', style: TextStyle(fontSize: 24.0)),
onPressed: () => changeData('+')),
RaisedButton(
child: Text('-', style: TextStyle(fontSize: 24.0)),
onPressed: () => changeData('-')),
],
),
),
),
);
}
}
/// old SecondClass
class QuestionAndAnswerScreen extends StatefulWidget {
final operationType; // if the operations will be (+) or (-)
QuestionAndAnswerScreen({this.operationType});
#override
_QuestionAndAnswerScreenState createState() => _QuestionAndAnswerScreenState();
}
class _QuestionAndAnswerScreenState extends State<QuestionAndAnswerScreen> {
String _userTypedAnswer=""; // numbers that user had typed
String _answerValidationOutput = ""; // will say if the user answer ir correct or wrong(false)
MathOperationTask _currentTask; // current arithmetic operation
// this member controls the back button "<-" activity
// if user had responded the question, this value will be true and
// the button "<-" will not work properly.
bool _isQuestionResponded = false;
#override
void initState() {
super.initState();
//using math operation task generator method to create a new math operation
_currentTask = MathOperationTask.generateMathTask( widget.operationType );
}
buttonPressed(String buttonText) {
// this logic can be improved if we
// transform the custom keyboard in a widget
if (buttonText == "<-") {
if (!_isQuestionResponded){
_changeUserTypedText("");
_changeAnswerValidationText("");
}
}
else if (buttonText == "OK") {
if (_userTypedAnswer.isNotEmpty) {
_isQuestionResponded = true;
if (_userTypedAnswer == _currentTask.results.toString()) {
_changeAnswerValidationText('Correct');
}
else {
_changeAnswerValidationText('False');
}
}
}
else if (buttonText == "NEXT") {
print("new OP");
_spawnArithmeticOperation();
}
else {
///This if statement solves the problem of put in member after question
///responded.If question is NOT responded, OK button not pressed then we update the text.
if (!_isQuestionResponded)
_changeUserTypedText( (_userTypedAnswer + buttonText) );
}
}
/// this mehtod creates a new arithmetic operation and update the screen with
void _spawnArithmeticOperation(){
_currentTask = MathOperationTask.generateMathTask(widget.operationType);
_answerValidationOutput ="";
_userTypedAnswer = "";
_isQuestionResponded = false;
setState(() {});
}
/// method to change and update UI after user type something.
void _changeUserTypedText(String text){
setState(() => _userTypedAnswer = text );
}
/// update the text if the answer is correct, wrong or clean the text.
void _changeAnswerValidationText(String text){
setState(() => _answerValidationOutput = text );
}
Widget buildButton(String buttonText) {
return Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, bottom: 8.0),
child: OutlineButton(
color: Colors.white,
child: Text(
buttonText,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold),
),
onPressed: () => buttonPressed(buttonText)),
),
);
}
#override
Widget build(BuildContext context) {
final operationField = CustomTextField( maxLength: 7, // max text length
text: "${_currentTask.firstMember}" // first member of operation
"${_currentTask.operationSymbol}" // operation signal
"${_currentTask.secondMember}= " // second member of math operation
"$_userTypedAnswer",
);
final answerFinalResultsField = CustomTextField(
maxLength: 7,
text: _answerValidationOutput
);
return Container(
decoration: BoxDecoration(color: Colors.purple),
child: Column(
children: <Widget>[
operationField,
answerFinalResultsField,
Expanded(child: Divider()),
_buildKeyboard(),
],
),
);
}
// here i put your keyboard layout..
Widget _buildKeyboard(){
return Column(
children: <Widget>[
Row(
children: <Widget>[
buildButton('1'),
buildButton('2'),
buildButton('3'),
],
),
Row(
children: <Widget>[
buildButton('4'),
buildButton('5'),
buildButton('6'),
],
),
Row(
children: <Widget>[
buildButton('7'),
buildButton('8'),
buildButton('9'),
],
),
Row(
children: <Widget>[
buildButton('<-'),
buildButton('0'),
buildButton('OK'),
],
),
Row(
children: <Widget>[
buildButton('NEXT'),
],
),
],
);
}
}
/// this class represents an arithmetic operation
/// example 3 + 6 = 9
/// 3 is the firstMember, 6 the secondMember and results is 9.
class MathOperationTask {
final firstMember;
final secondMember;
final results; //operation results
final operationSymbol;
// text math symbols constants only to show...
static final String PLUS = "+";
static final String LESS = "-";
MathOperationTask( {this.firstMember, this.secondMember, this.results, this.operationSymbol} );
/// this method is used to generate a specific math task from a specific type.
static MathOperationTask generateMathTask( MathOperation type ){
var random = Random();
var firstMember = random.nextInt(10);// 0..9
var secondMember = random.nextInt(10);
switch(type){
case MathOperation.SUM:
return MathOperationTask(
firstMember: firstMember,
secondMember: secondMember,
results: (firstMember + secondMember),
operationSymbol: PLUS
);
case MathOperation.SUBTRACTION:
var results;
if (firstMember < secondMember) {
// we exchange the values position in operation...
var temp = firstMember;
firstMember = secondMember;
secondMember = temp;
}
results = firstMember - secondMember;
return MathOperationTask(
results: results,
secondMember: secondMember,
firstMember: firstMember,
operationSymbol: LESS,
);
default:
break;
}
//in case of invalid operation...
return MathOperationTask(
firstMember: 0,
secondMember: 0,
results: 0,
);
}
}
/// A simple custom text field that limits his text
/// with a specific characters number
///
class CustomTextField extends StatelessWidget {
final maxLength;
final _text;
CustomTextField({this.maxLength = 7, String text}) :
_text = (text.length > maxLength) ? text.substring(0, maxLength ) : text;
/// this lines solves characters numbers problem
///if the text contains more characters that is allowed (maxLength) then we
/// cut the string form character 0 until last position allowed (maxLength).
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40.0, left: 20.0, right: 20.0),
child: Container(
height: 60.0,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0)),
child: Center(
child: Text(
_text,
style: TextStyle(
color: Colors.black,
fontSize: 48.0,
fontWeight: FontWeight.bold),
),
),
),
);
}
}