issues with input length and buttonclicks - function

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),
),
),
),
);
}
}

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(),),
);
}

Flutter how can NOT reload FutureBuilder when state is updated?

I'm studying flutter by myself.
trying to make doto Calendar with carousel calendar.
I want to make date number big when there's event
-> it's done
the problem is
# line 238
if I put code like below, the calendar is keep refreshing whenever I press date button.
return snapshot.hasData && _markedDateMap.events.length > 0 ...
if I put code like below, my lovely big big date buttons becomes small!!
return snapshot.hasData ...
is there any solution for this problem?
please help me to stop the future builder is keep building!
TT
enter image description here
import 'package:flutter/material.dart';
import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart'
show CalendarCarousel;
import 'package:flutter_calendar_carousel/classes/event.dart';
import 'package:flutter_calendar_carousel/classes/event_list.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
Future<List<ipoData>> fetchIPODatas(http.Client client) async {
final response = await client.get('http://realchord.net/ipo/getIPOData.php');
// Use the compute function to run parseIpoData in a separate isolate.
return compute(parseIpoData, response.body);
}
// A function that converts a response body into a List<Photo>.
List<ipoData> parseIpoData(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<ipoData>((json) => ipoData.fromJson(json)).toList();
}
// ignore: camel_case_types
class ipoData {
final String ipoDate;
final String company;
ipoData({this.ipoDate, this.company});
factory ipoData.fromJson(Map<String, dynamic> json) => ipoData(
ipoDate: json['date'],
company: json['company'],
);
}
// factory Model.fromJson(Map<String, dynamic> json) => Model(
// value1: json['key1'],
// value2: json['key2'],
// value3: json['key3'],
// value4: json['key4'],
// );
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'IPO calendar',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'IPO calendar'));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DateTime _currentDate = new DateTime.now();
DateTime _currentDate2 = new DateTime.now();
String _currentMonth = DateFormat.MMMM().format(new DateTime.now());
DateTime _next = new DateTime(
DateTime.now().year, DateTime.now().month + 1, DateTime.now().day);
DateTime _prev = new DateTime(
DateTime.now().year, DateTime.now().month - 1, DateTime.now().day);
String _nextMonth;
String _prevMonth;
DateTime _targetDateTime = new DateTime.now();
// List<DateTime> _markedDate = [DateTime(2018, 9, 20), DateTime(2018, 10, 11)];
EventList<Event> _markedDateMap = new EventList<Event>();
// ignore: unused_field
CalendarCarousel _calendarCarouselNoHeader;
// load data
#override
void initState() {
super.initState();
}
// List response = await fetchIPODatas(http.Client());
#override
Widget build(BuildContext context) {
/// Example Calendar Carousel without header and custom prev & next button
_calendarCarouselNoHeader = CalendarCarousel<Event>(
onDayPressed: (DateTime date, List<Event> events) {
this.setState(() => _currentDate2 = date);
// events.forEach((event) => print(event.title));
},
//달력 기본 설정
daysHaveCircularBorder: true,
showOnlyCurrentMonthDate: false,
showHeader: false,
customGridViewPhysics: NeverScrollableScrollPhysics(),
height: 500.0,
daysTextStyle: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.white, //weekend day font color
),
weekendTextStyle: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.white, //weekend day font color
),
weekdayTextStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
// thisMonthDayBorderColor: Colors.grey, // border color of each day
weekFormat: false,
// firstDayOfWeek: 4,
markedDatesMap: _markedDateMap,
selectedDateTime: _currentDate2,
targetDateTime: _targetDateTime,
minSelectedDate: _currentDate.subtract(Duration(days: 360)),
maxSelectedDate: _currentDate.add(Duration(days: 360)),
// //이벤트가 있는 날에 대한 설정
// markedDateCustomShapeBorder:
// CircleBorder(side: BorderSide(color: Colors.yellow)),
markedDateCustomTextStyle: TextStyle(
fontSize: 30,
color: Colors.white,
),
// markedDateIconBorderColor: Colors.white,
// markedDateMoreShowTotal: true,
//오늘에 대한 설정
todayTextStyle: TextStyle(
color: Colors.white,
),
todayButtonColor: Colors.white24,
todayBorderColor: null,
// 선택한 날에 대한 설정
selectedDayTextStyle: TextStyle(
color: Colors.black,
),
selectedDayButtonColor: Colors.white,
// selectedDayBorderColor: Colors.white,
// 지난 달에 대한 설정
prevDaysTextStyle: TextStyle(
fontSize: 16,
color: Colors.white38,
),
// 다음 달에 대한 설정
nextDaysTextStyle: TextStyle(
fontSize: 16,
color: Colors.white38,
),
inactiveDaysTextStyle: TextStyle(
color: Colors.white38,
fontSize: 16,
),
onCalendarChanged: (DateTime date) {
this.setState(() {
_targetDateTime = date;
_currentMonth = DateFormat.MMMM().format(_targetDateTime);
_next = new DateTime(_targetDateTime.year, _targetDateTime.month + 1,
_targetDateTime.day);
_nextMonth = DateFormat.MMMM().format(_next);
_prev = new DateTime(_targetDateTime.year, _targetDateTime.month - 1,
_targetDateTime.day);
_prevMonth = DateFormat.M().format(_prev);
});
},
onDayLongPressed: (DateTime date) {
print('long pressed date $date');
},
);
_markedDateMap.clear();
_nextMonth = DateFormat.MMMM().format(_next);
_prevMonth = DateFormat.M().format(_prev);
// dotColorList.add(Colors.white);
return new Scaffold(
body: FutureBuilder<List<ipoData>>(
future: fetchIPODatas(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
final List<ipoData> ipoDataList = snapshot.data;
for (var i = 0; i < snapshot.data.length; i++) {
DateTime dt = DateTime.parse(ipoDataList[i].ipoDate.toString());
_markedDateMap.add(
dt,
new Event(
date: dt,
title: ipoDataList[i].company,
dot: Container(
margin: EdgeInsets.symmetric(horizontal: 1.0),
color: Colors.red.shade300,
height: 5.0,
width: 5.0,
),
));
}
}
return snapshot.hasData && _markedDateMap.events.length > 0
? SingleChildScrollView(
child: Stack(children: [
Container(
padding: EdgeInsets.only(top: 50),
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//custom icon without header
Container(
//display Month
margin: EdgeInsets.only(
top: 20.0,
bottom: 16.0,
left: 0.0,
right: 0.0,
),
padding: EdgeInsets.all(0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FlatButton(
padding: EdgeInsets.all(0),
child: Text(_prevMonth,
// softWrap: false,
maxLines: 1,
textAlign: TextAlign.right,
style: TextStyle(
color: Colors.white12,
fontSize: 48,
fontWeight: FontWeight.bold,
)),
onPressed: () {
setState(() {
_targetDateTime = DateTime(
_targetDateTime.year,
_targetDateTime.month - 1);
_currentMonth = DateFormat.MMMM()
.format(_targetDateTime);
});
},
),
Text(
_currentMonth,
maxLines: 1,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 48.0,
),
),
FlatButton(
child: Text(
_nextMonth,
softWrap: true,
maxLines: 1,
style: TextStyle(
color: Colors.white12,
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
onPressed: () {
setState(() {
_targetDateTime = DateTime(
_targetDateTime.year,
_targetDateTime.month + 1);
_currentMonth = DateFormat.MMMM()
.format(_targetDateTime);
_next = new DateTime(
_targetDateTime.year,
_targetDateTime.month + 1,
_targetDateTime.day);
_nextMonth =
DateFormat.MMMM().format(_next);
_prev = new DateTime(
_targetDateTime.year,
_targetDateTime.month - 1,
_targetDateTime.day);
_prevMonth = DateFormat.M().format(_prev);
});
},
)
],
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16.0),
child: _calendarCarouselNoHeader,
)
],
),
),
Container(
height: MediaQuery.of(context).size.height - 500,
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(top: 500),
padding: const EdgeInsets.only(
top: 40,
left: 15,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white,
),
child: Column(
children: [
EventItem(
company: "LGENSol",
)
],
),
)
]),
)
: Center(child: CircularProgressIndicator());
},
));
}
}
class EventItem extends StatefulWidget {
// final Icon icon;
final String company;
// final Text category;
// final Function() notifyParent;
EventItem(
{
// #required this.icon,
#required this.company}); //#required this.notifyParent
#override
_EventItemState createState() => _EventItemState();
}
class _EventItemState extends State<EventItem> {
get futureAlbum => null;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Row(
children: [
Icon(
Icons.favorite,
color: Colors.pink,
size: 24.0,
),
Padding(
padding: EdgeInsets.all(12.0),
child: Text(widget.company,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
)),
),
Padding(
padding: EdgeInsets.all(5.0),
child: Text("(청약)",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
)),
),
],
),
Row(
children: [
Padding(
padding: EdgeInsets.only(left: 37),
child: Text("공모가:20,000원 | 상장:8/20 | 주관:키움",
style: TextStyle(
fontSize: 20,
)),
),
],
)
],
);
}
}
Every time you call setState in your stateful widget, you're calling that widget's build method, and since your FutureBuilder is part of it then it will get called. It doesn't seem like you're changing the future so I'd suggest you could bring the FutureBuilder a level higher and put your stateful widget inside the FutureBuilder
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: fetchIPODatas(http.Client()),
builder: (context, snapshot) {
//... (add your extracted stateful widget in here..)
},
),
);
}
}

How to put the result of a function into a Text widget in Flutter?

I am new in this language and I am working on a BMI(body mass index) app. As you see in the picture below:
I take the user input and calculate the result, and print out the result in console. For example:
I/flutter ( 4500): 2.25
I/flutter ( 4500): 20.0 // this is the result of BMI
I/flutter ( 4500): you are at your ideal weight. // this is the explanation
I want to show these results in a Text widget to let user see them. But I do not know how to do it. How can I take the value of the result from a function and add it to interface?
Here is my code, and in code I pointed out where did I stuck. Main function:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'calculation.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'BMI';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title),
centerTitle: true,
backgroundColor: Colors.amber[900],
),
body: Center(
child: MyStatefulWidget(),
),
),
);
}
}
enum SingingCharacter { lafayette, jefferson }
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
SingingCharacter _character = SingingCharacter.lafayette;
double height=1;
double weight=1;
String info1="";
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child:Scrollbar(
child:SingleChildScrollView(
child:Card(
color: Colors.amber[50],
child:Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0, 30, 10, 10),
child: Text("Sex:",
style:TextStyle(fontSize: 24, letterSpacing: 1.0)),
),
ListTile(
title: const Text('Female',
style:TextStyle(fontSize: 18, letterSpacing: 1.0)
),
leading: Radio(
activeColor: Colors.orange,
value: SingingCharacter.lafayette,
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
),
),
ListTile(
title: const Text('Male',
style:TextStyle(fontSize: 18, letterSpacing: 1.0,)
),
leading: Radio(
activeColor: Colors.orange,
value: SingingCharacter.jefferson,
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
),
),
SizedBox(height: 10.0),
Text("Your height:",
style:TextStyle(fontSize: 24, letterSpacing: 1.0)
),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 50, 10),
child: TextField(
decoration: new InputDecoration(labelText: "Your height(cm)"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
], // Only numbers can be entered
onSubmitted: (input1){
if(double.parse(input1)>0){
setState(() => height=double.parse(input1));
print(input1);
}
},
),
),
SizedBox(height: 20),
Text("Your weight:",
style:TextStyle(fontSize: 24, letterSpacing: 1.0)
),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 50, 10),
child: new TextField(
decoration: new InputDecoration(labelText: "Your weight(kg)"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
], // Only numbers can be entered
onSubmitted: (input2){
if (double.parse(input2)>0){
// print(weight);
setState(() {
return weight=double.parse(input2);
});
}
},
),),
SizedBox(height: 10,),
RaisedButton(
padding:EdgeInsets.fromLTRB(20, 5, 20, 5),
onPressed: () async{
await Calculation(height, weight);
// return Calculation.info1 ??? //i don't know how to take info1 from calculation function
},
color: Colors.amber[900],
child:Text(
'Calculate',
style:TextStyle(
color: Colors.white,
fontSize: 30,
letterSpacing: 2.0,
),
),
),
SizedBox(height: 20,),
Text('Results: $height,$weight'),
// Text('Calculation.info1'), // i do not know how to show info in a text box.
],
),
),
),
),
);
}
}
Calculation function;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math';
void Calculation(height,weight) {
double squarevalue = pow(height, 2);
double newsquare = squarevalue / 10000;
String info1="";
print(newsquare);
double value = weight / newsquare;
print(value);
// return value.toString();
if (value < 18.5) {
print("your weight is less than your ideal weight.");
// setState(() => info1="your weight is less than your ideal weight."); //i do not know how to set
// info1 to a new text
// return info1;
}
if (value > 25) {
if (value > 30) {
print("your weight is more than your ideal weight, your health is under risk.");
// info1="your weight is more than your ideal weight, your health is under risk.";
}
else {
print("your weight is more than your ideal weight.");
// info1="your weight is more than your ideal weight.";
}
}
else {
print("you are at your ideal weight.");
// info1="you are at your ideal weight.";
}
}
Instead of returning void from Calculation(height,weight) function, return String and display the string value in the Text Widget.
String Calculation(height,weight) {
....// your code with all conditions
return "you are at your ideal weight."
}
In onpressed function, update the obtained string to the state variable inside setState.
onPressed: () async{
String info = await Calculation(height, weight);
setState(){
infoDisplayedInText = info;
}
},

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