How to update child StateFulWidget value using parent stateful widget - android

I have two separate widgets. I want to update the child widget textFormField value when I click on the button in the parent widget.
I have provided the code below. How can I do this without getX or Provider in flutter? I looked for a solution to this problem but did not find a solution for this kind of problem.
Parent Widget
FutureBuilder(
future: SupervisorAttendanceServices.getAttendancesDetailsList(
widget.attendanceId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var data = snapshot.data['labour'];
return ListView.builder(
itemCount: data.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return LabourAttendanceWidget(
workerId: data[index]['worker_id'],
masterAttendanceId: widget.attendanceId,
name: data[index]['worker_name'],
wages: data[index]['attendance_worker_wages'],
isPrensent: data[index]
['attendance_worker_presense']
.toString());
});
} else if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong !"),
);
} else {
return const Center(child: LinearProgressIndicator());
}
},
),
CHILD WIDGET
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:get/get.dart';
import 'package:site_management/supervisors/screens/supervisor_attendance/controller/labour_attendance_controller.dart';
import 'package:site_management/supervisors/supervisor_services/supervisor_attendance_services.dart';
class LabourAttendanceWidget extends StatefulWidget {
const LabourAttendanceWidget({
Key? key,
required this.name,
required this.wages,
required this.isPrensent,
required this.workerId,
required this.masterAttendanceId,
}) : super(key: key);
final int workerId;
final int masterAttendanceId;
final String name;
final String wages;
final String isPrensent;
#override
State<LabourAttendanceWidget> createState() => _LabourAttendanceWidgetState();
}
class _LabourAttendanceWidgetState extends State<LabourAttendanceWidget> {
final TextEditingController _wagesController = TextEditingController();
String _character = "";
Timer? searchOnStoppedTyping;
LabourAttendanceController attendanceController =
Get.put(LabourAttendanceController());
_onChangeHandler(value) {
const duration = Duration(
milliseconds:
800); // set the duration that you want call search() after that.
if (searchOnStoppedTyping != null) {
setState(() => searchOnStoppedTyping?.cancel()); // clear timer
}
setState(() =>
searchOnStoppedTyping = Timer(duration, () => submitWages(value)));
}
submitWages(value) {
SupervisorAttendanceServices.storeWorkerWages(
widget.workerId, value, widget.masterAttendanceId);
}
#override
void initState() {
super.initState();
_character = widget.isPrensent;
_wagesController.text = widget.wages;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Card(
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 10,
height: 50,
),
const Icon(FeatherIcons.user),
const SizedBox(
width: 20,
),
Text(
widget.name,
style: const TextStyle(fontSize: 18),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
SizedBox(
width: 150,
height: 60,
child: TextFormField(
controller: _wagesController,
onChanged: _onChangeHandler,
decoration: const InputDecoration(
// border: OutlineInputBorder(),
hintText: "Wages",
prefixIcon: Icon(
Icons.wallet,
color: Colors.blue,
)),
)),
Row(
children: [
Radio(
value: "P",
groupValue: _character,
fillColor:
MaterialStateColor.resolveWith((states) => Colors.green),
onChanged: (selectedValue) {
setState(() {
_character = selectedValue.toString();
SupervisorAttendanceServices.changeAttendance(
widget.workerId,
_character,
widget.masterAttendanceId)
.then((response) {
if (response == 1) {
return null;
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: const [
Icon(FeatherIcons.home),
SizedBox(
width: 10,
),
Text("Something went wrong !"),
],
),
),
// sb
);
}
});
});
attendanceController
.getAttendanceCount(widget.masterAttendanceId);
},
),
const Text("P"),
Radio(
value: "A",
fillColor:
MaterialStateColor.resolveWith((states) => Colors.red),
groupValue: _character,
onChanged: (selectedValue) {
setState(() {
_wagesController.text = "0";
_onChangeHandler("0");
_character = selectedValue.toString();
SupervisorAttendanceServices.changeAttendance(
widget.workerId,
_character,
widget.masterAttendanceId);
});
attendanceController
.getAttendanceCount(widget.masterAttendanceId);
}),
const Text("A"),
],
)
],
)
]),
);
}
}

First change your LabourAttendanceWidget to this:
class LabourAttendanceWidget extends StatefulWidget {
const LabourAttendanceWidget({
Key? key,
required this.name,
required this.wages,
required this.isPrensent,
required this.workerId,
required this.masterAttendanceId,
this.someString,
}) : super(key: key);
final int workerId;
final int masterAttendanceId;
final String name;
final String wages;
final String isPrensent;
final String someString;
#override
State<LabourAttendanceWidget> createState() => _LabourAttendanceWidgetState();
}
then in LabourAttendanceWidget's initState do this:
#override
void initState() {
super.initState();
_character = widget.isPrensent;
_wagesController.text = widget.someString ?? widget.wages;
setState(() {});
}
and in your parent widget first define this variable out of build method:
String? _value;
then do this:
return LabourAttendanceWidget(
workerId: data[index]['worker_id'],
masterAttendanceId: widget.attendanceId,
name: data[index]['worker_name'],
wages: data[index]['attendance_worker_wages'],
someString: _value,
isPrensent: data[index]
['attendance_worker_presense']
.toString());
then fill _value when came back from pop up and then call setstate.

Related

Flutter StreamBuilder widget Error : type ' () => Map<String, dynamic >' is not a subtype of type 'DocumentSnapshot<Object?>' in type cast

Error : type ' () => Map<String, dynamic >' is not a subtype of type 'DocumentSnapshot<Object?>' in type cast.
Below is the code sample of my chat screen which is one of the major component of my chat appllication where I wanted to build a stream of messages using the firebase but this gives an error app works completely fine till this screen but after this screen it throws an error please help me resolve this issue , I am using Streambuilder widget , please look into it
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:lets_chat/constant.dart';
import 'package:firebase_auth/firebase_auth.dart';
//final _firestore = FirebaseFirestore.instance;
//FirebaseUser = loggedInUser;
late User loggedInUser;
class ChatScreen extends StatefulWidget {
static const String id3 = 'chat_screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextController = TextEditingController();
final _fireStore = FirebaseFirestore.instance;
final _auth = FirebaseAuth.instance;
late String messageText;
#override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
void messagesStream() async {
await for (var snapshot in _fireStore.collection('messages').snapshots()) {
for (var messages in snapshot.docs) {
print(messages.data);
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
//Implement logout functionality
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: _fireStore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final messages = snapshot.data?.docs;
List<MessageBubble> messageBubbles = [];
for (var message in messages!) {
final messageText =
(message.data as DocumentSnapshot)['text'];
final messageSender =
(message.data as DocumentSnapshot)['sender'];
final currentUser = loggedInUser.email;
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
loggedUser: currentUser == messageSender,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
children: messageBubbles,
),
);
},
),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
TextButton(
onPressed: () {
_fireStore.collection('messages').add({
'text': messageText,
'sender': loggedInUser.email,
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class MessageBubble extends StatelessWidget {
MessageBubble(
{required this.sender, required this.text, required this.loggedUser});
final String sender;
final String text;
final bool loggedUser;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'sender',
style: TextStyle(
color: Colors.pink,
),
),
Material(
elevation: 5.0,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
bottomLeft: Radius.circular(20.0),
bottomRight: Radius.circular(20.0)),
color: loggedUser
? Colors.pinkAccent.shade200
: Colors.blueAccent.shade200,
child: Padding(
padding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
child: Text('$text from $sender'))),
],
),
);
}
}
Issue with your code is that you're trying to do message.data as DocumentSnapshot which is trying to cast () => Map<String, dynamic> (returned by .data) to a DocumentSnapshot, which is incorrect.
message is a QuerySnapshot and you need to use data() method to access the fields.
final messages = snapshot.data?.docs;
List<MessageBubble> messageBubbles = [];
for (var message in messages!) {
final messageText = message.data()['text'];
final messageSender = message.data()['sender'];
// ...
}

Error Get Value TextEditingController In Flutter

I want to retrieve the value from textEditingController with a loop, the problem that occurs when the listview is not scrolled will result in an error, "RangeError (index): Invalid value: Not in inclusive range 0..23: 24", I have random data of 40 records,
the application will run properly when scrolling to the end of the line. I want even though I don't scroll down the data can still be retrieved without error.
you can run my sample code, please help me thanks.
import 'dart:math';
import 'package:flutter/material.dart';
class Karyawan {
int id;
String nama;
int jamKerja;
Karyawan({this.id, this.nama,});
Karyawan.fromJson(Map<String, dynamic> json) {
id = json['id'];
nama = json['nama_karyawan'];
jamKerja = json['jam_kerja'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['nama_karyawan'] = this.nama;
data['jam_kerja'] = this.jamKerja;
return data;
}
}
List<Karyawan> _daftarKaryawan = List.generate(
40,
(index) => Karyawan(
id: Random().nextInt(100),
nama: 'test ${Random().nextInt(100)}',
),
);
class FormInputAbsenV3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Entry Absen"),
),
body: FormEntry(listKaryawan:_daftarKaryawan)
);
}
}
class FormEntry extends StatefulWidget {
final List<Karyawan> listKaryawan;
const FormEntry({Key key, this.listKaryawan}) : super(key: key);
#override
_FormEntryState createState() => _FormEntryState();
}
class _FormEntryState extends State<FormEntry> {
List karyawan = [];
final _formKey = GlobalKey<FormState>();
List<TextEditingController> _brutos = new List();
List<TextEditingController> _nettos = new List();
void addlistAbsen() {
for (int i = 0; i < widget.listKaryawan.length; i++) {
karyawan.add({
"id": widget.listKaryawan[i].id,
"bruto": _brutos[i].text,
"netto": _nettos[i].text
});
}
print(karyawan);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(5.0),
child: Form(
key: _formKey,
child: Container(
child: ListView.builder(
itemCount: widget.listKaryawan.length,
itemBuilder: (context,index) {
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
return Column(
children: [
FormWidget(
index: index,
nama: widget.listKaryawan[index].nama,
brucon: _brutos[index],
netcon: _nettos[index],
),
SizedBox(
height: 20.0,
),
],
);
},
),
),
),
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.save),
label: Text("Save"),
onPressed: () {
if (_formKey.currentState.validate()) {
addlistAbsen();
}
},
),
);
}
}
class FormWidget extends StatelessWidget {
final int index;
final String nama;
final brucon;
final netcon;
FormWidget({this.index, this.nama, this.brucon, this.netcon});
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text("${index + 1}. ${nama}"),
),
Expanded(
child: TextFormField(
decoration: new InputDecoration(
labelText: "Bruto",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
),
style: new TextStyle(
fontFamily: "Poppins",
),
controller: brucon,
),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
decoration: new InputDecoration(
labelText: "Netto",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
),
style: new TextStyle(
fontFamily: "Poppins",
),
controller: netcon,
))
],
);
}
}
Please initialize your TextEditingControllers during initState.
#override
void initState() {
super.initState();
for (int i = 0; i < widget.listKaryawan.length; i++) {
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
}
}
Then, remove these lines inside the itemBuilder...
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
NOTE: itemBuilder will be called when a list item is to be redisplayed/newly displayed. Therefore, your list of TextEditingControllers > list items. (try scrolling to the bottom most, then scroll back up)
You CANNOT use ListView.builder for this and you will have to use ListView. You cannot use ListView.builder constructor because the builder is called only for those children that are actually visible. Please see the documentation for ListView.builder
ListView.builder constructor
Creates a scrollable, linear array of widgets that are created on
demand.
This constructor is appropriate for list views with a large (or
infinite) number of children because the builder is called only for
those children that are actually visible.
Please see the following code :
import 'package:flutter/material.dart';
import 'dart:math';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
//theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: FormInputAbsenV3(),
),
),
);
}
}
class Karyawan {
int id;
String nama;
int jamKerja;
Karyawan({
this.id,
this.nama,
});
Karyawan.fromJson(Map<String, dynamic> json) {
id = json['id'];
nama = json['nama_karyawan'];
jamKerja = json['jam_kerja'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['id'] = this.id;
data['nama_karyawan'] = this.nama;
data['jam_kerja'] = this.jamKerja;
return data;
}
}
List<Karyawan> _daftarKaryawan = List.generate(
40,
(index) => Karyawan(
id: Random().nextInt(100),
nama: 'test ${Random().nextInt(100)}',
),
);
class FormInputAbsenV3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Entry Absen"),
),
body: FormEntry(listKaryawan: _daftarKaryawan));
}
}
class FormEntry extends StatefulWidget {
final List<Karyawan> listKaryawan;
const FormEntry({Key key, this.listKaryawan}) : super(key: key);
#override
_FormEntryState createState() => _FormEntryState();
}
class _FormEntryState extends State<FormEntry> {
List karyawan = [];
final _formKey = GlobalKey<FormState>();
List<TextEditingController> _brutos = [];
List<TextEditingController> _nettos = [];
#override
void initState() {
super.initState();
for (int i = 0; i < widget.listKaryawan.length; i++) {
_brutos.add(TextEditingController());
_nettos.add(TextEditingController());
}
}
void addlistAbsen() {
for (int i = 0; i < widget.listKaryawan.length; i++) {
karyawan.add({
"id": widget.listKaryawan[i].id,
"bruto": _brutos[i].text,
"netto": _nettos[i].text
});
}
print(karyawan);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(5.0),
child: Form(
key: _formKey,
child: Container(
// child: ListView.builder(
// itemCount: widget.listKaryawan.length,
// itemBuilder: (context, index) {
// _brutos.add(new TextEditingController());
// _nettos.add(new TextEditingController());
// return Column(
// children: [
// FormWidget(
// index: index,
// nama: widget.listKaryawan[index].nama,
// brucon: _brutos[index],
// netcon: _nettos[index],
// ),
// SizedBox(
// height: 20.0,
// ),
// ],
// );
// },
// ),
child: ListView(
children: [
for (int index = 0; index < widget.listKaryawan.length; index++)
Column(
children: [
FormWidget(
index: index,
nama: widget.listKaryawan[index].nama,
brucon: _brutos[index],
netcon: _nettos[index],
),
SizedBox(
height: 20.0,
),
],
),
],
),
),
),
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.save),
label: Text("Save"),
onPressed: () {
if (_formKey.currentState.validate()) {
addlistAbsen();
}
},
),
);
}
}
class FormWidget extends StatelessWidget {
final int index;
final String nama;
final TextEditingController brucon;
final TextEditingController netcon;
const FormWidget({this.index, this.nama, this.brucon, this.netcon});
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text("${index + 1}. ${nama}"),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Bruto",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(),
),
),
style: TextStyle(
fontFamily: "Poppins",
),
controller: brucon,
),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Netto",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(),
),
),
style: TextStyle(
fontFamily: "Poppins",
),
controller: netcon,
))
],
);
}
}

Stuck on being able to view detailed information from a list in flutter app

I have a louded information in RealTime database in format for example:
record
0
Club: "Club1"
Name: "Ronaldo"
Place: "London"
date: "25.07.2020"
email: "flutter#gmail.com"
phone: "12345678"
I have created a list that consists of names and clubs and I want to go to the full information according to the form by clicking on the Name, but I can't write the code. Please help to the new programmer
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:.../anketa/tile.dart';
class Anketa extends StatefulWidget {
Anketa({Key key, this.title}) : super(key: key);
final String title;
#override
_AnketaState createState() => _AnketaState();
}
class _AnketaState extends State<Anketa> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Registration form",
style: TextStyle(
fontWeight: FontWeight.w200,
fontSize: 30,
fontFamily: 'Roboto',
fontStyle: FontStyle.italic)),
RegisterStudent(),
]),
)),
);
}
}
class RegisterStudent extends StatefulWidget {
RegisterStudent({Key key}) : super(key: key);
#override
_RegisterStudentState createState() => _RegisterStudentState();
}
class _RegisterStudentState extends State<RegisterStudent> {
final _formKey = GlobalKey<FormState>();
final listOfClubs = ["Club1", "Club2", "Club3", "Club4"];
String dropdownValue = "Club1";
final clubController = TextEditingController();
final nameController = TextEditingController();
final placeController = TextEditingController();
final dateController = TextEditingController();
final emailController = TextEditingController();
final phoneController = TextEditingController();
final rawController = TextEditingController();
final dbRef = FirebaseDatabase.instance.reference().child("record");
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: TextFormField(
controller: nameController,
decoration: InputDecoration(
labelText: "EnterName",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
// The validator receives the text that the user has entered.
validator: (value) {
if (value.isEmpty) {
return "Enter name";
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: DropdownButtonFormField(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
decoration: InputDecoration(
labelText: "Club",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
items: listOfClubs.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
this.dropdownValue = newValue;
});
},
validator: (value) {
if (value.isEmpty) {
return 'Club';
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: TextFormField(
keyboardType: TextInputType.number,
controller: dateController,
decoration: InputDecoration(
labelText: "Date",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
// The validator receives the text that the user has entered.
validator: (value) {
if (value.isEmpty) {
return 'Date';
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
color: Colors.lightBlue,
onPressed: () {
if (_formKey.currentState.validate()) {
dbRef.push().set({
"Name": nameController.text,
"date": dateController.text,
"Club": dropdownValue
}).then((_) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Add')));
dateController.clear();
nameController.clear();
}).catchError((onError) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(onError)));
});
}
},
child: Text('Enter'),
),
RaisedButton(
color: Colors.amber,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ListOfNames()),
);
},
child: Text('Go to'),
),
],
)),
])));
}
#override
void dispose() {
super.dispose();
dateController.dispose();
nameController.dispose();
}
}
and this is the second page with a list, from where I want to go to full information
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
class ListOfNames extends StatelessWidget {
final dbRef = FirebaseDatabase.instance.reference().child("record");
List<Map<dynamic,dynamic>> lists = List();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade300,
appBar: AppBar(
backgroundColor: Colors.deepPurple,
title: Text("List of students"),
),
body: StreamBuilder(
stream: dbRef.onValue,
builder: (context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
lists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value;
values.forEach((key, values) {
lists.add(values);
});
return new ListView.builder(
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context, int index) {
itemBuilder: (BuildContext context, int index) {
return ListTile(
title:Text(lists[index]["Name"], style: TextStyle(height: 2.5, fontSize:20.0),),
subtitle:Text(lists[index]['Club'], style: TextStyle(fontSize:16.0),),
onTap: (){
// in this line i have a problem...
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]['Name']),
)
);
},
);
});
}
return CircularProgressIndicator();
})
);
}
}
I want to create such a page:
class DetailPage extends StatelessWidget {
List<Map<dynamic,dynamic>> data ;
DetailPage ({this.data});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(data.name),
// here I want to show the all information from the form about one person from the list
),
);
}
}
In the body of the scaffold of 'DetailsPage' use 'Card' widget with 'Text' widgets to show the info. Like Card(child:Text('${data.clubLoc}').

how to call the method in provider inside the build method in flutter?

i have a method in the class which extends changeNotifier which gets the data from API. now I want to call this method whenever page is opened in the build method but when I call that method it is repeatedly called because of the notifyListeners method. I want to know how to call method only once.
ReportProvider.dart
class ReportProvider extends ChangeNotifier
{
static DateFormat dateFormat = new DateFormat('dd-MM-yyyy');
static DateFormat actualDateFormat = new DateFormat("yyyy-MM-dd");
String _toDate = dateFormat.format(new DateTime.now());
String _actualToDate = actualDateFormat.format(new DateTime.now());
String _actualFromDate = actualDateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
String _fromDate = dateFormat.format(new DateTime.now().subtract(new Duration(days: 7)));
bool _progressStatuc = false;
bool _chartVisible = true;
bool _errorVisible = false;
String _errorMessage;
String get errorMessage => _errorMessage;
bool get errorVisible => _errorVisible;
bool get chartVisible => _chartVisible;
bool get progressStatus => _progressStatuc;
String get toDate => _toDate;
String get fromDate => _fromDate;
List<PieData> _data = new List();
List<PieData> get data => _data;
Future<void> getReportData() async
{
Map<String,dynamic> sessiondata = await new Utilities().getSessionData();
int shopid = sessiondata['shopid'];
Map<String,String> reportData = new Map();
reportData['shopid'] = shopid.toString();
reportData["fromdate"] = _actualFromDate;
reportData["todate"] = _actualToDate;
String token = await new Utilities().getToken();
Map userHeader = {"token": token};
print(reportData.toString());
if(await new Utilities().checkInternet())
{
try
{
http.Response response = await http.post(EndPointUrl.report,body: reportData,headers: userHeader);
String message = json.decode(response.body)['message'];
List<ReportData> data = json.decode(response.body)['data'];
data.forEach((reportData){
_data.add(new PieData(reportData.menuname,reportData.itemcount));
});
notifyListeners();
}
catch(error)
{
_errorMessage = "Server error";
notifyListeners();
}
}
else
{
_progressStatuc = false;
_chartVisible = false;
_errorVisible = true;
_errorMessage = "No Internet Connection";
notifyListeners();
}
}
}
Report.dart
class Report extends StatefulWidget
{
#override
State<StatefulWidget> createState() {
return ReportState();
}
}
class ReportState extends State<Report>
{
#override
Widget build(BuildContext context) {
final reportProvider = Provider.of<ReportProvider>(context);
reportProvider.getReportData();
//yprint(reportProvider.data.toString());
if(reportProvider.errorMessage != null && reportProvider.errorMessage.contains("Internet"))
{
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text("Error"),
content: Text("${reportProvider.errorMessage}"),
actions: <Widget>[
FlatButton(
child: Text("ok"),
onPressed: (){
Navigator.pop(context);
},
)
],
);
});
}
return Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(8, MediaQuery.of(context).size.height*0.05,0, 0),
child: Text(
"Report",
style: TextStyle(fontSize: 28,color: Colors.black),
),
),
Row(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
margin: EdgeInsets.fromLTRB(8, 8, 0, 0),
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "fromdate");
},
child: Text(
"${reportProvider.fromDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
),
Expanded(
flex: 1,
child: Text(
"To",
style: TextStyle(fontSize: 16,color: Colors.grey),
),
),
Expanded(
flex: 3,
child: GestureDetector(
onTap: (){
reportProvider.selectDate(context, "todate");
},
child: Text(
"${reportProvider.toDate}",
style: TextStyle(color: Colors.black,fontSize: 16),
),
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: (){},
child: Icon(
Icons.check,
color: Theme.of(context).accentColor,
),
),
)
],
),
// Visibility(
// visible: reportProvider.chartVisible,
// child: charts.PieChart<PieData>(
// ),
// ),
Expanded(
child: Visibility(
visible: reportProvider.errorVisible,
child: Container(
alignment: Alignment.center,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset('assets/images/undraw_report.svg',width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height*0.40),
Text(
"No Reports are available",
style: TextStyle(color: Colors.black,fontSize: 20),
)
],
),
),
),
)
],
),
],
);
}
}
As per the documentation, build() method is going to be called every time something changes. If you want calls to be triggered just once, you could use initState() method and add some helpers methods to update the UI. An example can be found here: https://flutter.dev/docs/get-started/flutter-for/android-devs#what-is-the-equivalent-of-runonuithread-in-flutter
Example for async loading from the previous link, special attention to loadData method:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
#override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
#override
void initState() {
super.initState();
loadData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
},
),
);
}
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}"),
);
}
Future<void> loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}

Flutter stateful widget with child not updating state

I'm working on a part of an app that will essentially just be keeping track of physical tokens that are like forms of currency. I'm trying to build a reusable Widget that will take in the state of that token quantity as a parameter, and increment/decrement that based on user interaction. For the sake of clarity, I've just included the decrement part of the Widget. My question: is the state of the token that is getting passed into the widget not updating because it's just a reference to that state? Or am I missing something else.
class RedeemTokensState extends State<RedeemTokens> {
int oneQuantity = 0;
int fiveQuantity = 0;
int tenQuantity = 0;
int total = 0;
Widget _counterWidget(int tokenQuantity) {
return Row(
children: <Widget>[
Expanded(
child: IconButton(
icon: Icon(Icons.remove),
onPressed: () {
setState(() {
tokenQuantity = tokenQuantity - 1;
print(tokenQuantity);
});
},
),
),
),
}
Widget _buildOneField() {
return ListTile(
title: Text('\$1 Token'),
trailing: Container(width: 200.0, child: _counterWidget(oneQuantity)),
);
}
Widget _buildFiveField() {
return ListTile(
title: Text('\$5 Token'),
trailing: Container(width: 200.0, child: _counterWidget(fiveQuantity)),
);
}
Widget _buildTenField() {
return ListTile(
title: Text('\$10 Token'),
trailing: Container(width: 200.0, child: _counterWidget(tenQuantity)),
);
}
}
// main scaffold with build method
... Card(
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
_buildOneField(),
Divider(),
_buildFiveField(),
Divider(),
_buildTenField(),
Divider(),
_buildFreshConnectField(),
],
),
),
),
A generic solution could look like:
Parent widget
class RedeemTokens extends StatefulWidget {
#override
RedeemTokensState createState() => RedeemTokensState();
}
class RedeemTokensState extends State<RedeemTokens> {
final _quantities = new Map<TokenType, int>.fromIterable(TokenType.values,
key: (k) => k, value: (k) => 0);
Widget build(BuildContext context) {
final widgets = <Widget>[];
for (final type in _quantities.keys) {
widgets
..add(
new TokenQuantity(
tokenType: type,
quantity: _quantities[type],
onQuantityUpdated: (newValue) {
setState(() {
print('\$${type.value}: $newValue');
print(_quantities);
_quantities[type] = newValue;
});
}),
)
..add(Divider());
}
// widgets.add(_buildFreshConnectField());
return Card(
child: Container(
padding: EdgeInsets.all(10.0),
child: Column(
children: widgets,
),
),
);
}
}
Child widget added once per TokenType
class TokenQuantity extends StatelessWidget {
const TokenQuantity(
{#required this.tokenType,
#required this.quantity,
this.onQuantityUpdated})
: assert(quantity != null);
final TokenType tokenType;
final int quantity;
final TokenQuantityUpdatedFn onQuantityUpdated;
Widget _counterWidget() {
return Row(
children: <Widget>[
Text('$quantity'),
Expanded(
child: IconButton(
icon: Icon(Icons.remove),
onPressed: () {
if (onQuantityUpdated != null) {
onQuantityUpdated(quantity - 1);
}
},
),
),
],
);
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text('\$${tokenType.value} Token'),
trailing: Container(width: 200.0, child: _counterWidget()),
);
}
}
Typedef for the event callback
typedef TokenQuantityUpdatedFn = void Function(int newValue);
"Old-style" enum to be able to set custom values.
class TokenType {
static const one = const TokenType(1);
static const fife = const TokenType(5);
static const ten = const TokenType(10);
static const values = const <TokenType>[one, fife, ten];
final int value;
const TokenType(this.value);
#override
String toString() => 'TokenType $\$value';
}

Categories

Resources