Data from database is not fetched/displayed in listview flutter - sqlite

I have a screen which shows list of customers using listview. Next when I click on a customer I want to show the notes(records) only of that particular customer(customerId) in next screen in listview. This is my code which should work fine but its not displaying anything. I have checked if noteDetails table contains data and it has. Can anyone tell where am I wrong or what code should i add more please.
main.dart
import 'package:flutter/material.dart';
import 'package:vers2cts/screens/user_login.dart';
void main() {
runApp(MyApp());
}
Map<int, Color> color ={50:Color.fromRGBO(170, 0, 95, .1),
100:Color.fromRGBO(170, 0, 95, .2),
200:Color.fromRGBO(170, 0, 95, .3),
300:Color.fromRGBO(170, 0, 95, .4),
400:Color.fromRGBO(170, 0, 95, .5),
500:Color.fromRGBO(170, 0, 95, .6),
600:Color.fromRGBO(170, 0, 95, .7),
700:Color.fromRGBO(170, 0, 95, .8),
800:Color.fromRGBO(170, 0, 95, .9),
900:Color.fromRGBO(170,0,95, 1),};
MaterialColor colorCustom = MaterialColor(0xFF880E4F, color);
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'NoteKeeper',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch:colorCustom,
primaryColor:colorCustom//primarySwatch: Colors.purple
),
home: UserLogin(),
);
}
}
Note_info.dart //This is the file where i want to display the notes of particular customer.
import 'dart:io';
import 'package:customer/models/CustomerNote.dart';
import 'package:customer/models/addCustomer.dart';
import 'package:customer/services/db_service.dart';
import 'package:customer/utils/database_helper.dart';
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'New_Note.dart';
class Note_Info extends StatefulWidget{
final String appBarTitle;
final AddCustomer customer;
Note_Info(this. customer, this.appBarTitle);
#override
State<StatefulWidget> createState() {
return Note_InfoState(this. customer,this.appBarTitle);
}
}
class Note_InfoState extends State<Note_Info> {
DBService dbService = DBService();
List<CustomerNote> noteList;
int count = 0;
static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
CustomerNote note=CustomerNote();
String appBarTitle;
AddCustomer customer;
Note_InfoState(this.customer, this.appBarTitle);
DateTime _date = DateTime.now();
TextEditingController custNameController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
int customerId=customer.custId;
if (noteList == null) {
noteList = List<CustomerNote>();
updateListView();
}
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
var height = MediaQuery.of(context).size.height;
custNameController.text = customer.custName;
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(
Icons.add,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => NewNote(note)));
},
)
],
),
body: Container(
child: Column(
children: <Widget>[
TextField(controller: custNameController,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
Padding(
padding: const EdgeInsets.all(15.0),
child: Row(children: [
ImageProfile(customer.custPhoto),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: IconButton(
icon: Icon(
Icons.call,
color: Colors.green,
size: 45,
),
onPressed: () {
},
),
),
],),
),
SizedBox(
height: 50,
child: AppBar(
bottom: TabBar(
tabs: [
Tab(
text: "All",
),
Tab(
text: "Pending",
),
Tab(
text: "Cancelled",
),
Tab(
text: "Completed",
),
],
),
),
),
// create widgets for each tab bar here
Expanded(
child: TabBarView(
children: [
// first tab bar view widget
Container(
child: getNoteListView()
),
// second tab bar viiew widget
Container(
),
Container(
child: Center(
child: Text(
'Cancelled',
),
),
),
Container(
child: Center(
child: Text(
'Completed',
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 55.0,
width: 200,
child: RaisedButton(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Theme
.of(context)
.primaryColorDark,
textColor: Colors.white,
child: Text('Save', textScaleFactor: 1.5,),
onPressed: () {
setState(() {
//_reset();
});
},
),
),
),
]
),
)
));
}
Widget ImageProfile(String fileName) {
return Center(
child: CircleAvatar(
radius: 80.0,
backgroundImage: fileName == null
?AssetImage('images/person_icon.jpg')
:FileImage(File(customer.custPhoto))),
);
}
ListView getNoteListView() {
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
title: Text(this.noteList[position].note, style: titleStyle,),
trailing: GestureDetector(
child: Icon(Icons.delete, color: Colors.grey,),
onTap: () {
},
),
onTap: () {
},
),
);
},
);
}
void updateListView() {
final Future<Database> dbFuture = DB.init();
dbFuture.then((database) {
int cid=customer.custId;
Future<List<CustomerNote>> noteListFuture = dbService.getCustomerNotes(cid);
noteListFuture.then((noteList) {
setState(() {
this.noteList = noteList;
this.count = noteList.length;
});
});
});
}
}
New_Note.dart //This is where new note is added in database successfully
import 'package:customer/models/CustomerNote.dart';
import 'package:customer/models/addCustomer.dart';
import 'package:customer/screens/Note_info.dart';
import 'package:customer/services/db_service.dart';
import 'package:customer/utils/form_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
import 'package:intl/intl.dart';
class NewNote extends StatefulWidget{
final CustomerNote note;
NewNote(this. note);
final Function(Color) onChanged;
final double height;
final double width;
NewNote({
Key key,
this.onChanged,
this.height = 25,
this.width = 150,
this.label,
}) : super(key: key);*/
#override
State<StatefulWidget> createState() {
return New_NoteState(this.note);
}
}
class New_NoteState extends State<NewNote> with SingleTickerProviderStateMixin{
New_NoteState(this.note);
CustomerNote note=new CustomerNote();
AddCustomer customer=new AddCustomer();
TextEditingController NoteController=TextEditingController();
TextEditingController custNameController = TextEditingController();
DateTime _reminderDate = DateTime.now();
DBService dbService=new DBService();
SpeedDial _speedDial(){
return SpeedDial(
animatedIcon: AnimatedIcons.add_event,
animatedIconTheme: IconThemeData(size: 24.0),
backgroundColor: Colors.yellow,
curve: Curves.easeInCirc,
children: [
SpeedDialChild(
child: Icon(Icons.location_on,color: Colors.yellow,),
label: 'Add Location',
),
SpeedDialChild(
child: Icon(Icons.keyboard_voice),
//backgroundColor: Colors.yellow,
label: 'Add voice',
//labelBackgroundColor: Colors.yellow
),
SpeedDialChild(
child: Icon(Icons.attachment_outlined,color :Colors.redAccent),
label: 'Add File',
),
SpeedDialChild(
child: Icon(Icons.image,color: Colors.lightBlue,),
label: 'Add Image',
),
],
);
}
//for DropDownMenu
Color value=Colors.red;
final List<Color> colors = [
Colors.red,
Colors.blue,
Colors.green,
Colors.yellow,
Colors.pink,
Colors.purple,
Colors.brown,
];
bool isSwitched = false;
var textValue = 'Switch is OFF';
void toggleSwitch(bool value) {
if(isSwitched == false)
{
setState(() {
isSwitched = true;
this.note.remindOn = _reminderDate.toString();
});
}
else
{
setState(() {
isSwitched = false;
});
}
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
custNameController.text = customer.custName;
return WillPopScope(
onWillPop: () {
moveToLastScreen();
},
child: Scaffold(
appBar:AppBar(),
body:ListView(
children: <Widget>[
SizedBox(
height: 2.0,
),
TextField(controller: custNameController,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
Align(
alignment: Alignment.centerLeft,
child: Text("Add New",textAlign: TextAlign.left,
style: TextStyle(fontSize: 22,fontWeight: FontWeight.bold),),
),
SizedBox(
height: 2.0,
),
Divider(),
SizedBox(
height: 2.0,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: NoteController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(width: 2.0),)),
keyboardType: TextInputType.multiline,
minLines: 5,
maxLines: 5,
onChanged: (value) {
this.note.note = value;
},
),
),
TableCalendar(
selectedDayPredicate: (day) {
return isSameDay(_reminderDate, day);
},
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_reminderDate = selectedDay;
});
},
focusedDay: DateTime.now(),
firstDay: DateTime.utc(2010, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),),
SizedBox(
height: height*0.03,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(//mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Remind me",style: TextStyle(fontSize: 20),),
Padding(
padding: const EdgeInsets.only(left:80.0),
child: Container(
child: Switch(
onChanged: toggleSwitch,
value: isSwitched,
),
),
],),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment: MainAxisAlignment.start,
children:<Widget>[
Text("Priority",style: TextStyle(fontSize: 20.0),),
Padding(
padding: const EdgeInsets.only(left:20.0),
child: Container(
child: SmoothStarRating(
size: height=50.0,
allowHalfRating: false,
onRated: (value) {
this.note.priority=value;
print("rating value -> $value");
},
),
),
)]),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Color",style: TextStyle(fontSize: 20),),
Padding(
padding: const EdgeInsets.only(left:80.0),
child: Container(
child: DropdownButton<Color>(
value: value,
onChanged: (color) {
setState(() => value = color);
},
items: colors.map((e) => DropdownMenuItem(
value: e,
child: Container(
width: 60.0,
color: e,
),
),
)
.toList(),
),
),
),
],),
),
SizedBox(
height: height*0.08,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 55.0,
width: 200,
child: RaisedButton(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Theme.of(context).primaryColorDark,
textColor: Colors.white,
child: Text('Save',textScaleFactor: 1.5,),
onPressed: (){
setState(() {
_save();
});
},
),
),
),
],
),
floatingActionButton:_speedDial(),
));
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
void _save() async {
moveToLastScreen();
note.custId=customer.custId;
note.date = DateFormat.yMMMd().format(DateTime.now());
int result;
if (note.id != null) { // Case 1: Update operation
result = await dbService.updateNote(note);
} else { // Case 2: Insert Operation
result = await dbService.insertNote(note);
}
if (result != 0) { // Success
FormHelper.showAlertDialog(context,'Status', 'Note Saved Successfully');
} else { // Failure
FormHelper.showAlertDialog(context,'Status', 'Problem Saving Note');
}
}
}
db_service.dart
Future<bool> insertCustomer(AddCustomer model) async {
await DB.init();
bool isSaved = false;
if (model != null) {
int inserted = await DB.insert(AddCustomer.table, model);
isSaved = inserted == 1 ? true : false;
}
return isSaved;
}
Future<List<Map<String, dynamic>>> getCustomerMapList() async {
await DB.init();
var result = await DB.query(AddCustomer.table);
return result;
}
Future<List<AddCustomer>> getCustomerList() async {
var CustomerMapList = await getCustomerMapList();
int count = CustomerMapList.length;
List<AddCustomer> customerList = List<AddCustomer>();
for (int i = 0; i < count; i++) {
customerList.add(AddCustomer.fromMap(CustomerMapList[i]));
}
return customerList;
}
Future<int> insertNote(CustomerNote note) async {
await DB.init();
var result = await DB.insert(CustomerNote.table, note);
return result;
}
Future<List<CustomerNote>> getCustomerNotes(int customer) async {
await DB.init();
var res = await DB.rawQuery("noteDetails WHERE custId = '$customer'");
int count = res.length;
List<CustomerNote> notelist = List<CustomerNote>();
for (int i = 0; i < count; i++) {
notelist.add(CustomerNote.fromMap(res[i]));
}
return notelist;
}
database_helper.dart
import 'dart:async';
import 'package:customer/models/model.dart';
import 'package:path/path.dart' as p;
import 'package:sqflite/sqflite.dart';
abstract class DB {
static Database _db;
static int get _version => 1;
static Future<Database> init() async {
if (_db != null) {
return _db;
}
try {
var databasesPath = await getDatabasesPath();
String _path = p.join(databasesPath, 'Customer.db');
_db = await openDatabase(_path, version: _version, onCreate: onCreate);
print('db location:'+_path);
} catch (ex) {
print(ex);
}
}
static void onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE userDetails (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'firstName STRING, '
'lastName STRING, mobileNum STRING, emailId STRING, address String,'
'userType STRING,password STRING)');
await db.execute(
'CREATE TABLE customerDetails (custId INTEGER PRIMARY KEY AUTOINCREMENT, '
'custName STRING, '
'mobileNum STRING, company STRING, custPhoto STRING, showOnCall bool,'
'remindOn STRING,location STRING)');
await db.execute(
'CREATE TABLE noteDetails (noteId INTEGER PRIMARY KEY AUTOINCREMENT, '
'custId STRING, '
'custName STRING, date STRING, note STRING, remindOn STRING,'
'priority STRING,status STRING,attachment STRING)');
await db.execute(
'CREATE TABLE languagesKnown(custId INTEGER REFERENCES customerDetails(custId),'
' languages STRING,PRIMARY KEY(custId))');
}
static Future<List<Map<String, dynamic>>> query(String table) async =>
_db.query(table);
static Future<int> insert(String table, Model model) async =>
await _db.insert(table, model.toMap());
static Future<int> update(String table, Model model) async => await _db
.update(table, model.toMap(), where: 'id = ?', whereArgs: [model.id]);
static Future<int> delete(String table, Model model) async =>
await _db.delete(table, where: 'id = ?', whereArgs: [model.id]);
static Future<int> deleteCustomer(String table, Model model) async =>
await _db.delete(table, where: 'custId = ?', whereArgs: [model.custId]);
static Future<Batch> batch() async => _db.batch();
static Future<List<Map<String, dynamic>>> rawQuery(String table) async =>
_db.query(table);
}
people_list.dart // This is where list of customers is displayed and by clicking on a listtile i.e customer, Note_info opens
import 'package:customer/models/addCustomer.dart';
import 'package:customer/screens/Note_info.dart';
import 'package:customer/screens/User_Settings.dart';
import 'package:customer/screens/add_person.dart';
import 'package:customer/services/db_service.dart';
import 'package:customer/utils/database_helper.dart';
import 'package:customer/utils/form_helper.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:sqflite/sqflite.dart';
class People_List extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return People_ListState();
}
}
class People_ListState extends State<People_List> with SingleTickerProviderStateMixin{
DBService dbService = DBService();
List<AddCustomer> customerList;
int count = 0;
static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
var _isSelectedItemIndex;
#override
void initState() {
super.initState();
_searchQuery = new TextEditingController();
}
Widget _buildTitle(BuildContext context) {
var horizontalTitleAlignment =
Platform.isIOS ? CrossAxisAlignment.center : CrossAxisAlignment.start;
return new InkWell(
onTap: () => scaffoldKey.currentState.openDrawer(),
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: horizontalTitleAlignment,
children: <Widget>[
const Text(''),
],
),
),
);
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
if (customerList == null) {
customerList = List<AddCustomer>();
updateListView();
}
return Scaffold(
appBar: new AppBar(
),
body:getCustomerListView(),
floatingActionButton: FloatingActionButton(
onPressed: () {
navigateToCustomer(AddCustomer(), 'Add Person');
},
child: const Icon(Icons.add),
),
}
ListView getCustomerListView() {
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
var _imageFile=customerList[position].custPhoto;
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
backgroundImage: _imageFile==null?AssetImage('images/person_icon.jpg')
:FileImage(File(_imageFile.toString()))),
title: Text(this.customerList[position].custName, style: titleStyle,),
trailing: Icon(Icons.keyboard_arrow_right),
onTap: () {
navigateToDetail(this.customerList[position],'Edit ');
},
),
);
},
);
}
void navigateToDetail(AddCustomer customer, String title) async {
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return Note_Info(customer, title);
}));
if (result == true) {
updateListView();
}
}
void updateListView() {
final Future<Database> dbFuture = DB.init();
dbFuture.then((database) {
Future<List<AddCustomer>> customerListFuture = dbService.getCustomerList();
customerListFuture.then((customerList) {
setState(() {
this.customerList = customerList;
this.count = customerList.length;
});
});
});
}
}

Finally I myself had to solve the problem. The problem was that where I was creating note and saving it in "note" table cust_id was not saved in the table. So I passed CustomerModel object and NoteModel object to New_Note(). Now its working fine
new_note.dart
class NewNote extends StatefulWidget{
final NoteModel note;
final CustomerModel customer;
NewNote(this.customer,this. note);
#override
State<StatefulWidget> createState() {
//return New_NoteState(this.customer);
return New_NoteState(this.customer,this.note);
}
class New_NoteState extends State<NewNote> with SingleTickerProviderStateMixin{
New_NoteState(this.customer,this.note);
NoteModel note=new NoteModel();
CustomerModel customer=new CustomerModel();
}
void _save() async {
note.cust_id=customer.cust_id;
...
}

Related

Flutter Firebase get recordID

In the code below, I am trying to get the id of snap. This info is supposed to be recorded, later if the user tap on save.
But, I am not able to get the right id associated with the project selected. If you can put me to the right direction, it would be appreciated. Many thanks.
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 3.0, 0.0, 0.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('projects')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
const Text("Loading.....");
else {
List<DropdownMenuItem <Project>> goalItems = [];
for (int i = 0; i < snapshot.data.docs.length; i++) {
snap = snapshot.data.docs[i];
// print (gael.id);
// print(snap['project_Name']);
goalItems.add(
DropdownMenuItem(
value: Project(snap['project_Name'], snap.id),//(snap['project_Name'], snap.id),
child: SizedBox(
width:MediaQuery.of(context).size.width*0.89,
child: Text(
(snap['project_Name']),
style: TextStyle(color: Colors.black),
),
),
// style: TextStyle(color: Color(0xff11b719)),
// ),
),
);
}
return Row(
children:<Widget> [
DropdownButton <Project>(
items: goalItems,
onChanged: (Project pro) {
setState(() {
selectedProject = pro.name;
taskNewValue ['project_Name'] = pro.name ;
taskNewValue['project_ID'] = pro.id;
print(pro.id);
});
},
value: selectedProject,
isExpanded: false,
hint: Text(projectName,
style: TextStyle(color: Color (0xff29B6F6)), //Color(0xff0d53dc)),
),
)]);
}
return Container(
height: 0,width: 0,
);
}),
),
class Project {
// var User(this.name, this.id);
final String id;
final String name;
Project(this.id,this.name);
}
//Last modification done. When I remove value, it works, but, when I select a new item, it is not displayed
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 3.0, 0.0, 0.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('projects')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
const Text("Loading.....");
else {
List<DropdownMenuItem <Project>> goalItems = [];
for (int i = 0; i < snapshot.data.docs.length; i++) {
snap = snapshot.data.docs[i];
print(snap.id);
print(snap['project_Name']);
goalItems.add(
DropdownMenuItem(
value: Project(snap.id, snap['project_Name']),
//(snap['project_Name'], snap.id),
child: SizedBox(
width: MediaQuery
.of(context)
.size
.width * 0.89,
child: Text(
(snap['project_Name']),
style: TextStyle(color: Colors.black),
),
),
// style: TextStyle(color: Color(0xff11b719)),
// ),
),
);
}
return Row(
children: <Widget>[
DropdownButton <Project>(
items: goalItems,
onChanged: (Project pro) {
setState(() {
selectedProject = pro.name;
project_ID=pro.id;
myTest = pro.name;
final test4 = Project(project_ID, myTest);
// print('ID');
// print(pro.id);
// print('name');
// print(pro.name);
// print('test4');
// print(test4.id);
// print(test4.name);
// print(project_ID);
});
},
// value: Project(project_ID,myTest),//selectedProject,
isExpanded: false,
hint: Text(projectName,
style: TextStyle(color: Color(
0xff29B6F6)), //Color(0xff0d53dc)),
),
)
]);
}
return Container(
height: 0, width: 0,
);
}),
),
I have created a new page with the following code. I am getting the following error.
The following _TypeError was thrown building StreamBuilder<QuerySnapshot<Object?>>(dirty, dependencies: [MediaQuery], state: _StreamBuilderBaseState<QuerySnapshot<Object?>, AsyncSnapshot<QuerySnapshot<Object?>>>#50df7):
type 'String' is not a subtype of type 'Project?'
I am not sure how to fix that.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
var selectedProject;
var idProject;
DocumentSnapshot snap;
class MyTest extends StatefulWidget {
const MyTest({Key key}) : super(key: key);
#override
_MyTestState createState() => _MyTestState();
}
class _MyTestState extends State<MyTest> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text('test'),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.add_circle_outline,
color: Colors.white,
),
onPressed: () {
},
),
],
),
body:
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 3.0, 0.0, 0.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('projects')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
const Text("Loading.....");
else {
List<DropdownMenuItem <Project>> goalItems = [];
for (int i = 0; i < snapshot.data.docs.length; i++) {
snap = snapshot.data.docs[i];
print(snap.id);
print(snap['project_Name']);
goalItems.add(
DropdownMenuItem(
value: Project(snap.id, snap['project_Name']),
//(snap['project_Name'], snap.id),
child: SizedBox(
width: MediaQuery
.of(context)
.size
.width * 0.89,
child: Text(
(snap['project_Name']),
style: TextStyle(color: Colors.black),
),
),
// style: TextStyle(color: Color(0xff11b719)),
// ),
),
);
}
return Row(
children: <Widget>[
DropdownButton <Project>(
items: goalItems,
onChanged: (Project pro) {
setState(() {
selectedProject = pro.name;
idProject = pro.id;
final test4 = Project(idProject, selectedProject);
});
},
value: selectedProject,
isExpanded: false,
hint: Text('test',//projectName,
style: TextStyle(color: Color(
0xff29B6F6)), //Color(0xff0d53dc)),
),
)
]);
}
return Container(
height: 0, width: 0,
);
}),
),
);
}
}
class Project {
// var User(this.name, this.id);
final String id;
final String name;
Project(this.id,this.name);
}
You need to use a custom model like Project:
class Project {
const Project(this.name, this.id);
final String name;
final String id;
}
And instead of adding just the String names to the DropdownButton items add those models:
List<DropdownMenuItem> goalItems = [];
for (int i = 0; i < snapshot.data.docs.length; i++) {
DocumentSnapshot snap = snapshot.data.docs[i];
test = snapshot.data.docs[i];
goalItems.add(
DropdownMenuItem(
value: Project(snap['project_Name'], snap.id),
child: SizedBox(
width:MediaQuery.of(context).size.width*0.89,
child: Text(
(snap['project_Name']),
style: TextStyle(color: Colors.black),
),
),
// style: TextStyle(color: Color(0xff11b719)),
// ),
value: (snap['project_Name']),
),
);
}
Then you can get the id from the Project on the onClick listener:
return Row(
children:<Widget> [
DropdownButton(
items: goalItems,
onChanged: (Project pro) {
setState(() {
selectedProject = pro;
taskNewValue ['project_Name'] = pro.name ;
taskNewValue['project_ID'] = pro.id;
//here I am trying to get the id of the record, so I can record the id when the user select save
});
},
value: selectedProject,
isExpanded: false,
hint: Text(projectName,
style: TextStyle(color: Color (0xff29B6F6)), //Color(0xff0d53dc)),
),
)]);
}

The getter 'data' was called on null. Receiver: null Tried calling: data

Tried signing up user with Firebase but got the above error instead, and the code is formatted in such a way that it should redirect the user to homepage after a successful signup.
My main.dart
import 'package:bedc_app/home_page.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:bedc_app/auth/login_page.dart';
void main() async{
//solution study
WidgetsFlutterBinding.ensureInitialized();
FirebaseAuth.instance.currentUser()
.then((FirebaseUser user) {
if(user != null){
Firestore.instance.collection('users').document(user.uid).get().then((DocumentSnapshot doc){
runApp(MyApp(true, doc));
});
return;
}
runApp(MyApp(false, null));
});
}
// void main() =>runApp(MyApp());
class MyApp extends StatelessWidget {
bool isLoggedIn;
DocumentSnapshot doc;
MyApp(this.isLoggedIn, this.doc);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Multifunctional bedc project App',
debugShowCheckedModeBanner: false,
home: isLoggedIn ? HomePage(userDoc: doc) : loginPage(),
);
}
}
My Signup_page.dart
import 'package:bedc_app/auth/login_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:bedc_app/utils/constants.dart';
import '../home_page.dart';
class SignUpPage extends StatefulWidget {
#override
_SignUpPageState createState() => _SignUpPageState();
}
class _SignUpPageState extends State<SignUpPage> {
String email, password;
bool isLoggedIn = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ListView(
children: [
SizedBox(
height: 50,
),
Align(
alignment: Alignment.topCenter,
child: (
Image.asset(
'assets/bedclogo.jpg'
)),
),
SizedBox(
height: 100,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0),
child: TextField(
decoration: InputDecoration(
hintText: 'email',
border: OutlineInputBorder(),
labelText: 'Email',
suffixIcon: Icon(Icons.email, color: Colors.green)
),
keyboardType: TextInputType.emailAddress,
onChanged: (String val){
email = val;
},
),
),
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0),
child: TextField(
decoration: InputDecoration(
hintText: 'Password',
border: OutlineInputBorder(),
labelText: 'Password',
suffixIcon: Icon(Icons.lock, color: Colors.green)
),
obscureText: true,
obscuringCharacter: '!',
keyboardType: TextInputType.emailAddress,
onChanged: (String val){
password = val;
},
),),
SizedBox(
height: 5,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26),
child: MaterialButton(
color: Color(0xFF88C540),
child: isLoading ? Container(
height: 24,
width: 24,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
strokeWidth: 2,
)
):Text(
'SignUp'.toUpperCase(),
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: (){
checkUserInput();
},
),
),
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: RichText(
text: TextSpan(
text: 'Already have an Account ?',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'Login here',
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()..onTap = (){
Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_) => loginPage()));
},
)
]
)),
)
],
));
}
//SIGNUP FUNCTION USING FIREBASE
bool isLoading = false;
signUpUserWithFirebase(){
FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password
).then((AuthResult result){
//Authresult cant be stored to string.....
storeUserDataToFirestore(result.user.uid);
if(result.user.uid != null){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => HomePage()));
}
}).catchError((e){
print(e);
isLoading = false;
setState(() { });
});
}
//storeUserDataToFirestore
storeUserDataToFirestore(String userId){
Map<String, dynamic> userData = Map<String, dynamic>();
userData = {
Constants.USERNAME: '#username',
Constants.USER_ID: userId,
Constants.EMAIL : email,
Constants.ACCOUNT_NUM : '00000000',
Constants.IS_ADMIN: false,
Constants.PROFILE_IMAGE: '',
Constants.PROFILE_IMAGE_THUMB: '',
};
CollectionReference usersRef = Firestore.instance.collection(Constants.USERS_COLLECTION);
usersRef.document(userId).setData(userData).then((_){
isLoading = false;
setState(() { });
}).catchError((e){
print(e);
});
}
//FUNCTION TO CHECK USER ENTRY
checkUserInput(){
isLoading = true;
setState(() { });
if(email == null || email.isEmpty){
print('Enter email');
isLoading = false;
setState(() { });
return;
}
if(password == null || email.isEmpty){
print('Enter password');
isLoading = false;
setState(() { });
}
//SIGNUP THE USER
signUpUserWithFirebase();
}
getUserData(String UserId){
Firestore.instance.collection(Constants.USERS_COLLECTION)
.document(UserId)
.get().then((DocumentSnapshot userDoc){
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_) => HomePage(userDoc: userDoc)));
}).catchError((e){
print(e);
});
}
}
My Home_page.dart
import 'dart:ui';
import 'package:bedc_app/auth/login_page.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:bedc_app/auth/signup_page.dart';
import 'model/user.dart';
User currentUser;
class HomePage extends StatefulWidget {
DocumentSnapshot userDoc;
HomePage({this.userDoc,});
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//user cant access this page unless logged in
bool userIsLoggedIn = true;
#override
void initState() {
// TODO: implement initState
super.initState();
currentUser = User.fromSnapshot(widget.userDoc);
}
//HERE IS BUILDING PROFILE IMAGE
Widget buildProfileImage(){
if(currentUser.profileImage == ''){
return CircleAvatar(
backgroundColor: Colors.white70,
radius: 20,
child: Icon(Icons.person, size: 30),
);
}else{
return CircleAvatar(
backgroundImage: NetworkImage(currentUser.profileImage),
);
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.brown,
title: Text('Homepage'),
),
body: ListView(
children: [
SizedBox(height: 20),
Align(
alignment: Alignment.topRight,
child: (
Image.asset(
'assets/bedclogo.jpg',
scale: 2,
)),
),
ListTile(
title: Text(currentUser.username, style: TextStyle(color: Colors.brown, fontWeight: FontWeight.bold),),
leading: buildProfileImage(),
),
SizedBox(height: 30),
//CAPTURE METER READING UI
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: GestureDetector(
onTap: (){
//print(currentUser.isAdmin);
},
child: Row(
children: [
//CAPTURE METR READING
Container(
width: 100,
height: 100,
color: Colors.green,
child: Align(
alignment: Alignment.center,
child: (
Image.asset(
'assets/meterIcon.png',
scale: 3,
)),
),
),
Container(
width: 100,
height: 100,
child: Align(
alignment: Alignment.center,
child: Text('Capture Meter Reading'.toUpperCase()),
),
),
],
),
),
),
SizedBox(height: 30),
//UPDATE PROFILE UI DESIGN
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: GestureDetector(
onTap:(){
//print(currentUser.isAdmin);
},
child: Row(
children: [
Container(
width: 100,
height: 100,
color: Colors.green,
child: Align(
alignment: Alignment.center,
child: (
Image.asset(
'assets/profileAvatar.png',
scale: 3,
)),
),
),
Container(
width: 100,
height: 100,
child: Align(
alignment: Alignment.center,
child: Text('Update Profile'.toUpperCase()),
),
),
],
),
),
),
// TextButton (
// onPressed: (){
// //just logout, implement pop dialog later
// // FirebaseAuth.instance.signOut();
// // Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_)=> loginPage()));
// // currentUser = null;
// // setState(() {
// // });
// // still showing error... try to fix asap
// FirebaseAuth.instance.signOut().then((_){
// userIsLoggedIn = false;
// Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_)=> loginPage()));
// //currentUser = null;
// setState(() {
//
// });
// });
// },
// child: Text('SignOut'))
],
)
);
}
}
User.dart for user Model
import 'package:bedc_app/utils/constants.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class User{
String username;
String email;
String userId;
String accountNum;
bool isAdmin;
String profileImage;
String profileImageThumb;
User({
this.username,
this.email,
this.userId,
this.accountNum,
this.isAdmin,
this.profileImage,
this.profileImageThumb
});
factory User.fromSnapshot(DocumentSnapshot doc){
return User(
username: doc.data[Constants.USERNAME],
email: doc.data[Constants.EMAIL],
userId: doc.data[Constants.USER_ID],
accountNum: doc.data[Constants.ACCOUNT_NUM],
isAdmin: doc.data[Constants.IS_ADMIN],
profileImage: doc.data[Constants.PROFILE_IMAGE],
profileImageThumb: doc.data[Constants.PROFILE_IMAGE_THUMB]
);
}
}
Having signed the user up, im getting error The getter 'data' was called on null. Receiver: null Tried calling: data.
i dont know which part of the code is causing this exception
This error generally show when you try to get some attribute on null object.
In your case, call one of doc.data[..] in file User.dart generate the error that means doc == null is true.
Verify that widget.userDoc is not null at this line currentUser = User.fromSnapshot(widget.userDoc); on initState method of Home_page.dart file
So error provide in one of HomePage call who require a non null userDoc (todo: make sure your data is not null using assert and add #required before required variable on your class exp: HomePage({#required this.userDoc,}): assert(userDoc != null, "BOooh my userDoc may be not null"); that help very much when you debug your app)
finaly your problem provide in signUpUserWithFirebase() method of your Signup_page.dart file... here Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => HomePage()));
Try to replace by this getUserData(result.user.uid) so your methode should become
signUpUserWithFirebase(){
FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password
).then((AuthResult result){
//Authresult cant be stored to string.....
storeUserDataToFirestore(result.user.uid);
if(result.user.uid != null){
getUserData(result.user.uid); // replaced line
}
}).catchError((e){
print(e);
isLoading = false;
setState(() { });
});
}

A non-null String must be provided to a Text widget flutter

I have this problem with this code. I tried to solve the problem, but I did not succeed. Please Help
Please see the screenshots to understand the problem well
A non-null String must be provided to a Text widget.
'package:flutter/src/widgets/text.dart':
Failed assertion: line 370 pos 10: 'data != null'
Pictures description error
null in firebase
The users email address.Will be null if signing in anonymously.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
class ChatScreen extends StatefulWidget {
static const Id = 'chat_screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
// ignore: deprecated_member_use
final _firestore = Firestore.instance;
final _auth = FirebaseAuth.instance;
// ignore: deprecated_member_use
FirebaseUser loggedInUser;
String messageText;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
// ignore: await_only_futures
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
// void getMessages() async {
// // ignore: deprecated_member_use
// final messages = await _firestore.collection('Messages').getDocuments();
// // ignore: deprecated_member_use
// for (var message in messages.docs) {
// print(message.data());
// }
// }
void messagesStream() async {
await for (var snapshot in _firestore.collection('Messages').snapshots()) {
for (var message in snapshot.docs) {
print(message.data());
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
messagesStream();
//_auth.signOut();
//Navigator.pop(context);
}),
],
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(),
// ignore: missing_return
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
));
}
// ignore: deprecated_member_use
final messages = snapshot.data.documents;
List<Messagebubble> messagebubbles = [];
for (var message in messages) {
final messageText = message.data()['text'];
final messagesendar = message.data()['Sender'];
final messagebubble = Messagebubble(
sendar: messagesendar,
text: messageText,
);
messagebubbles.add(messagebubble);
}
return Expanded(
child: ListView(
padding: EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 20.0,
),
children: messagebubbles,
),
);
},
),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
_firestore.collection('Messages').add({
'text': messageText,
'Sender': loggedInUser,
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class Messagebubble extends StatelessWidget {
Messagebubble({
Key key,
this.sendar,
this.text,
}) : super(key: key);
final String sendar;
final String text;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
Text(
sendar,
style: TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
Material(
borderRadius: BorderRadius.circular(30.0),
elevation: 5.0,
color: Colors.lightBlueAccent,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
),
),
],
),
);
}
}
You just need to check whether the text that you are passing is null or not. If it is null, you can show that the user is Anonymous.
final messageText = message.data()['text'];
final messagesendar = message.data()['Sender'] ?? 'Anonymous'; // If null then use 'Anonymous'
final messagebubble = Messagebubble(
sendar: messagesendar,
text: messageText,
);
Flutter doesn't allow you to pass null to Text widgets.

The method '[]' was called on null. I/flutter (Receiver: null I/flutter (18112): Tried calling: []("id"))

I'm trying to go to timeline_details page from activity_feed page by clicking on the image but I'm getting this error:
I/flutter (28907): The following NoSuchMethodError was thrown building FutureBuilder<DocumentSnapshot>(dirty, state:
I/flutter (28907): _FutureBuilderState<DocumentSnapshot>#ddf5c):
I/flutter (28907): The method '[]' was called on null.
I/flutter (28907): Receiver: null
I/flutter (28907): Tried calling: []("id")
I/flutter (28907):
I/flutter (28907): The relevant error-causing widget was:
I/flutter (28907): FutureBuilder<DocumentSnapshot>
I/flutter (28907): file:///F:/COURS%20DE%20PROGRAMMATION/PROJETS/MOBILE/minisocialnetwork/lib/pages/timeline_details.dart:272:12
I/flutter (28907):
I/flutter (28907): When the exception was thrown, this was the stack:
I/flutter (28907): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
I/flutter (28907): #1 DocumentSnapshot.[] (package:cloud_firestore/src/document_snapshot.dart:31:42)
I/flutter (28907): #2 new User.fromDocument (package:minisocialnetwork/models/user.dart:26:16)
I/flutter (28907): #3 _TimelineDetailsState.buildPostHeader.<anonymous closure> (package:minisocialnetwork/pages/timeline_details.dart:278:26)
I/flutter (28907): #4 _FutureBuilderState.build (package:flutter/src/widg**
Here are the images for references:
activity_feed page:
activity_feed page
timeline_details page:
timeline_details page
This is my full code:
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:minisocialnetwork/models/user.dart';
import 'package:minisocialnetwork/pages/post_screen.dart';
import 'package:minisocialnetwork/pages/timeline.dart';
import 'package:minisocialnetwork/pages/timeline_details.dart';
import 'package:minisocialnetwork/widgets/header.dart';
import 'package:minisocialnetwork/widgets/progress.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'home.dart';
import 'profile.dart';
class ActivityFeed extends StatefulWidget {
#override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
getActivityFeed() async {
QuerySnapshot snapshot = await activityFeedRef
.document(currentUser.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.getDocuments();
List<ActivityFeedItem> feedItems = [];
snapshot.documents.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
// print('Activity Feed Item: ${doc.data}');
});
return feedItems;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: header(context, titleText: "Notifications"),
body: Container(
child: FutureBuilder(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
);
},
)),
);
}
}
Widget mediaPreview;
String activityItemText;
class ActivityFeedItem extends StatelessWidget {
final User currentUser;
final String profileId;
final String username;
final String userId;
final String type; // 'like', 'follow', 'comment'
final String mediaUrl;
final String postId;
final String title;
final String content;
final String category;
final String ownerId;
bool isLiked;
int likeCount;
Map likes;
final String userProfileImg;
final String commentData;
final Timestamp timestamp;
ActivityFeedItem({
this.currentUser,
this.profileId,
this.username,
this.userId,
this.type,
this.mediaUrl,
this.postId,
this.title,
this.content,
this.category,
this.ownerId,
this.isLiked,
this.likeCount,
this.likes,
this.userProfileImg,
this.commentData,
this.timestamp,
});
factory ActivityFeedItem.fromDocument(DocumentSnapshot doc) {
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
ownerId: doc['ownerId'],
title: doc['title'],
content: doc['content'],
category: doc['selectedCategory'],
likes: doc['likes'],
type: doc['type'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
mediaUrl: doc['mediaUrl'],
);
}
showTimelineDetails(BuildContext context,
{User currentUser,
String profileId,
String postId,
String ownerId,
String userId,
String mediaUrl,
String username,
String title,
String content,
String category,
Map likes,
int likeCount,
Timestamp timestamp,
bool isLiked}) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return TimelineDetails(
currentUser: currentUser,
profileId: profileId,
postId: postId,
ownerId: ownerId,
username: username,
title: title,
content: content,
category: category,
mediaUrl: mediaUrl,
likes: likes,
likeCount: likeCount,
isLiked: isLiked,
timestamp: timestamp,
);
}));
}
configureMediaPreview(context) {
if (type == "like" || type == 'comment') {
mediaPreview = GestureDetector(
onTap: () => showTimelineDetails(context),
child: Container(
height: 50.0,
width: 50.0,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: CachedNetworkImageProvider(mediaUrl),
),
),
)),
),
);
} else {
mediaPreview = Text('');
}
if (type == 'like') {
activityItemText = "a aimé votre article";
} else if (type == 'follow') {
activityItemText = "vous suit";
} else if (type == 'comment') {
activityItemText = 'a écrit: $commentData';
} else {
activityItemText = "Error: Unknown type '$type'";
}
}
#override
Widget build(BuildContext context) {
configureMediaPreview(context);
return Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Container(
color: Colors.white54,
child: ListTile(
leading: GestureDetector(
onTap: () => showProfile(context, profileId: userId),
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(userProfileImg),
),
),
title: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: username,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
subtitle: Text(
timeago.format(timestamp.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
),
);
}
}
showProfile(BuildContext context, {String profileId}) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
profileId: profileId,
),
),
);
}
and
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:minisocialnetwork/models/user.dart';
import 'package:minisocialnetwork/widgets/custom_image.dart';
import 'package:minisocialnetwork/widgets/post.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:share/share.dart';
import '../widgets/progress.dart';
import '../models/user.dart';
import 'activity_feed.dart';
import 'home.dart';
//final usersRef = Firestore.instance.collection('users');
class TimelineDetails extends StatefulWidget {
final User currentUser;
final String profileId;
final postId;
final ownerId;
final username;
final title;
final content;
final category;
final mediaUrl;
final likes;
final likeCount;
bool isLiked;
final Timestamp timestamp;
TimelineDetails(
{this.currentUser,
this.profileId,
this.postId,
this.ownerId,
this.username,
this.title,
this.content,
this.category,
this.mediaUrl,
this.likes,
this.likeCount,
this.isLiked,
this.timestamp});
factory TimelineDetails.fromDocument(DocumentSnapshot doc) {
return TimelineDetails(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
title: doc['title'],
content: doc['content'],
category: doc['selectedCategory'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
// if no likes, return 0
if (likes == null) {
return 0;
}
int count = 0;
// if the key is explicitly set to true, add a like
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_TimelineDetailsState createState() => _TimelineDetailsState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
title: this.title,
content: this.content,
category: this.category,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
timestamp: this.timestamp,
);
}
class _TimelineDetailsState extends State<TimelineDetails> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final String currentUserId = currentUser?.id;
List<Post> posts;
bool isFollowing = false;
bool isLoading = false;
int postCount = 0;
int followerCount = 0;
int followingCount = 0;
List<String> followingList = [];
final String postId;
final String ownerId;
final String username;
final String title;
final String content;
final String category;
final String mediaUrl;
final Timestamp timestamp;
bool showHeart = false;
bool isLiked = false;
int likeCount;
Map likes;
_TimelineDetailsState({
this.postId,
this.ownerId,
this.username,
this.title,
this.content,
this.category,
this.mediaUrl,
this.likes,
this.likeCount,
this.timestamp,
});
#override
void initState() {
super.initState();
}
handleDeletePost(BuildContext parentContext) {
return showDialog(
context: parentContext,
builder: (context) {
return SimpleDialog(
title: Text("Supprimer cet article?"),
children: <Widget>[
SimpleDialogOption(
onPressed: () {
Navigator.pop(context);
deletePost();
},
child: Text(
'Supprimer',
style: TextStyle(color: Colors.red, fontSize: 18.0),
)),
SimpleDialogOption(
onPressed: () => Navigator.pop(context),
child: Text(
'Annuler',
style: TextStyle(fontSize: 18.0),
)),
],
);
});
}
// Note: To delete post, ownerId and currentUserId must be equal, so they can be used interchangeably
deletePost() async {
// delete post itself
postsRef
.document(ownerId)
.collection('userPosts')
.document(postId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
// delete uploaded image for thep ost
storageRef.child("post_$postId.jpg").delete();
// then delete all activity feed notifications
QuerySnapshot activityFeedSnapshot = await activityFeedRef
.document(ownerId)
.collection("feedItems")
.where('postId', isEqualTo: postId)
.getDocuments();
activityFeedSnapshot.documents.forEach((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
// then delete all comments
QuerySnapshot commentsSnapshot = await commentsRef
.document(postId)
.collection('comments')
.getDocuments();
commentsSnapshot.documents.forEach((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
}
handleLikePost() {
bool _isLiked = likes[currentUserId] == true;
if (_isLiked) {
postsRef
.document(ownerId)
.collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': false});
removeLikeFromActivityFeed();
setState(() {
likeCount -= 1;
isLiked = false;
likes[currentUserId] = false;
});
} else if (!_isLiked) {
postsRef
.document(ownerId)
.collection('userPosts')
.document(postId)
.updateData({'likes.$currentUserId': true});
addLikeToActivityFeed();
setState(() {
likeCount += 1;
isLiked = true;
likes[currentUserId] = true;
showHeart = true;
});
Timer(Duration(milliseconds: 500), () {
setState(() {
showHeart = false;
});
});
}
}
addLikeToActivityFeed() {
// add a notification to the postOwner's activity feed only if comment made by OTHER user (to avoid getting notification for our own like)
bool isNotPostOwner = currentUserId != ownerId;
if (isNotPostOwner) {
activityFeedRef
.document(ownerId)
.collection("feedItems")
.document(postId)
.setData({
"type": "like",
"username": currentUser.username,
"userId": currentUser.id,
"userProfileImg": currentUser.photoUrl,
"postId": postId,
"mediaUrl": mediaUrl,
"timestamp": timestamp,
});
}
}
removeLikeFromActivityFeed() {
bool isNotPostOwner = currentUserId != ownerId;
if (isNotPostOwner) {
activityFeedRef
.document(ownerId)
.collection("feedItems")
.document(postId)
.get()
.then((doc) {
if (doc.exists) {
doc.reference.delete();
}
});
}
}
buildPostHeader() {
return FutureBuilder(
future: usersRef.document(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
User user = User.fromDocument(snapshot.data);
return ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 8.0,
top: 12.0,
right: 8.0,
bottom: 8.0,
),
child: Text(
title,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 22.0,
),
),
),
ListTile(
leading: GestureDetector(
onTap: () => showProfile(context, profileId: user.id),
child: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(user.photoUrl),
backgroundColor: Colors.grey,
),
),
title: Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(
username,
),
),
// Container(
// width: 100,
// height: 27,
// child: buildProfileButton(),
// ),
],
),
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
timeago.format(timestamp.toDate(), locale: 'fr'),
overflow: TextOverflow.ellipsis,
),
Text(category),
],
),
// trailing: IconButton(
// onPressed: () => handleDeletePost(context),
// icon: Icon(Icons.more_vert),
// ),
),
Container(child: cachedNetworkImage(mediaUrl)),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(content, style: TextStyle(fontSize: 18.0)),
),
],
);
},
);
}
void goBack() {
Navigator.pop(context);
}
#override
Widget build(context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: GestureDetector(
onTap: goBack,
child: Icon(
Icons.arrow_back,
color: Colors.white,
)),
title: Text(
'MiaKoz',
style: TextStyle(color: Colors.white),
),
),
body: Container(child: buildPostHeader()),
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
GestureDetector(
onTap: handleLikePost,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Image(
image: isLiked
? AssetImage("assets/images/clap.png")
: AssetImage("assets/images/no_clap.png"),
color: Colors.cyan,
fit: BoxFit.scaleDown,
alignment: Alignment.center,
width: 28,
height: 28,
),
),
),
// GestureDetector(
// onTap: handleLikePost,
// child: Icon(
// isLiked ? Icons.favorite : Icons.favorite_border,
// size: 28.0,
// color: Colors.cyan,
// ),
// ),
Container(
margin: EdgeInsets.only(left: 8.0),
child: Text(
"$likeCount claps",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
GestureDetector(
onTap: () => showComments(
context,
postId: postId,
ownerId: ownerId,
mediaUrl: mediaUrl,
),
child: Icon(
Icons.chat,
size: 28.0,
color: Colors.cyan,
),
),
IconButton(
icon: Icon(
Icons.share,
color: Theme.of(context).primaryColor,
),
onPressed: () {
final RenderBox box = context.findRenderObject();
Share.share('${title} - ${mediaUrl}',
subject: content,
sharePositionOrigin:
box.localToGlobal(Offset.zero) & box.size);
}),
],
),
),
),
);
}
}

How to restore data in Listview

My goal is restoring data from sql database to Listview. Firstly, I started with creating database and model class. Secondly I realized that I have to use FutureBuilder. But I can't understood, how to use this stuff in my case. In addition I have known, that have to use GlobalKey.
This is my code. In this version of my code, Alert dialog doesn't work
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:path/path.dart';
import 'dart:ui';
import 'package:samuraigym/program_training_handler.dart';
import 'package:samuraigym/my_icons_icons.dart' as custicon;
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:io' as io;
import 'package:fluttertoast/fluttertoast.dart';
class MeasurementsScreen extends StatefulWidget {
#override
_MeasurementsScreenState createState() => _MeasurementsScreenState();
}
class _MeasurementsScreenState extends State<MeasurementsScreen> {
List<ListItem> listItems;
final scaffoldKey = new GlobalKey<ScaffoldState>();
final formKey = new GlobalKey<FormState>();
String typeOfMuscle;
String numberOfMuscle;
var nameItem = ["Рост","Вес","Шея","Плечевой пояс","Грудь","Бицепс",
"Предплечье","Запястье","Живот","Бедро","Голень","Лодыжка"];
#override
void initState() {
super.initState();
initListItems();
}
void initListItems() {
listItems = [
new ListItem(
detail: nameItem[0],
index: 0,
data: " "),
new ListItem(
detail: nameItem[1],
index: 1,
data: " "),
new ListItem(
detail: nameItem[2],
index: 2,
data: " "),
new ListItem(
detail: nameItem[3],
index: 3,
data: " "),
new ListItem(
detail: nameItem[4],
index: 4,
data: " "),
new ListItem(
detail: nameItem[5],
index: 5,
data: " "),
new ListItem(
detail: nameItem[6],
index: 6,
data: " "),
new ListItem(
detail: nameItem[7],
index: 7,
data: " "),
new ListItem(
detail: nameItem[8],
index: 8,
data: " "),
new ListItem(
detail: nameItem[9],
index: 9,
data: " "),
new ListItem(
detail: nameItem[10],
index: 10,
data: " "),
new ListItem(
detail: nameItem[11],
index: 11,
data: " ")
];
}
void sumbitContact(int index, String numberOfMuscle) {
if(this.formKey.currentState.validate())
formKey.currentState.save();
else
return null;
var measurementsDatabaseModel = MeasurementsDatabaseModel();
measurementsDatabaseModel.numberOfMuscle = numberOfMuscle;
measurementsDatabaseModel.typeOfMuscle = index as String;
var dbHelper = DatabaseHelperForMeasurements();
dbHelper.addNewMeasurementsDatabaseModel(measurementsDatabaseModel);
Fluttertoast.showToast(msg: 'Contact was saved',
toastLength: Toast.LENGTH_SHORT);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: scaffoldKey,
backgroundColor: Color(0xff2b2b2b),
appBar: AppBar(
backgroundColor: Colors.lightGreen[400],
title: Text(
'Замеры',
style: new TextStyle(
color: Colors.white
),),
leading: IconButton(
icon:Icon(Icons.arrow_back),
color: Colors.white ,
onPressed:() => Navigator.of(context).pop(),
),
),
body: FutureBuilder<List<MeasurementsDatabaseModel>>(
future: getMeasurementsDatabaseModelFromDB(),
builder: (context, snapshot){
if(snapshot.data != null && snapshot.hasData){
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: listItems.length,
itemBuilder: (BuildContext ctxt, int index) => listItems[index],
);
} else {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: listItems.length,
itemBuilder: (BuildContext ctxt, int index) => listItems[index],
);
}
}
)
);
}
}
Future<List<MeasurementsDatabaseModel>> getMeasurementsDatabaseModelFromDB() async {
var dbHelper = DatabaseHelperForMeasurements();
Future<List<MeasurementsDatabaseModel>> contacts = dbHelper.getMeasurementsDatabaseModel();
return contacts;
}
class ListItem extends StatefulWidget {
String detail;
int index;
String data;
_MeasurementsScreenState measurementsScreen;
ListItem({Key key, this.detail, this.index, this.data}) : super(key: key);
#override
_ListItem createState() => _ListItem(measurementsScreen);
}
class _ListItem extends State<ListItem> {
bool isAppear = false;
final _MeasurementsScreenState measurementsScreen;
DatabaseHelperForMeasurements db = DatabaseHelperForMeasurements();
_ListItem(this.measurementsScreen);
MeasurementsDatabaseModel measurementsDatabaseModel = new MeasurementsDatabaseModel();
String typeOfMuscle;
String numberOfMuscle;
String lastSelectedValue;
var name = ["Рост","Вес","Шея","Плечевой пояс","Грудь","Бицепс",
"Предплечье","Запястье","Живот","Бедро","Голень","Лодыжка"];
var indication = ["Ваш рост","Ваш вес","Ваша шея","Ваш плечевой пояс","Ваша грудь","Ваш бицепс",
"Ваше предплечье","Ваше запястье","Ваш живот","Ваше бедро","Ваша голень","Ваша лодыжка"];
var prefix = ["см: ","кг: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: ","см: "];
var prefixAlert = ["см","кг","см","см","см","см","см","см","см","см","см","см"];
TextEditingController customcintroller;
Future<String> createAlertDialog(BuildContext context, int indexAl) async{
customcintroller = TextEditingController();
String returnVal = await showDialog(
context: context, builder: (context){
return AlertDialog(
title: Text(name[indexAl]),
content: TextFormField(
textDirection: TextDirection.ltr,
controller: customcintroller,
style: TextStyle(
color: Colors.lightGreen[400],
fontSize: 18.5),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 4.0),
labelText: indication[indexAl],
suffixText: prefixAlert[widget.index],
alignLabelWithHint: false,
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.done,
onSaved: (val) => this.numberOfMuscle = val,
),
actions: <Widget>[
FlatButton(
child: const Text('ОТМЕНА'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('ОК'),
onPressed: () {
setState(() {
widget.data = customcintroller.text.toString();
isAppear = !isAppear;
measurementsScreen.sumbitContact(widget.index, widget.data);
getMeasurementsDatabaseModelFromDB();
Navigator.of(context).pop();
});
},
),
],
);
});
return returnVal;
}
#override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
onTap: () {
createAlertDialog(context, widget.index);
},
child: Container(
color: Color(0xff2b2b2b),
height: 55.0,
margin: const EdgeInsets.symmetric(
vertical: 1.0,
),
child: new Stack(
children: <Widget>[
new Container(
child: new SizedBox.expand(
child: Container(
alignment: Alignment.center,
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: Icon(
// custicon.MyIcons.bathroom_scale,
// color: Colors.lightGreen[400],
// size: 40.0)),
Padding(
padding: const EdgeInsets.all(16.0),
child: new Text(
widget.detail,
style:
new TextStyle(fontSize: 16.0, color: Colors.white),
),
),
Container(
alignment: Alignment.centerRight,
child: isAppear ? Padding(
padding: EdgeInsets.all(8.0),
child: Container(
decoration: ShapeDecoration(
color: Colors.lightGreen[400],
shape: RoundedRectangleBorder(
side: BorderSide(width: 1.0, style: BorderStyle.solid, color: Colors.white),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
child: Padding(
padding: EdgeInsets.all(4.0),
child: new Text(
prefix[widget.index] + widget.data,
style: new TextStyle(
fontSize: 16.0,
color: Colors.white
),
)))) : SizedBox(),
)
],
),
),
),
),
],
),
)));
}
}
class MeasurementsDatabaseModel{
int id;
String typeOfMuscle;
String numberOfMuscle;
MeasurementsDatabaseModel();
}
class DatabaseHelperForMeasurements{
static Database db_instance;
final String TABLE_NAME = "Measurements";
Future<Database> get db async{
if(db_instance == null)
db_instance = await initDB();
return db_instance;
}
initDB() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path,"Measurements_db.db");
var db = await openDatabase(path,version: 1, onCreate: onCreateFunc);
return db;
}
void onCreateFunc(Database db, int version) async{
await db.execute('CREATE TABLE $TABLE_NAME(id INTEGER PRIMARY KEY AUTOINCREMENT, typeOfMuscle TEXT, numberOfMuscle TEXT);');
}
Future<List<MeasurementsDatabaseModel>> getMeasurementsDatabaseModel() async{
var db_connection = await db;
List<Map> list = await db_connection.rawQuery('SELECT * FROM $TABLE_NAME');
List<MeasurementsDatabaseModel> modelList = new List();
for(int i = 0; i < list.length; i++){
MeasurementsDatabaseModel measurementsDatabaseModel = new MeasurementsDatabaseModel();
measurementsDatabaseModel.id = list[i]['id'];
measurementsDatabaseModel.typeOfMuscle = list[i]['typeOfMuscle'];
measurementsDatabaseModel.numberOfMuscle = list[i]['numberOfMuscle'];
modelList.add(measurementsDatabaseModel);
}
return modelList;
}
void addNewMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
var db_connection = await db;
String query =
'INSERT INTO $TABLE_NAME(name,phone) VALUES( \'${measurementsDatabaseModel.typeOfMuscle}\',\'${measurementsDatabaseModel.numberOfMuscle}\')';
await db_connection.transaction((transition) async{
return await transition.rawInsert(query);
});
}
void updateMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
var db_connection = await db;
String query =
'UPDATE $TABLE_NAME SET name =\'${measurementsDatabaseModel.typeOfMuscle}\',phone =\'${measurementsDatabaseModel.typeOfMuscle}\' WHERE id =${measurementsDatabaseModel.id}';
await db_connection.transaction((transition) async{
return await transition.rawQuery(query);
});
}
void deleteMeasurementsDatabaseModel(MeasurementsDatabaseModel measurementsDatabaseModel) async {
var db_connection = await db;
String query = 'DELETE FROM $TABLE_NAME WHERE id = ${measurementsDatabaseModel.id}';
await db_connection.transaction((transition) async{
return await transition.rawQuery(query);
});
}
}
This is gif, where I want to save data. How can you see, there is container with text near right part of screen. There I want to save data, which I put in AlertDialog.
Firstly, we are going to discuss its simplified implementation. At the MainScreen,
we won't connect it to SQLite. But later on, We can discuss more complex
implementation by calling SQLite queries.
Should we use FutureBuilder ?
In many tutorials spread accross internet, app screen can shows Loading indicator, and later on, it shows ListView and its ListTile after fetching to Database.
It works well if there are no further interaction with ListView later on.
As we using future, the builder will only triggered twice.
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: queryDatabase(), // Calling Database
builder: (context, snapshot) {
if (snapshot.hasData) { // After Callback, it may triggers this Part
return ListView.builder(
itemBuilder: (_, index) {
return ListTile(
title: Text("$index"),
);
},
);
}
return Center( // First Triggering this Part
child: CircularProgressIndicator(),
);
},
),
);
}
}
The solution is Stateful Widget !
By using Stateful Widget, we can store our products, in variable. Therefore,
each time MainScreen's build method called, the app will display updated List.
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
List<Map<String, dynamic>> products = []; // Store Item, Here
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: Container(
child: renderProducts(), // Render stored Item, here
),
);
}
We can see at the demo below, as we interact with buttons, the build method
always being re-called, and fortunately, we can have our Expected Screen
Identified Problems
class _ListItem extends State<ListItem> {
String typeOfMuscle;
String numberOfMuscle;
TextEditingController customcintroller;
...
AlertDialog(
title: Text(name[indexAl]),
content: TextFormField(
controller: customcintroller,
onSaved: (val) => this.numberOfMuscle = val,
),
);
),
the problem is, in onSaved method, the app will only update local variable that
resides on each of ListItem. Therefore, the app does not know that the new value
should be displayed.
How to make the widget rerenders ?
By using setState((){}) as the code below
class _ListItem extends State<ListItem> {
String typeOfMuscle;
String numberOfMuscle;
TextEditingController customcintroller;
...
void updateAndRerender(val){
this.numberOfMuscle = val;
setState((){});
}
AlertDialog(
title: Text(name[indexAl]),
content: TextFormField(
controller: customcintroller,
onSaved: (val) {
updateAndRerender(val);
},
),
);
),
Working Example-App Repo
you may look into this repository. Github

Resources