Flutter how can NOT reload FutureBuilder when state is updated? - json

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

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

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

How to fix " [ERROR: The method '[]' was called on null. E/flutter (18386): Receiver: null"flutter

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'Models/UserData.dart';
class LoginScreen extends StatefulWidget {
static const String id = '/login_screen';
#override
State<StatefulWidget> createState() {
return _LoginPageState();
}
}
class _LoginPageState extends State<LoginScreen> {
static var url = "url";
static BaseOptions options = BaseOptions(
baseUrl: url,
responseType: ResponseType.plain,
connectTimeout: 30000,
receiveTimeout: 30000,
validateStatus: (code) {
if (code >= 200) {
return true;
}
});
static Dio dio = Dio(options);
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _emailController = TextEditingController();
TextEditingController _passwordController = TextEditingController();
UserData userData;
#override
void initState() {
SystemChrome.setEnabledSystemUIOverlays([]);
userData = UserData();
super.initState();
}
Future<Map<String, dynamic>> _loginUser(String email, String password, String version) async {
try {
Options options = Options(
contentType: ContentType.parse('application/json'),
);
final Response response = await dio.post<Map<String, dynamic>>(url + '/users/login',
data: {'login': _emailController, 'pwd': _passwordController, 'version' :'2.0'}, options: options);
print(url + '/users/login');
if (response.statusCode == 200 || response.statusCode == 201) {
return json.decode(response.data);
} else if (response.statusCode == 401) {
throw Exception("Incorrect Email/Password");
} else
throw Exception('Authentication Error');
} on DioError catch (exception) {
if (exception == null ||
exception.toString().contains('SocketException')) {
throw Exception("Network Error");
} else if (exception.type == DioErrorType.RECEIVE_TIMEOUT ||
exception.type == DioErrorType.CONNECT_TIMEOUT) {
throw Exception(
"Could'nt connect, please ensure you have a stable network.");
} else {
return null;
}
}
}
#override
Widget build(BuildContext context) {
bool _isLoading = false;
String version = '2.0';
bool _obscureText = true;
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
width: MediaQuery
.of(context)
.size
.width,
height: MediaQuery
.of(context)
.size
.height / 2.5,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.white, Colors.white],
),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(90),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(),
Align(
alignment: Alignment.center,
child: Image.asset('assets/logo.png'),
),
Spacer(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(bottom: 32, right: 32),
child: Text(
'Customer App',
style: TextStyle(color: Colors.red, fontSize: 18),
),
),
),
],
),
),
Container(
color: Colors.white,
height: MediaQuery
.of(context)
.size
.height / 1,
width: MediaQuery
.of(context)
.size
.width,
padding: EdgeInsets.only(top: 20),
child: Column(
children: <Widget>[
Container(
width: MediaQuery
.of(context)
.size
.width / 1.2,
height: 45,
padding:
EdgeInsets.only(top: 2, left: 16, right: 16, bottom: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50)),
color: Colors.white,
boxShadow: [
BoxShadow(color: Colors.red, blurRadius: 5)
]),
child: TextField(
controller: _emailController,
decoration: InputDecoration(
errorText: _isLoading ? 'Value Can\'t Be Empty' : null,
border: InputBorder.none,
icon: Icon(
Icons.email,
color: Colors.red,
),
hintText: 'Email',
),
keyboardType: TextInputType.emailAddress,
),
),
const SizedBox(
width: 42.0,
height: 20.0,
),
Container(
width: MediaQuery
.of(context)
.size
.width / 1.2,
height: 45,
padding:
EdgeInsets.only(top: 2, left: 16, right: 16, bottom: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50)),
color: Colors.white,
boxShadow: [
BoxShadow(color: Colors.red, blurRadius: 8)
]),
child: TextField(
obscureText: _obscureText,
controller: _passwordController,
inputFormatters: [LengthLimitingTextInputFormatter(10)],
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(
Icons.lock_open,
color: Colors.red,
),
hintText: 'Password',
),
keyboardType: TextInputType.text,
),
),
Container(
height: 45,
margin: EdgeInsets.only(top: 22),
width: MediaQuery
.of(context)
.size
.width / 1.2,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Center(
child: InkWell(
onTap: () async {
FocusScope.of(context).requestFocus(new FocusNode());
setState(() => _isLoading = true,);
Navigator.of(context)
.pushReplacementNamed('/home_screen');
var res = await _loginUser(
_emailController.text, _passwordController.text,version);
final UserData users = UserData.fromJson(res);
List<Map> items = json.decode("response.body");
List<UserData> listOfDesig = items.map((json) => UserData.fromJson(json)).toList();
if (listOfDesig != null) {
Navigator.of(context)
.pushReplacementNamed('/home_screen');
} else {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("Wrong email")));
}
},
child: Text(
'Login'.toUpperCase(),
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
),
),
),
],
),
),
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.only(top: 20, right: 0),
child: Text(
'Forgot Password ?',
style: TextStyle(color: Colors.grey),
),
),
),
],
),
),
);
}
}
class UserData {
String customerid;
String profileid;
String branch;
String bookername;
String emailid;
String mobileno;
int cutofftime;
String ppnames;
String paymenttype;
String usertype;
int ptopCutoffTime;
String designation;
String designationid;
UserData({
this.customerid,
this.profileid,
this.branch,
this.bookername,
this.emailid,
this.mobileno,
this.cutofftime,
this.ppnames,
this.paymenttype,
this.usertype,
this.ptopCutoffTime,
this.designation,
this.designationid,
});
factory UserData.fromJson(Map<String, dynamic> json) => UserData(
customerid: json["customerid"],
profileid: json["profileid"],
branch: json["branch"],
bookername: json["bookername"],
emailid: json["emailid"],
mobileno: json["mobileno"],
cutofftime: json["cutofftime"],
ppnames: json["ppnames"],
paymenttype: json["paymenttype"],
usertype: json["usertype"],
ptopCutoffTime: json["ptopCutoffTime"],
designation: json["designation"],
designationid: json["designationid"],
);
}
This is my flutter code and I'm new to flutter.
Error Message -
[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception:
NoSuchMethodError: The method '[]' was called on null. E/flutter
(18386): Receiver: null E/flutter (18386): Tried calling:
E/flutter (18386): #0 Object.noSuchMethod
(dart:core-patch/object_patch.dart:50:5) E/flutter (18386): #1
new UserData.fromJson
(package:/Models/UserData.dart:33:21)
The way I see your code the Error is from this
#override
void initState() {
SystemChrome.setEnabledSystemUIOverlays([]);
userData = UserData(); //What cause the error.
super.initState();
}
Why? Because You haven't put value into your UserData class after getting it from the Server. Try to collect your data from the server if the authentication is valid. with UserData.fromJson(json.decode(response.body));
Am using http not Dio here, But try to convert it to Dio by your previous code.
Future<UserData> getUserData() async{
final SharedPreferences prefs = await SharedPreferences.getInstance();
String email = prefs.getString('Email');
String session = prefs.getString('session');
var map = new Map<String, String>();
map["email"] = email;
map["sesscode"] = session;
var response = await http.post(new API().userdata, body: map);
var convertDataToJson = json.decode(response.body);
UserData usdata = UserData .fromJson(convertDataToJson); //THis solve your error
return usdata ;
}
It seems that you send null when you call UserData.fromJson(Map<String, dynamic> json). Check if json != null before calling UserData.fromJson
In case if you are using google APIs you should not restrict the API keys. That was my mistake. Happy coding
This error happens, if you are using a class variable without initializing.
Check weather you have initialized all the variables before using it.

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