Flutter: How to stop state from changing UI - json

I have some local JSON data that I've parsed into my app. The data contains a few arrays and math questions (building a quiz app), so based on the math there are chapters which I've displayed in a ListView builder, when clicking on one of those it opens a new screen and passes on specific data for that chapter based on the index of the chapter.
In my quiz screen math questions in an array are brought from the JSON data depending on what chapter it is, I have the questions shuffled so that they appear in a random order, issue is when a user tries to answer a question in a provided TextField the keyboard opens as intended but the state of the UI changes which also changes the currently displayed question. How can I stop this?
To see the problem I have in the app here's a video link: https://youtu.be/n_VeT26I8NE
The JSON data has been parsed from a different screen and set in these variables:
List quiz = widget.newData["quiz"];
//Shuffle questions
var randomQuiz = (quiz..shuffle()).first;
If you prefer to read the Dart code from GitHub: https://github.com/BotsheloRamela/classio/blob/main/quiz_screen.dart
Some of the JSON data:
[
{
"chapter": "Exponents & Surds",
"introduction": {
"description": "A number's exponent determines how many times to multiply it.
Exponents can also be called powers or indices. In mathematics, a surd is a value
that cannot be further simplified into a whole number or integer. They are
irrational numbers.",
"videoID": ["568dGLFTom8", "XZRQhkii0h0"]
},
"quiz": [
{
"question": "Simplify the follwoing.",
"sum": "2a^2.3a^2.b^0",
"answer": "6a^5",
"explaination": ""
},
{
"question": "Rewrite the following with prime bases, your answer should be a fraction.",
"sum": "16.8^{-4}",
"answer": "1/256",
"explaination": ""
},
{
"question": "Solve the following, your answer should be a fraction.",
"sum": "\\frac{(-3a^3 b)^2}{a^5 b^3}",
"answer": "9a/b",
"explaination": ""
},
{
"question": "Rewrite the following with prime bases and simplify, your answer should be a fraction.",
"sum": "(\\frac{2x^3}{8y^{-4}})^{-3}",
"answer": "\\frac{64}{x^9 y^12}",
"explaination": ""
}
]
},
{
"chapter": "Equations & Inequalities",
"introduction": {
"description": "Equations and inequalities are both mathematical sentences made up of two expressions related to one another. The symbol = indicates that two expressions are regarded as equal within an equation. While in an inequality, two terms are not necessarily equal, which is indicated by the following symbols: >, <, ≤ or ≥.",
"videoID": ["hJ-_OoCHTks"]
},
"quiz": [
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2+2x+1",
"answer": "(x+1)^2",
"explaination": ""
},
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2-6x+9",
"answer": "(x-3)^2",
"explaination": ""
},
{
"question": "Show that the following trinomial is a perfect square.",
"sum": "x^2+8x+16",
"answer": "(x+4)^2",
"explaination": ""
}
]
},
]
Dart code:
import 'package:classio_students/bottom_nav.dart';
import 'package:classio_students/colors.dart';
import 'package:classio_students/screens/learn/maths_formula_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:page_transition/page_transition.dart';
// ignore: must_be_immutable
class Gr11MathQuiz extends StatefulWidget {
var newData;
Gr11MathQuiz({Key? key, required this.newData}) : super(key: key);
#override
_Gr11MathQuizState createState() => _Gr11MathQuizState();
}
class _Gr11MathQuizState extends State<Gr11MathQuiz> {
final TextEditingController _answerController = TextEditingController();
#override
void initState() {
super.initState();
//SystemChannels.textInput.invokeMethod('TextInput.show');
}
#override
Widget build(BuildContext context) {
//Creates a list from the JSON data & shuffles the data quiz in a random order everytime the state changes
List quiz = widget.newData["quiz"];
var randomQuiz = (quiz..shuffle()).first;
//Check answer function
checkAnswer() async {
if (_answerController.text.toString() !=
randomQuiz['answer'].toString()) {
Fluttertoast.showToast(
msg: 'Are you sure? Take a look at your answer.',
gravity: ToastGravity.TOP,
backgroundColor: Colors.orangeAccent,
textColor: Colors.white);
} else {
Fluttertoast.showToast(
msg: 'Nice!',
gravity: ToastGravity.TOP,
backgroundColor: Colors.green,
textColor: Colors.white);
Future.delayed(const Duration(seconds: 1), () {
setState(() {});
_answerController.clear();
});
}
}
final height = MediaQuery.of(context).size.height;
const shape = StadiumBorder();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text('Gr11 Maths Quiz',
style: TextStyle(
color: titleColor,
letterSpacing: 1,
fontSize: 20,
fontWeight: FontWeight.bold)),
leading: IconButton(
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.arrow_back,
color: Colors.black,
size: 30,
)),
actions: [
IconButton(
onPressed: () => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const CustomNavBar())),
icon: const Icon(
Icons.home,
color: Colors.black,
size: 30,
))
],
backgroundColor: Colors.white,
centerTitle: true,
elevation: 0,
),
body: SizedBox(
height: height,
child: Flexible(
child: Padding(
padding: const EdgeInsets.all(13),
child: Column(
children: [
Text(
randomQuiz['question'].toString(),
style: const TextStyle(color: Colors.black, fontSize: 17),
),
const SizedBox(
height: 30,
),
//Sum
Center(
child: Math.tex(
randomQuiz['sum'].toString(),
mathStyle: MathStyle.display,
textStyle: const TextStyle(fontSize: 25),
),
),
const SizedBox(
height: 60,
),
//Answer text
const Align(
alignment: Alignment.centerLeft,
child: Text(
'Answer:',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 20),
),
),
const SizedBox(
height: 10,
),
//Answer textfield & check btn
Row(
children: [
//Textfield
Expanded(
child: TextField(
controller: _answerController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
hintText: 'Answer',
hintStyle: const TextStyle(color: Colors.grey),
labelStyle: const TextStyle(color: Colors.grey),
enabledBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(13)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(13),
),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: submitBtn),
borderRadius: BorderRadius.circular(13))),
),
),
const SizedBox(
width: 15,
),
//Button
Container(
decoration: BoxDecoration(
color: titleColor,
borderRadius: BorderRadius.circular(10)),
child: ElevatedButton(
onPressed: () {
checkAnswer();
},
child: const Text(
"Check",
style: TextStyle(fontSize: 16),
),
style: ElevatedButton.styleFrom(
primary: Colors.transparent,
shadowColor: Colors.transparent,
padding: const EdgeInsets.symmetric(
vertical: 19, horizontal: 20),
//minimumSize: const Size(100.0, 5.0),
textStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23)),
),
),
],
),
const SizedBox(
height: 20,
),
//View Math symbols dialog
Align(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade400,
borderRadius: BorderRadius.circular(10)),
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return const MathFormulaDialog();
});
},
child: const Text(
"How should I type my answer?",
style: TextStyle(fontSize: 16, color: Colors.black),
),
style: ElevatedButton.styleFrom(
primary: Colors.transparent,
shadowColor: Colors.transparent,
padding: const EdgeInsets.symmetric(
vertical: 19, horizontal: 20),
//minimumSize: const Size(100.0, 5.0),
textStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 23)),
),
),
),
],
),
),
),
));
}
}

try not to use code inside build method unless you want it to be refreshed anytime any little change occurred in widget tree.
basically the problem is here in the very beginning lines of build method:
...
#override
Widget build(BuildContext context) {
List quiz = widget.newData["quiz"];
var randomQuiz = (quiz..shuffle()).first;
...
you must use it outside of build and only use it whenever an action is triggered for example if user finished answering current question then refresh ui and show another random question.

Thanks to #Namini40, I moved the variables to initState() from the build function, the checkAnswer() function remained in the build method and I changed a couple things. If a user gets the answer right rather than using setState() I instead used Navigator.pushReplacement() to navigate to the very same page but will display a different question.
New code: https://github.com/BotsheloRamela/classio/blob/main/quiz_screen.dart

Related

Get values from dynamically created form fields in flutter and dart

I have a form that allows the user to create addition cards at a tap of a button. Each card contains three form fields. How do i get the data from the forms to a json or to a list?
Hopefully get it to this format
"supplier_former_business": [
{
"name_of_company": "John Bull",
"year": 2018,
"amount": 28556
},
{
"name_of_company": "Gini Bull",
"year": 2019,
"amount": 1225
}
]
Here is the code that i have written so far but I am not sure how to get the values from the form fields in the cards.
class CustomerCurrentSuppliers extends StatefulWidget {
const CustomerCurrentSuppliers({Key key}) : super(key: key);
#override
_CustomerCurrentSuppliersState createState() => _CustomerCurrentSuppliersState();
}
class _CustomerCurrentSuppliersState extends State<CustomerCurrentSuppliers> {
int counter=1;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kPrimaryColor,
title: const Text('Suppliers Data'),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FloatingActionButton(
child: Icon(
Icons.arrow_forward
),
onPressed: () {
print(counter);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SupplierSchemeUnderConsideration()
),
);
//...
},
heroTag: null,
),
SizedBox(
height: 10,
),
FloatingActionButton(
child: Icon(
Icons.add
),
onPressed: () {
setState(() { //setState is used to update the UI
counter++;
});
},
heroTag: null,
)
]
),
body: Padding(
padding: const EdgeInsets.only(top: 38.0, right: 20, left: 20),
child: ListView.builder(
itemCount: counter, //updating counter will update the UI with new card
itemBuilder: (context,index){
return Card(
child:
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: FormBuilderTextField(
name: 'Region',
decoration: InputDecoration(
labelText: "Supplier",
border: OutlineInputBorder()),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButtonHideUnderline(
child: FormBuilderDropdown(
name: 'dropdown',
hint: Text("Year"),
isExpanded: true,
items: [
"2018",
"2019",
"2020",
"2021",
].map((option) {
return DropdownMenuItem(
child: Text("$option"),
value: option,
);
}).toList(),
),
),
),
SizedBox(height: 20,),
Padding(
padding: const EdgeInsets.all(8.0),
child: FormBuilderTextField(
name: 'Region',
decoration: InputDecoration(
labelText: "Amount",
border: OutlineInputBorder()),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
),
),
],
),
elevation: 8,
);
}),
),
);
}
}

passing a list of data from one screen to 2nd screen

I am passing a list of data that I called through API on 1st screen to 2nd screen and I am able to print all the data at once.
but now I want to use this data one by one and I don't know how to do that.... can you help?
this is the second screen -:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'Home_View.dart';
class AboutScreen extends StatefulWidget {
final data;
AboutScreen({Key key,#required this.data}) : super(key: key);
#override
_AboutScreenState createState() => _AboutScreenState(data:data);
}
class _AboutScreenState extends State<AboutScreen> {
var data;
_AboutScreenState({this.data});
int i = 0;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
print(data.toString()); // here i am printing all data
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 150, bottom: 40),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Card(
elevation: 25,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(65),
),
child: CircleAvatar(
child: Icon(Icons.insert_emoticon_outlined, size: 100,),
radius: 80,
foregroundColor: Colors.deepPurpleAccent,
),
),
],
),
),
Text(
'${data[0][i.toString()]}', // here i wanted to get data individually but it's showing error
style: TextStyle(
fontSize: 30,
letterSpacing: 2.5,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 30,),
Text(
'Version 2.2',
style: TextStyle(
fontSize: 20,
letterSpacing: 2.5,
),
),
SizedBox(height: 30,),
Text(
'Developed By Black Console',
style: TextStyle(
fontSize: 20,
letterSpacing: 2.5,
),
),
SizedBox(height: 40,),
FloatingActionButton(
child: Icon(Icons.arrow_back_ios),
backgroundColor: Colors.deepPurpleAccent,
elevation: 16,
onPressed: (){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => homeView()
));
},
),
],
),
);
}
}
below is the data that I am getting as a list -:
I/flutter ( 4689): {alternateno: [7845659877, 9865358577], coachaddress: [anand vihar, delhi, delhi, Gautam Budha nagar, noida], coachemail: [bct#gmail.com, SSG#gmail.com], coachid: [1, 2], coachname: [BCT Coaching, SSG Coaching], primary_number: [9865328899, 9898557898], userid: [1, 1]}
can you guide me please..
Your data variable seems to be a Map, not a list, so you can access its values like this:
data['alternateno'][i]

form.currentstate.validate() failing - Flutter

I need some help with this. My task is to pick the location along with the address and submit it. But for some reasons, the _addPointKey.currentState.validate() is always giving false.
I have a TextFormField for taking the address, a location preview container, a button to open the google_map_location_picker and finally a button to submit the data. All these fields are declared inside a form with _addPointKey key.
Form validation does work, i.e. if the fields are empty, it does show in red that the fields can't be empty. But still, I have some problem with submitting the whole formData. The textFormField becomes null.
I have used google_map_location_picker package for picking the location. Can somebody help?
Thanks in advance.
final GlobalKey<FormState> _addPointKey = GlobalKey<FormState>();
Map<String, dynamic> formData = {
"address": null,
};
Widget _addressField() {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Address of the Collection Center",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Theme.of(context).primaryColor),
),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
maxLines: 4,
validator: (String value) {
if (value.isEmpty) {
return "Address is required";
}
return '';
},
onChanged: (String value) {
formData["address"] = value;
},
onSaved: (String value) {
formData["address"] = value;
},
),
],
),
);
}
Widget _locationPreview() {
return (_pickedLocation != null)
? Container(
height: 300,
decoration: BoxDecoration(
border: Border.all(
width: 2,
color: Theme.of(context).accentColor,
)),
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: _pickedLocation.latLng,
zoom: 14.4746,
),
markers: Set.from(newMarkers),
),
)
: Container(
child: Text(
"Location is required.",
style: TextStyle(
color: Colors.red,
),
),
);
}
Widget _locationButton() {
return RaisedButton(
color: Theme.of(context).accentColor,
onPressed: () async {
LocationResult result = await showLocationPicker(
context,
< API key>,
initialCenter: LatLng(31.1975844, 29.9598339),
myLocationButtonEnabled: true,
layersButtonEnabled: true,
);
setState(() {
_pickedLocation = result;
_addPointKey.currentState.save();
newMarkers.add(Marker(
markerId: MarkerId("newId"),
draggable: false,
position: _pickedLocation.latLng,
onTap: null,
));
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.location_on,
color: Colors.white,
),
Text(
(_pickedLocation == null) ? 'Locate on Map' : 'Change Location',
style: TextStyle(color: Colors.white),
),
],
),
);
}
Widget _addLocationButton() {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget widget, MainModel model) {
return model.managingCenter
? CircularProgressIndicator()
: InkWell(
onTap: () async {
print(_addPointKey.currentState.validate());
if (!_addPointKey.currentState.validate() ||
_pickedLocation == null) {
print(formData);
return;
}
_addPointKey.currentState.save();
Map<String, dynamic> successInfo =
await model.addCenter(model.token, formData);
if (successInfo["success"]) {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FutureBuilder(
future: model.fetchCenter(model.token),
builder: (context, authResultSnapShot) {
return authResultSnapShot.connectionState ==
ConnectionState.waiting
? SplashScreen()
: CollectionPoints(model);
})));
} else {
print("Something went wrong");
}
},
child: Container(
height: 50,
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.grey.shade200,
offset: Offset(2, 4),
blurRadius: 5,
spreadRadius: 2)
],
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Color(0xfffbb448), Color(0xfff7892b)]),
),
padding: const EdgeInsets.all(10.0),
child: Text('Add the Location',
style: TextStyle(fontSize: 20, color: Colors.white)),
),
);
});
}
void _addPoint() {
showBottomSheet(
context: context,
builder: (context) {
return Container(
padding: EdgeInsets.all(10),
child: Form(
key: _addPointKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
_addressField(),
_locationPreview(),
_locationButton(),
_addLocationButton()
],
),
),
),
);
});
}
In the TextFormField validator function you must return null, it indicates the field has correct value and there are no errors. Returning anything other than null will indicate that the field has an invalid value, i've shared the corrected code of the validator function.
TextFormField(
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
maxLines: 4,
validator: (String value) {
if (value.isEmpty) {
return "Address is required";
}
// return ''; this is causing you the error you must return null
return null; // this is correct
},
onChanged: (String value) {
formData["address"] = value;
},
onSaved: (String value) {
formData["address"] = value;
},
),
A simpler way to create a confirm password on your flutter.
'showSnackBar' bottom pop-up to alert users for errors.
[1]: https://i.stack.imgur.com/zjXLd.png
var _password = '';
var _confirmPassword = '';
// this will called 'onTap'
void signUpUser() async {
if (_password != _confirmPassword){ // you can add your statements here
showSnackBar(context,
"Password does not match. Please re-type again.");
} else {
FirebaseAuthMethods(FirebaseAuth.instance).signUpWithEmail(
email: emailController.text,
password: passwordController.text,
context: context,
);
}
}
// Insert inside your build()
// to you password field.
child: TextFormField(
controller: passwordController,
obscureText: true,
onChanged: (value) {
_password = value;
},
// to your confirm password field.
child: TextFormField(
controller: confirmPasswordController,
obscureText: true,
onChanged: (value) {
_confirmPassword = value;
},
// add this to your Sign-up button
onTap: signUpUser,

How can I view the details of an item in a detailed screen - Flutter

I am new to flutter this error is giving me a serious nightmare. I have a card list of items called Anchors. These items are coming from the shared preference file which belongs to the logged-in user. In the shared preference Json file, each anchor has one or more distribution centers nested to it. All in a JSON form stored in the shared Preference. Now, I was able to iterate over the anchors respectfully at the first screen but the challenge I am having is when I click on view anchor details button instead of taking me to the details page where I can view the details of that anchor and iterate over the distribution centers of that anchor it doesn't but instead it takes the whole anchors there. I tried to parse only one id to the details page so I can Iterate over the nested objects of that anchor but still its not working. The error message says: type 'int' is not a subtype of type 'List' But if I remove [i]['Oid'] from the link to the details screen it takes the whole data there. They are all on two different screens. Please can anybody help me?
JSON format of:
"Anchors": [
{
"Oid": 11,
"Name": "MAIZE ASSOCIATION OF NIGERIA",
"Acronym": "MAAN",
"DistributionCentres": [
{
"Oid": 11,
"Name": "Logo Centre (Zone A)",
"Address": "Private Warehouse, Ugba, Logo LGA"
},
{
"Oid": 12,
"Name": "Makurdi Centre (Zone B)",
"Address": "Ministry of Agric, Makurdi "
},
{
"Oid": 13,
"Name": "Oturkpo Centre (Zone C)",
"Address": "Private Warehouse, Oturkpo"
},
{
"Oid": 15,
"Name": "Borno MAAN centre",
"Address": "Bolori Store, Flavour Mill, Behind Vita Foam, Maiduguri"
},
{
"Oid": 18,
"Name": "Bauchi Centre",
"Address": "BASPD, Dass Road, Bauchi"
}
],
"NoOfDistributionCentres": 5
},
{
"Oid": 2,
"Name": "MAIZE GROWERS, PROCESSORS AND MARKETERS ASSOCIATION OF NIGERIA",
"Acronym": "MAGPAMAN",
"DistributionCentres": [
{
"Oid": 2,
"Name": "Guma Centre",
"Address": "P 32, 2nd Avenue Federal Housing Estate, N/Bank, Makurdi"
},
{
"Oid": 3,
"Name": "Logo Centre",
"Address": "Terhemen Akema Storage Facility, Ugba, Logo LGA"
},
{
"Oid": 5,
"Name": "Oturkpo Centre",
"Address": "Grain Store, Lower Benue Okele Project, Otukpo"
},
{
"Oid": 6,
"Name": "Gboko Centre",
"Address": "K3 New Road, Opposite former coca cola plant. Solar Schools Street, Gboko"
},
{
"Oid": 7,
"Name": "Gwer East Centre",
"Address": "Ahua Shardye's Warehouse, Behind Sylkan Filling Station, Ikpayongo , G/East LGA"
},
{
"Oid": 8,
"Name": "Kwande Centre",
"Address": "KM 3, Adagi Road, Adikpo"
},
{
"Oid": 9,
"Name": "Ohimini Centre",
"Address": "Ajoga Oglewu, Ohimini"
},
{
"Oid": 10,
"Name": "Oju Centre",
"Address": "Behind Town Hall, Ohuhu owo, Oju LGA"
}
],
"NoOfDistributionCentres": 8
}
],
Anchors Page:
import 'package:erg_app/Details.dart';
import 'package:flutter/material.dart';
import 'package:erg_app/Widgets/nav-drawer.dart';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MaterialApp(
home: AnchorsPage(),
));
class AnchorsPage extends StatefulWidget {
#override
_MyHomeState createState() => _MyHomeState();
}
List<Anchor> _parseAnchors(Map<String, dynamic> map) {
final anchors = <Anchor>[];
for (var anchorMap in map['Anchors']) {
final anchor = Anchor.fromMap(anchorMap);
anchors.add(anchor);
}
return anchors;
}
class Anchor {
final int oId;
final String name;
final String acronym;
final List<DistributionCenter> distributionCenters;
const Anchor({
#required this.oId,
#required this.name,
#required this.acronym,
#required this.distributionCenters,
});
factory Anchor.fromMap(Map<String, dynamic> map) {
final distributionCenters = <DistributionCenter>[];
for (var distribution in map['DistributionCentres']) {
final distributionCenter = DistributionCenter.fromMap(distribution);
distributionCenters.add(distributionCenter);
}
return Anchor(
oId: map['Oid'] as int,
name: map['Name'] as String,
acronym: map['Acronym'] as String,
distributionCenters: distributionCenters,
);
}
}
class DistributionCenter {
final int id;
final String name;
final String address;
const DistributionCenter({
#required this.id,
#required this.name,
#required this.address,
});
factory DistributionCenter.fromMap(Map<String, dynamic> map) {
return DistributionCenter(
id: map['Oid'] as int,
name: map['Name'] as String,
address: map['Address'] as String,
);
}
}
class _MyHomeState extends State<AnchorsPage> {
var user;
var userData;
List anchors = [];
#override
void initState() {
_getUserAnchor();
super.initState();
}
_getUserAnchor() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var userJson = localStorage.getString('loginRes');
user = json.decode(userJson);
setState(() {
anchors = user['Anchors'];
});
print(anchors);
setState(() {
userData = anchors;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: NavDrawer(),
appBar: AppBar(
title: Text('Anchors Details'),
iconTheme: IconThemeData(color: Colors.white),
backgroundColor: Colors.green,
),
body: Container(
padding: const EdgeInsets.fromLTRB(10, 30, 10, 10),
child: ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(Icons.card_membership,
size: 35, color: Colors.orange[400]),
Text(
'Assigned Anchors',
style: TextStyle(color: Colors.orange[400], fontSize: 25),
),
],
),
ListView.builder(
shrinkWrap: true,
itemCount: anchors.length,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int i) {
return Padding(
padding: const EdgeInsets.all(10.0),
////////////// 1st card///////////
child: Card(
elevation: 4.0,
color: Colors.grey[100],
margin: EdgeInsets.only(
left: 10, right: 10, top: 20, bottom: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Container(
padding: EdgeInsets.only(left: 15, top: 20, bottom: 10),
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 50.0,
height: 50.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.cover,
image: AssetImage(
'assets/images/user.png')))),
),
SizedBox(
width: 20,
),
Text(
anchors[i]['Acronym'],
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 20,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
],
),
Container(width: 10),
Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 10, top: 10),
child: Text(
'Allocated Farmers:',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
Padding(
padding:
const EdgeInsets.only(left: 70, top: 12),
child: Text(
anchors[i]['Oid'].toString(),
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey[700],
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
],
),
Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 10, top: 10),
child: Text(
'Validated Farmers:',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
Padding(
padding:
const EdgeInsets.only(left: 70, top: 12),
child: Text(
anchors[i]['Oid'].toString(),
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey[700],
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
],
),
Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 10, top: 10),
child: Text(
'Non Validated Farmers:',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
Padding(
padding:
const EdgeInsets.only(left: 40, top: 12),
child: Text(
anchors[i]['Oid'].toString(),
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey[700],
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
],
),
Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 10, top: 10),
child: Text(
'Distribution Centers:',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
Padding(
padding:
const EdgeInsets.only(left: 60, top: 12),
child: Text(
anchors[i]['Oid'].toString(),
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.grey[700],
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
],
),
Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 10, top: 10),
child: Text(
'Daily Inventory Status:',
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
Padding(
padding:
const EdgeInsets.only(left: 50, top: 12),
child: Text(
'3',
textAlign: TextAlign.left,
style: TextStyle(
color:Colors.green,
fontSize: 14.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
],
),
Container(
height: 20,
),
Row(
children: <Widget>[
/////////// Buttons /////////////
Padding(
padding: const EdgeInsets.all(10.0),
child: FlatButton(
child: Padding(
padding: EdgeInsets.only(
top: 8,
bottom: 8,
left: 10,
right: 8),
child: Text(
'View Details',
textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
),
),
color: Colors.blueGrey,
shape: new RoundedRectangleBorder(
borderRadius:
new BorderRadius.circular(
20.0)),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) =>
detailsPage(value : anchors[i]['Oid'])));
},
),
),
/////////// End of Buttons /////////////
],
),
],
),
),
),
);
})
],
),
),
);
}
}
Details Page:
import 'package:flutter/material.dart';
class detailsPage extends StatefulWidget {
List value;
detailsPage({Key key, #required this.value}) : super(key: key);
#override
_detailsPageState createState() => _detailsPageState(value);
}
class _detailsPageState extends State<detailsPage> {
List value;
_detailsPageState(this.value);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Anchors Details Page"),
iconTheme: IconThemeData(color: Colors.white),
backgroundColor: Colors.green,
),
body: Container(
child: ListView(
children: <Widget>[
Text(value[1]['Name']),
Text(value[1]['Oid'].toString()),
ListView.builder(
shrinkWrap: true,
itemCount: value[1]['DistributionCentres'].length,
//context:context, //it saying the name parameter context is not defined
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int i) {
return Text(value[1]['DistributionCentres'][i]['Name']);
})
],
),
),
);
}
}
Image of what I want to achieve below:
first screen onPressed get the index and assigned it to variable, or you can just pass i varibale too,
onPressed: () {
Navigator.push(context,new MaterialPageRoute(builder: (context) =>
detailsPage(i))); },
In detailed page
class detailsPage extends StatefulWidget {
final int selectedIndex;
detailsPage(this.selectedIndex,{Key key}) : super(key: key);
#override
_detailsPageState createState() => _detailsPageState();
}
place that you want to use the previous page passsed index,
return Text(value[widget.selectedIndex]['DistributionCentres'][i]['Name']);
Hope this will help..
The problem seems to be here
detailsPage(value : anchors[i]['Oid'])
You're passing Oid as parameter that is an Int. But look that the constructor for detailsPage
List value;
detailsPage({Key key, #required this.value}) : super(key: key);
The parameter value is a List. It's hard for me to know what you're trying to do... but you will need to fix this type mismatch.
EDIT
It seems that value parameter should be of type Map<String,dynamic>
class detailsPage extends StatefulWidget {
Map<String, dynamic> value;
detailsPage({Key key, #required this.value}) : super(key: key);
#override
_detailsPageState createState() => _detailsPageState(value);
}
class _detailsPageState extends State<detailsPage> {
Map<String, dynamic> value;
_detailsPageState(this.value);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Anchors Details Page"),
iconTheme: IconThemeData(color: Colors.white),
backgroundColor: Colors.green,
),
body: Container(
child: ListView(
children: <Widget>[
Text(value['Name']),
Text(value['Oid'].toString()),
ListView.builder(
shrinkWrap: true,
itemCount: value['DistributionCentres'].length,
//context:context, //it saying the name parameter context is not defined
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int i) {
return Text(value['DistributionCentres'][i]['Name']);
})
],
),
),
);
}
}
And then you should do
detailsPage(value : anchors[i])));

Flutter GestureDetector is not working on functions

class GridDashboard extends StatefulWidget {
final Function onTap;
const GridDashboard({Key key, this.onTap}) : super(key: key);
#override
_GridDashboardState createState() => _GridDashboardState();
}
class _GridDashboardState extends State<GridDashboard> {
Items item1 = new Items(
title: 'Books',
img: 'assets/images/open-book.png',
onTap: () {
Books();
});
Items item2 = new Items(
title: 'Audio',
img: 'assets/images/headphones.png',
onTap: () => print('Audio')); // it works when I just print smth
Items item3 = new Items(
title: 'Videos',
img: 'assets/images/play-button.png',
onTap: () {
Videos();
});
#override
Widget build(BuildContext context) {
List<Items> myList = [item1, item2, item3];
return Flexible(
child: GridView.count(
childAspectRatio: 1.0,
padding: EdgeInsets.only(left: 30, right: 20, top: 220),
crossAxisCount: 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
children: myList.map((data) {
return GestureDetector(
onTap: data.onTap, // this line is not working
child: Container(
decoration: BoxDecoration(
color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(35),
boxShadow: [
BoxShadow(
color: Color(0xFF373234),
blurRadius: 6.0,
offset: Offset(0, 2),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(data.img, width: 45),
SizedBox(height: 15),
Text(
data.title,
style: GoogleFonts.openSans(
textStyle: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
);
}).toList(),
),
);
}
}
class Items {
String title;
String img;
Function onTap;
Items({this.img, this.title, this.onTap});
}
Hi people.
I am building an android app. The above code is for a dashboard (menu) section of my code. The GestureDetector onTap function is not working when I want it to go to another screen. But it works if I just want to print out smth. If you know how to solve this issue can you please help here ?
Thank you.
You can use named routes.
For Example :
Add the routes to you MaterialApp
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => FirstScreen(),
'/books': (context) => Books(),
'/videos': (context) => Videos(),
},
));
}
Change onTap to go to the specific page
onTap: () {
Navigator.pushNamed(context, '/books');
});
Make changes to the widgets accordingly.