How to perform CRUD operations with BLoC pattern? - sqlite

I was learning on flutter how to perform CRUD operations using BLoC pattern, I saw a tutorial online and try to apply it. C,R,U are OK, but deletion is not working. this is the source code.
Please Help !
file todo_bloc.dart
import 'package:zencartos/models/todo.dart';
import 'package:zencartos/repository/todo_repository.dart';
class TodoBloc {
//Get instance of the Repository
final _todoRepository = TodoRepository();
final _todoController = StreamController<List<Todo>>.broadcast();
get todos => _todoController.stream;
TodoBloc() {
getTodos();
}
getTodos({String query}) async {
_todoController.sink.add(await _todoRepository.getAllTodos(query: query));
}
addTodo(Todo todo) async {
await _todoRepository.insertTodo(todo);
getTodos();
}
updateTodo(Todo todo) async {
await _todoRepository.updateTodo(todo);
getTodos();
}
deleteTodoById(int id) async {
_todoRepository.deleteTodoById(id);
getTodos();
}
dispose() {
_todoController.close();
}
}
DAO Page todo_dao.dart
import 'package:zencartos/database/database.dart';
import 'package:zencartos/models/todo.dart';
class TodoDao {
final dbProvider = DatabaseProvider.dbProvider;
Future<int> createTodo(Todo todo) async{
final db = await dbProvider.database;
var result = db.insert(todoTABLE, todo.toDatabaseJson());
return result;
}
Future<List<Todo>> getTodos({List<String> columns, String query})
async{
final db = await dbProvider.database;
List<Map<String, dynamic>> result;
if (query != null){
if (query.isNotEmpty)
result = await db.query(todoTABLE, columns: columns, where: 'description LIKE ?', whereArgs: ["%$query%"]);
} else {
result = await db.query(todoTABLE, columns: columns);
}
List<Todo> todos = result.isNotEmpty
? result.map((item)=> Todo.fromDatabaseJson(item)).toList()
: [];
return todos;
}
//Update Todo record
Future<int> updateTodo(Todo todo) async{
final db = await dbProvider.database;
var result = await db.update(todoTABLE, todo.toDatabaseJson(),
where: "id = ?", whereArgs: [todo.id]);
return result;
}
//Delete Todo records
Future<int> deleteTodo(int id) async{
final db = await dbProvider.database;
var result = await db.delete(todoTABLE, where: 'id = ?', whereArgs: [id]);
return result;
}
}
Database Page database.dart
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
final todoTABLE = 'Todo';
class DatabaseProvider {
static final DatabaseProvider dbProvider = DatabaseProvider();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await createDatabase();
return _database;
}
createDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "ReactiveTodo");
var database = await openDatabase(path, version: 1, onCreate: initDB, onUpgrade: onUpgrade);
return database;
}
void onUpgrade(Database database, int oldVersion, int newVersion){
if (newVersion > oldVersion){}
}
void initDB(Database database, int version) async{
await database.execute("CREATE TABLE $todoTABLE ("
"id INTEGER PRIMARY KEY, "
"description TEXT, "
"is_done INTEGER "
")");
}
}
Model page todo.dart
class Todo{
int id;
String description;
bool isDone = false;
Todo({this.id, this.description, this.isDone = false});
factory Todo.fromDatabaseJson(Map<String, dynamic> data) => Todo(
id: data['data'],
description: data['description'],
isDone: data['is_done'] == 0 ? false : true,
);
Map<String, dynamic> toDatabaseJson() => {
"id": this.id,
"description": this.description,
"is_done": this.isDone == false ? 0 : 1,
};
}
View page home_Page2.dart
import 'package:flutter/services.dart';
import 'package:zencartos/bloc/todo_bloc.dart';
import 'package:zencartos/models/todo.dart';
class HomePage2 extends StatelessWidget {
HomePage2({Key key, this.title}) : super(key: key);
final TodoBloc todoBloc = TodoBloc();
final String title;
//Allows Todo card to be dismissable horizontally
final DismissDirection _dismissDirection = DismissDirection.horizontal;
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.white,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.dark));
return Scaffold(
resizeToAvoidBottomPadding: false,
body: SafeArea(
child: Container(
color: Colors.white,
padding:
const EdgeInsets.only(left: 2.0, right: 2.0, bottom: 2.0),
child: Container(
//This is where the magic starts
child: getTodosWidget()))),
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: 0.3),
)),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(
Icons.menu,
color: Colors.indigoAccent,
size: 28,
),
onPressed: () {
//just re-pull UI for testing purposes
todoBloc.getTodos();
}),
Expanded(
child: Text(
"Todo",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontFamily: 'RobotoMono',
fontStyle: FontStyle.normal,
fontSize: 19),
),
),
Wrap(children: <Widget>[
IconButton(
icon: Icon(
Icons.search,
size: 28,
color: Colors.indigoAccent,
),
onPressed: () {
_showTodoSearchSheet(context);
},
),
Padding(
padding: EdgeInsets.only(right: 5),
)
])
],
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: Padding(
padding: EdgeInsets.only(bottom: 25),
child: FloatingActionButton(
elevation: 5.0,
onPressed: () {
_showAddTodoSheet(context);
},
backgroundColor: Colors.white,
child: Icon(
Icons.add,
size: 32,
color: Colors.indigoAccent,
),
),
));
}
void _showAddTodoSheet(BuildContext context) {
final _todoDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 21, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'I have to...',
labelText: 'New Todo',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500)),
validator: (String value) {
if (value.isEmpty) {
return 'Empty description!';
}
return value.contains('')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.save,
size: 22,
color: Colors.white,
),
onPressed: () {
final newTodo = Todo(
description:
_todoDescriptionFormController
.value.text);
if (newTodo.description.isNotEmpty) {
/*Create new Todo object and make sure
the Todo description is not empty,
because what's the point of saving empty
Todo
*/
todoBloc.addTodo(newTodo);
//dismisses the bottomsheet
Navigator.pop(context);
}
},
),
),
)
],
),
],
),
),
),
),
);
});
}
void _showTodoSearchSheet(BuildContext context) {
final _todoSearchDescriptionFormController = TextEditingController();
showModalBottomSheet(
context: context,
builder: (builder) {
return new Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: new Container(
color: Colors.transparent,
child: new Container(
height: 230,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Padding(
padding: EdgeInsets.only(
left: 15, top: 25.0, right: 15, bottom: 30),
child: ListView(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextFormField(
controller: _todoSearchDescriptionFormController,
textInputAction: TextInputAction.newline,
maxLines: 4,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w400),
autofocus: true,
decoration: const InputDecoration(
hintText: 'Search for todo...',
labelText: 'Search *',
labelStyle: TextStyle(
color: Colors.indigoAccent,
fontWeight: FontWeight.w500),
),
validator: (String value) {
return value.contains('#')
? 'Do not use the # char.'
: null;
},
),
),
Padding(
padding: EdgeInsets.only(left: 5, top: 15),
child: CircleAvatar(
backgroundColor: Colors.indigoAccent,
radius: 18,
child: IconButton(
icon: Icon(
Icons.search,
size: 22,
color: Colors.white,
),
onPressed: () {
/*This will get all todos
that contains similar string
in the textform
*/
todoBloc.getTodos(
query:
_todoSearchDescriptionFormController
.value.text);
//dismisses the bottomsheet
Navigator.pop(context);
},
),
),
)
],
),
],
),
),
),
),
);
});
}
Widget getTodosWidget() {
return StreamBuilder(
stream: todoBloc.todos,
builder: (BuildContext context, AsyncSnapshot<List<Todo>> snapshot) {
return getTodoCardWidget(snapshot);
},
);
}
Widget getTodoCardWidget(AsyncSnapshot<List<Todo>> snapshot) {
if (snapshot.hasData) {
return snapshot.data.length != 0
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, itemPosition) {
Todo todo = snapshot.data[itemPosition];
final Widget dismissibleCard = new Dismissible(
background: Container(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Deleting",
style: TextStyle(color: Colors.white),
),
),
),
color: Colors.redAccent,
),
onDismissed: (direction) {
todoBloc.deleteTodoById(todo.id);
},
direction: _dismissDirection,
key: new ObjectKey(todo),
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey[200], width: 0.5),
borderRadius: BorderRadius.circular(5),
),
color: Colors.white,
child: ListTile(
leading: InkWell(
onTap: () {
//Reverse the value
todo.isDone = !todo.isDone;
todoBloc.updateTodo(todo);
},
child: Container(
//decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: todo.isDone
? Icon(
Icons.done,
size: 26.0,
color: Colors.indigoAccent,
)
: Icon(
Icons.check_box_outline_blank,
size: 26.0,
color: Colors.tealAccent,
),
),
),
),
title: Text(
todo.description,
style: TextStyle(
fontSize: 16.5,
fontFamily: 'RobotoMono',
fontWeight: FontWeight.w500,
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none),
),
)),
);
return dismissibleCard;
},
)
: Container(
child: Center(
//this is used whenever there 0 Todo
//in the data base
child: noTodoMessageWidget(),
));
} else {
return Center(
child: loadingData(),
);
}
}
Widget loadingData() {
todoBloc.getTodos();
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Text("Loading...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500))
],
),
),
);
}
Widget noTodoMessageWidget() {
return Container(
child: Text(
"Start adding Todo...",
style: TextStyle(fontSize: 19, fontWeight: FontWeight.w500),
),
);
}
dispose() {
todoBloc.dispose();
}
}
repository page todo_repository.dart
import 'package:zencartos/models/todo.dart';
class TodoRepository {
final todoDao = TodoDao();
Future getAllTodos({String query}) => todoDao.getTodos(query: query);
Future insertTodo(Todo todo) => todoDao.createTodo(todo);
Future updateTodo(Todo todo) => todoDao.updateTodo(todo);
Future deleteTodoById(int id) => todoDao.deleteTodo(id);
//We are not going to use this in the demo
//Future deleteAllTodos() => todoDao.deleteAllTodos();
}

In your todo_bloc.dart, your delete method should be:
deleteTodoById(int id) async {
await _todoRepository.deleteTodoById(id);
getTodos();
}

Related

How to check that the firebase RTDB date equal to the date i have given through the date picker

This is the Database Here is the code that I have taken the count of Orders that which the "IsDelivered" value is true and false separately.
**dasboard.dart.**
import 'package:easyway/future.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:intl/intl.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
class Dashboard extends StatefulWidget {
static DateTime _date;
Dashboard({this.app});
final FirebaseApp app;
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
final _referenceData = FirebaseDatabase.instance;
final databaseRef = FirebaseDatabase.instance.reference()
.child('orders')
.orderByChild('isDelivered');
Query dataRef;
Map myList = {};
String name;
#override
void initState() {
dataRef =
FirebaseDatabase.instance.reference().child('orders').orderByChild(
'deliveryDate');
super.initState();
}
#override
Widget build(BuildContext context) {
print(dataRef);
// Widget buildorder({Map orders,int count}){
// return Center(child: Text(orders['isDelivered'].toString(),style: TextStyle(fontSize: 30,fontWeight: FontWeight.bold),));
// }
// final ref = _referenceData.reference();
var _height = MediaQuery
.of(context)
.size
.height;
var _width = MediaQuery
.of(context)
.size
.width;
return Scaffold(
appBar: AppBar(
title: Text("DashBoard", style: TextStyle(
color: Colors.black
),),
backgroundColor: Colors.white,
),
body: ListView(
children: [
Padding(
padding: const EdgeInsets.all(29.0),
child: GestureDetector(
onTap: () {
showDatePicker(context: context,
initialDate: Dashboard._date == null
? DateTime.now()
: Dashboard._date,
firstDate: DateTime(2018),
lastDate: DateTime(2022)
).then((value) {
setState(() {
Dashboard._date = value;
});
});
},
child: Container(
height: _height * .15,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.blue,
boxShadow: [
BoxShadow(
color: Colors.black38,
blurRadius: 3,
spreadRadius: 3,
offset: Offset(4.0, 4.0),
)
]
),
padding: EdgeInsets.all(10),
child: FittedBox(
child: Text(
Dashboard._date == null ? DateFormat.yMMMMEEEEd().format(
DateTime.now()).toString() : DateFormat.yMMMMEEEEd()
.format(Dashboard._date)
.toString(),
style: TextStyle(fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
),
),
Padding(
padding: const EdgeInsets.only(left: 29, right: 14),
child: Text("Production Efficiency & Order Taken", style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 23, fontFamily: 'Lato'
),),
),
Padding(
padding: const EdgeInsets.all(25.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: _height * .3,
width: _width * .4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.yellow,
boxShadow: [
BoxShadow(
color: Colors.black38,
blurRadius: 2,
spreadRadius: 2,
offset: Offset(4.0, 4.0),
)
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Total Hours", style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 26,
color: Colors.lightBlue,
fontFamily: 'Lato'),
),
SizedBox(height: 20,),
Text("25", style: TextStyle(
fontSize: 30, fontWeight: FontWeight.bold),),
Divider(
color: Colors.white,
indent: _height / 13,
endIndent: _width / 8,
thickness: 4,
),
Text("30", style: TextStyle(
fontSize: 30, fontWeight: FontWeight.bold),)
],
),
),
SizedBox(width: 25,),
Container(
height: _height * .3,
width: _width * .4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.yellow,
boxShadow: [
BoxShadow(
color: Colors.black38,
blurRadius: 2,
spreadRadius: 2,
offset: Offset(4.0, 4.0),
)
]
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 29, right: 29, top: 20, bottom: 29),
child: Dashboard._date == null ?SampleClass(
height: _height*.35,
length: 'Delivered :',
length1: "Postponed :",
source: databaseRef.equalTo(true).once(),
source1: databaseRef.equalTo(false).once(),
):Container()
)
],
),
);
}
}
**future.dart.**
import 'package:easyway/dashboard.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
class SampleClass extends StatefulWidget {
final double height;
final String length;
final Future<DataSnapshot> source;
final String length1;
final Future<DataSnapshot> source1;
const SampleClass({Key key,this.height,this.length,this.source,this.length1,this.source1}):super(key:key);
#override
_SampleClassState createState() => _SampleClassState();
}
class _SampleClassState extends State<SampleClass> {
Dashboard date = Dashboard();
final databaseRef = FirebaseDatabase.instance.reference()
.child('orders')
.orderByChild('isDelivered');
final datarF = FirebaseDatabase.instance.reference().child('orders').orderByChild('deliveryDate');
final Future<FirebaseApp> _future = Firebase.initializeApp();
static List<Map<dynamic, dynamic>> lists = [];
static Widget future({String length,Future<DataSnapshot> source}){
return FutureBuilder(
future: source,
builder: (context,AsyncSnapshot<DataSnapshot> snapshot){
if(snapshot.hasData){
lists.clear();
Map<dynamic,dynamic> values = snapshot.data.value;
values.forEach((key, value) {
lists.add(values);
});
return Container(
child: Text(length+lists.length.toString(),style: TextStyle(fontSize: 35,fontWeight: FontWeight.bold),),
);
}else{
return CircularProgressIndicator();
}
},
); }
#override
Widget build(BuildContext context) {
return Container(
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.blue,
boxShadow: [
BoxShadow(
color: Colors.black38,
blurRadius: 3,
spreadRadius: 3,
offset: Offset(4.0, 4.0),
)
]
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
future(
length: widget.length,
source: widget.source,
),
future(
length: widget.length1,
source: widget.source1,
),
FutureBuilder(
future: datarF.equalTo("17/03/2021").once(),
builder: (context,AsyncSnapshot<DataSnapshot> snapshot){
if(snapshot.hasData){
lists.clear();
Map<dynamic,dynamic> values = snapshot.data.value;
values.forEach((key, value) {
if(values!=null){
lists.add(values);
}else{
lists.length = 0;
}
});
return Text(values['deliveryDate'].toString(),style: TextStyle(fontSize: 35,fontWeight: FontWeight.bold),);
}else{
return CircularProgressIndicator();
}
},
)
],
)
);
}
}
How to check that the firebase RTDB date equal to the date I have given through the date picker and I want to take the count of orders "isDElivered" value that was equal to the date chosen through the date picker.
How do I get the solution please help me to solve this?
Realtime database doesn't supports multiple equalTo query. Therefore necessary to filter list by second parameter by itself. Try to get list of delivered orders and filter it by where function. Something like this:
Widget _buildDeliveredOrdersWidget(DateTime date) {
final ref = FirebaseDatabase.instance
.reference()
.child('orders')
.orderByChild('isDelivered')
.equalTo(true)
.once()
.then((value) => filterByDate(value.value, date));
return FutureBuilder(
future: ref,
builder: (context, AsyncSnapshot<List<Map<dynamic, dynamic>>> snapshot) {
if (snapshot.hasData) {
return Text(
"Found ${snapshot.data.length} delivered orders for $date",
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
);
} else {
return CircularProgressIndicator();
}
},
);
}
List<Map<dynamic, dynamic>> filterByDate(
List<Map<dynamic, dynamic>> orders, DateTime date) =>
orders.where((element) => element['deliveryDate'] == date).toList();

Error "The method '[]' was called on null. Receiver: null Tried calling: []("0tm2JqPY0oNq5vSM74BqOufhGao1")"

I am getting this error in the poll post Page after added the voting feature to it:
The new method I added is handleTotalVotePosts to handle the count of userPostPolls(in line number 178).
I think the error is getting the userId or something which i am not Sure .
I am not sure what is actually wrong here
The method '[]' was called on null.
Receiver: null
Tried calling: []("0tm2JqPY0oNq5vSM74BqOufhGao1")
But code seems to be correct
This my **pollPost.dart**
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:justpoll/libmodels/user_model.dart';
import 'package:justpoll/screens/chat/chat_data.dart';
import 'package:justpoll/widgets/progress.dart';
import 'package:justpoll/widgets/toast_text.dart';
import '../Constants.dart';
import 'package:justpoll/widgets/custom_image.dart';
import 'package:animated_button/animated_button.dart';
class PollPost extends StatefulWidget {
final String pollPostId;
final String mediaUrl;
final String ownerId;
final String username;
final String caption;
final String category;
final String colorTheme;
final Timestamp duration;
final String question;
final dynamic votesTotal;
final String optText1;
final String optText2;
final String optText3;
final String optText4;
final String optemo1;
final String optemo2;
final String optemo3;
final String optemo4;
final String optCount1;
final String optCount2;
final String optCount3;
final String optCount4;
PollPost({
this.pollPostId,
this.mediaUrl,
this.ownerId,
this.username,
this.caption,
this.category,
this.colorTheme,
this.duration,
this.question,
this.votesTotal,
this.optText1,
this.optText2,
this.optText3,
this.optText4,
this.optemo1,
this.optemo2,
this.optemo3,
this.optemo4,
this.optCount1,
this.optCount2,
this.optCount3,
this.optCount4,
});
factory PollPost.fromDocument(DocumentSnapshot doc) {
return PollPost(
pollPostId: doc['pollPostid'],
mediaUrl: doc['mediaUrl'],
ownerId: doc['ownerId'],
username: doc['username'],
caption: doc['caption'],
category: doc['category'],
colorTheme: doc['colorTheme'],
duration: doc['Duration'],
question: doc['Question'],
votesTotal: doc['votesTotal'],
optText1: doc["options"]["1"]["optionText1"],
optText2: doc["options"]["2"]["optionText2"],
optText3: doc["options"]["3"]["optionText3"],
optText4: doc["options"]["4"]["optionText4"],
optemo1: doc["options"]["1"]["optionEmoji1"],
optemo2: doc["options"]["2"]["optionEmoji2"],
optemo3: doc["options"]["3"]["optionEmoji3"],
optemo4: doc["options"]["4"]["optionEmoji4"],
// optCount1: doc["options"]["1"]["optionCount1"],
// optCount2: doc["options"]["2"]["optionCount2"],
// optCount3: doc["options"]["3"]["optionCount3"],
// optCount4: doc["options"]["4"]["optionCount4"],
);
}
int getreactionsTotalCount(votesTotal) {
if (votesTotal == null) {
return 0;
}
int count = 0;
votesTotal.values.forEach((val) {
if (val = true) {
count += 1;
}
});
return count;
}
#override
_PollPostState createState() => _PollPostState(
pollPostId: this.pollPostId,
mediaUrl: this.mediaUrl,
ownerId: this.ownerId,
username: this.username,
caption: this.caption,
category: this.category,
colorTheme: this.colorTheme,
duration: this.duration,
question: this.question,
optText1: this.optText1,
optText2: this.optText2,
optText3: this.optText3,
optText4: this.optText4,
optemo1: this.optemo1,
optemo2: this.optemo2,
optemo3: this.optemo3,
optemo4: this.optemo4,
votesTotalCount: getreactionsTotalCount(this.votesTotal),
);
}
class _PollPostState extends State<PollPost> {
GlobalKey floatingKey = LabeledGlobalKey("Floating");
bool isFloatingOpen = false;
OverlayEntry floating;
static FirebaseAuth auth = FirebaseAuth.instance;
final userRef = FirebaseFirestore.instance.collection("users");
final pollPostsRef = FirebaseFirestore.instance.collection("pollPosts");
final String currentUserId = auth.currentUser?.uid;
final String pollPostId;
final String mediaUrl;
final String ownerId;
final String username;
final String caption;
final String category;
final String colorTheme;
final Timestamp duration;
final String question;
final String optText1;
final String optText2;
final String optText3;
final String optText4;
final String optemo1;
final String optemo2;
final String optemo3;
final String optemo4;
int votesTotalCount;
Map votesTotal;
bool isVoted;
_PollPostState({
this.pollPostId,
this.mediaUrl,
this.ownerId,
this.username,
this.caption,
this.category,
this.colorTheme,
this.duration,
this.question,
this.votesTotal,
this.votesTotalCount,
this.optText1,
this.optText2,
this.optText3,
this.optText4,
this.optemo1,
this.optemo2,
this.optemo3,
this.optemo4,
});
handleTotalVotePosts() {
bool _isVoted = votesTotal[currentUserId] == true;
if (_isVoted) {
pollPostsRef
.doc(ownerId)
.collection('usersPollPosts')
.doc(pollPostId)
.update({'votesTotal.$currentUserId': false});
setState(() {
votesTotalCount -= 1;
isVoted = false;
votesTotal[currentUserId] = false;
});
} else if (!_isVoted) {
pollPostsRef
.doc(ownerId)
.collection('usersPollPosts')
.doc(pollPostId)
.update({'votesTotal.$currentUserId': true});
setState(() {
votesTotalCount += 1;
isVoted = true;
votesTotal[currentUserId] = true;
});
}
}
OverlayEntry createFloating() {
RenderBox renderBox = floatingKey.currentContext.findRenderObject();
Offset offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(builder: (context) {
return Positioned(
left: offset.dx,
top: offset.dy - 70,
child: Material(
color: Colors.transparent,
elevation: 20,
child: ClipRRect(
borderRadius: BorderRadius.circular(40.0),
child: Container(
padding: EdgeInsets.all(5.0),
height: 50,
color: MyColors.black,
child: Row(
children: [
Padding(
padding: const EdgeInsets.all(5.0),
child: AnimatedButton(
onPressed: () {
handleTotalVotePosts();
toastMessage("You voted for $optText1 $optemo1");
setState(() {
if (isFloatingOpen)
floating.remove();
else {
floating = createFloating();
Overlay.of(context).insert(floating);
}
isFloatingOpen = !isFloatingOpen;
});
},
duration: 20,
enabled: true,
width: 30,
height: 30,
color: MyColors.black,
shadowDegree: ShadowDegree.light,
child:
Text(optemo1, style: TextStyle(fontSize: 20.0)))),
Padding(
padding: const EdgeInsets.all(5.0),
child: AnimatedButton(
onPressed: () {
toastMessage("You voted for $optText2 $optemo2");
setState(() {
handleTotalVotePosts();
if (isFloatingOpen)
floating.remove();
else {
floating = createFloating();
Overlay.of(context).insert(floating);
}
isFloatingOpen = !isFloatingOpen;
});
},
duration: 20,
enabled: true,
width: 30,
height: 30,
color: MyColors.black,
shadowDegree: ShadowDegree.light,
child:
Text(optemo2, style: TextStyle(fontSize: 20.0)))),
optText3.isEmpty
? Text("")
: Padding(
padding: const EdgeInsets.all(5.0),
child: AnimatedButton(
onPressed: () {
handleTotalVotePosts();
toastMessage(
"You voted for $optText3 $optemo3");
setState(() {
if (isFloatingOpen)
floating.remove();
else {
floating = createFloating();
Overlay.of(context).insert(floating);
}
isFloatingOpen = !isFloatingOpen;
});
},
duration: 20,
enabled: true,
width: 30,
height: 30,
color: MyColors.black,
shadowDegree: ShadowDegree.light,
child: Text(optemo3,
style: TextStyle(fontSize: 20.0)))),
optText4.isEmpty
? Text("")
: Padding(
padding: const EdgeInsets.all(5.0),
child: AnimatedButton(
onPressed: () {
handleTotalVotePosts();
toastMessage(
"You voted for $optText4 $optemo4");
setState(() {
if (isFloatingOpen)
floating.remove();
else {
floating = createFloating();
Overlay.of(context).insert(floating);
}
isFloatingOpen = !isFloatingOpen;
});
},
duration: 20,
enabled: true,
width: 30,
height: 30,
color: MyColors.black,
shadowDegree: ShadowDegree.light,
child: Text(optemo4,
style: TextStyle(fontSize: 20.0)))),
],
),
),
),
),
);
});
}
buildPollPostHeader() {
return FutureBuilder(
future: userRef.doc(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
UserModel user = UserModel.fromMap(snapshot.data.data());
return ListTile(
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(user.photoUrl),
backgroundColor: Colors.grey,
),
title: GestureDetector(
onTap: () => toastMessage("showing profile"),
child: Text(
user.username,
style: TextStyle(
color: MyColors.black,
fontWeight: FontWeight.bold,
),
),
),
subtitle: Text(category),
trailing: IconButton(
onPressed: () => toastMessage("pop up menu"),
icon: Icon(Icons.more_vert),
),
);
},
);
}
buildPollPostCenter() {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
question,
style: TextType.boldHeading,
),
),
mediaUrl == null
? Center(
child: Container(
height: 30.0,
))
: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: chachedNetworkImage(
mediaUrl,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Column(
children: [
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
optText1,
style: TextStyle(
color: MyColors.offBlack,
fontSize: 16.0,
),
),
Text(optemo1),
SizedBox(
width: 25.0,
),
Text(
optText2,
style: TextStyle(
color: MyColors.offBlack,
fontSize: 16.0,
),
),
Text(optemo2),
],
),
],
),
SizedBox(height: 13.0),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
optText3,
style: TextStyle(
color: MyColors.offBlack,
fontSize: 16.0,
),
),
Text(optemo3),
SizedBox(
width: 25.0,
),
Text(
optText4,
style: TextStyle(
color: MyColors.offBlack,
fontSize: 16.0,
),
),
Text(optemo4),
],
),
],
),
],
),
),
],
);
}
buildPollPostFooter() {
return Column(
children: [
Align(
alignment: Alignment.bottomLeft,
child: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(padding: EdgeInsets.only(top: 40.0, left: 20.0)),
isVoted
? GestureDetector(
key: floatingKey,
onTap: () {
setState(() {
if (isFloatingOpen)
floating.remove();
else {
floating = createFloating();
Overlay.of(context).insert(floating);
}
isFloatingOpen = !isFloatingOpen;
});
},
child: Icon(
Icons.poll,
color: MyColors.offBlack,
size: 28.0,
),
)
: Icon(Icons.check_circle),
Padding(padding: EdgeInsets.only(right: 20.0)),
GestureDetector(
onTap: () => toastMessage("show comments"),
child: Icon(
Icons.chat,
color: MyColors.offBlack,
size: 28.0,
),
),
Padding(padding: EdgeInsets.only(right: 20.0)),
GestureDetector(
onTap: () => toastMessage("saved successfully"),
child: Icon(
Icons.bookmark,
color: MyColors.offBlack,
size: 28.0,
),
),
Padding(padding: EdgeInsets.only(right: 20.0)),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => toastMessage("sharing with friends"),
child: Align(
alignment: Alignment.topRight,
child: Icon(
Icons.send,
color: MyColors.offBlack,
size: 28.0,
),
),
),
Padding(padding: EdgeInsets.only(right: 25.0)),
],
),
),
],
),
),
),
Row(
children: [
Container(
margin: EdgeInsets.only(left: 20.0, top: 5.0),
child: Text(
"$votesTotalCount votes",
style: TextStyle(
color: MyColors.black,
fontWeight: FontWeight.bold,
),
),
),
Container(
margin: EdgeInsets.only(left: 20.0, top: 5.0),
child: Text(
"expiring in 4 days",
style: TextStyle(
color: MyColors.black,
fontWeight: FontWeight.bold,
),
),
)
],
),
Padding(
padding: const EdgeInsets.only(top: 5.0, bottom: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$username ",
style: TextStyle(
color: MyColors.black,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: Text(caption),
),
],
),
)
],
);
}
#override
Widget build(BuildContext context) {
isVoted = (votesTotal[currentUserId] == true);
return Card(
elevation: 1.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
buildPollPostHeader(),
buildPollPostCenter(),
buildPollPostFooter(),
],
),
);
}
}
Please help me to get out of this bug.
Thank you :)
Please write the below code inside your handleTotalVotePosts method:
if(votesTotal == null){
return;
}

NoSuchMethodError: 'dart.global.firebase.auth' in the Flutter Chat App

I was creating this Chat App and after implementing the search function I encountered this NoSuchMethodError: tried to call a non-function, such as null: 'dart.global.firebase.auth' problem.
So basically now when I sign up the email and username doesn't get uploaded to firebase auth and Cloud Firestore, which earlier used to happen I think I did some mistake in the search.dart file which seems weird to me.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intro/helper/constants.dart';
import 'package:intro/services/database.dart';
import 'package:intro/widgets/widgets.dart';
import 'conversation_screen.dart';
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController searchTextEditingController =
new TextEditingController();
QuerySnapshot searchSnapshot;
Widget searchList() {
return searchSnapshot != null
? ListView.builder(
itemCount: searchSnapshot.docs.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return searchTile(
searchSnapshot.docs[index].data()["name"],
searchSnapshot.docs[index].data()["email"],
);
})
: Container();
}
initiateSearch() {
databaseMethods
.getUserByUsername(searchTextEditingController.text)
.then((val) {
setState(() {
searchSnapshot = val;
});
});
}
createChatroomAndStartConversation({String userName}) {
String chatRoomId = getChatRoomId(userName, Constants.myName);
List<String> users = [userName, Constants.myName];
Map<String, dynamic> chatRoomMap = {
"users": users,
"chatroomid": chatRoomId
};
DatabaseMethods().createChatRoom(chatRoomId, chatRoomMap);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen(),
));
}
Widget searchTile(String userName, String userEmail) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userName, style: mediumTextStyle()),
Text(userEmail, style: mediumTextStyle())
],
),
Spacer(),
GestureDetector(
onTap: () {
createChatroomAndStartConversation(userName: userName);
},
child: Container(
decoration: BoxDecoration(
color: Colors.blue, borderRadius: BorderRadius.circular(30)),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Text(
"Message",
style: mediumTextStyle(),
),
),
)
],
),
);
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context),
body: Container(
child: Column(
children: [
Container(
color: Color(0x54FFFFFF),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(children: [
Expanded(
child: TextField(
controller: searchTextEditingController,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
hintText: "Search Username",
hintStyle: TextStyle(
color: Colors.white54,
),
border: InputBorder.none),
),
),
GestureDetector(
onTap: () {
initiateSearch();
},
child: Container(
height: 35,
width: 35,
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
const Color(0x36FFFFFF),
const Color(0x0FFFFFFF)
]),
borderRadius: BorderRadius.circular(30)),
padding: EdgeInsets.all(1),
child: Icon(
Icons.search,
color: Colors.white,
)),
)
]),
),
searchList()
],
),
),
);
}
}
getChatRoomId(String a, String b) {
if (a.substring(0, 1).codeUnitAt(0) > b.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}
}
and just in case there is an error with the signUp.dart file here it is.
import 'package:flutter/material.dart';
import 'package:intro/helper/helperfunctions.dart';
import 'package:intro/services/auth.dart';
import 'package:intro/services/database.dart';
import 'package:intro/widgets/widgets.dart';
import 'chatRoomsScreen.dart';
class SignUp extends StatefulWidget {
final Function toggle;
SignUp(this.toggle);
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
bool isLoading = false;
AuthMethods authMethods = new AuthMethods();
DatabaseMethods databaseMethods = new DatabaseMethods();
final formKey = GlobalKey<FormState>();
TextEditingController userNameTextEditingController =
new TextEditingController();
TextEditingController emailTextEditingController =
new TextEditingController();
TextEditingController passwordTextEditingController =
new TextEditingController();
signMeUP() {
if (formKey.currentState.validate()) {
Map<String, String> userInfoMap = {
"name": userNameTextEditingController.text,
"email": emailTextEditingController.text,
};
HelperFunctions.saveUserEmailSharedPreference(
emailTextEditingController.text);
HelperFunctions.saveUserNameSharedPreference(
userNameTextEditingController.text);
setState(() {
isLoading = true;
});
authMethods
.signUpwithemailandpassword(emailTextEditingController.text,
passwordTextEditingController.text)
.then((value) {
// print("$value.uid");
databaseMethods.uploadUserInfo(userInfoMap);
HelperFunctions.saveUserLoggedInSharedPreference(true);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => ChatRoom()));
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context),
body: isLoading
? Container(
child: Center(child: CircularProgressIndicator()),
)
: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height - 60,
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Form(
key: formKey,
child: Column(
children: [
TextFormField(
validator: (val) {
return val.isEmpty || val.length < 2
? "Invalid Username (Needs to be more than 2 characters)"
: null;
},
controller: userNameTextEditingController,
style: simpleTextStyle(),
decoration: textfieldInputDecoration("username"),
),
TextFormField(
validator: (val) {
return RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(val)
? null
: "Enter correct email";
},
controller: emailTextEditingController,
style: simpleTextStyle(),
decoration: textfieldInputDecoration("email"),
),
TextFormField(
obscureText: true,
validator: (val) {
return val.length < 6
? "Please provide with 6+ character"
: null;
},
controller: passwordTextEditingController,
style: simpleTextStyle(),
decoration: textfieldInputDecoration("password"),
),
],
),
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.centerRight,
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
"Forgot Password?",
style: simpleTextStyle(),
),
),
),
SizedBox(
height: 10,
),
GestureDetector(
onTap: () {
signMeUP();
},
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
const Color(0xff007EF4),
const Color(0xff2A75BC)
]),
borderRadius: BorderRadius.circular(30)),
child: Text("Sign Up", style: mediumTextStyle()),
),
),
SizedBox(
height: 10,
),
Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30)),
child: Text("Sign Up with Google",
style:
TextStyle(color: Colors.black, fontSize: 18)),
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Already have an account? ",
style: mediumTextStyle(),
),
GestureDetector(
onTap: () {
widget.toggle();
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Text(
"Sign in now",
style: TextStyle(
color: Colors.white,
fontSize: 17,
decoration: TextDecoration.underline),
),
),
),
],
),
SizedBox(
height: 50,
)
],
),
),
),
),
);
}
}
And if there is any file anyone of you want: GitHub: Chat App
Because you Haven't initialize the method

Download data from firebase Firestore to flutter

I am using Firestore as a database for a Chat in one part of the application that I am building. I have a collection on my Firestore that looks like this:
messages(collection)
userId
userId-otherUserId(sub-collection)
randomId
content : String,
timestamp : Date
...
and I would like to retrieve firstly all of the userIs-otherUserId in a ListView and then to retrieve the lastMessage by retrieving the last randomId(and in that randomId the last message is the last element with key 'content') and I would like it to look something like this.
Prototype
where the Loading represents last message but the name and profilePicture I can get from my API.
The pictures of my Firestore database:
First Image
Second Image
Third Image
Is there any chance that I could save the name and Profile picture on the database in the userId-otherUserId(collection)?
The code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:dio/dio.dart';
import 'package:disby_app/const.dart';
import 'package:disby_app/data/request_model.dart';
import 'package:disby_app/global_widgets/loading_indicator.dart';
import 'package:disby_app/main_screens/main_chat_and_request_screens/chat.dart';
import 'package:disby_app/main_screens/main_chat_and_request_screens/profile_details_screen.dart';
import 'package:disby_app/public/keys.dart';
import 'package:disby_app/services/json_requests.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../global_widgets/chat_and_requests_placeholder.dart';
class MainChatScreen extends StatefulWidget {
#override
_MainChatScreenState createState() => _MainChatScreenState();
}
class _MainChatScreenState extends State<MainChatScreen> {
bool _areThereChats = false;
bool _isLoading = true;
String groupChatId;
String userId = '1';
List<String> listOfLastMessages = [];
List<String> listOfIds = [];
List<ArrayOfRequestsModel> arrayOfRequests = [];
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
Future readLocal() async {
var prefs = await SharedPreferences.getInstance();
userId = prefs.getString(kUserId) ?? '';
print(userId);
setState(() {});
}
Future<void> _getRequests() async {
final prefs = await SharedPreferences.getInstance();
final userId = prefs.getString(kUserId);
try {
await Dio().post(baseUrl + acceptedUsers + "/" + userId).then((snapshot) {
print(snapshot);
final Map<String, dynamic> response = snapshot.data;
print(response);
if (response['response'] == 'success') {
List<dynamic> arrayFromServer = response['acceptedUsers'];
if (arrayFromServer.isNotEmpty) {
arrayFromServer.forEach((userData) {
arrayOfRequests.add(ArrayOfRequestsModel.fromJson(userData));
listOfIds.add(userData['userId']);
print(userData['userId']);
});
_areThereChats = true;
_isLoading = false;
getMessages(userId);
} else {
_areThereChats = false;
_isLoading = false;
}
setState(() {});
} else {
_areThereChats = false;
_isLoading = false;
setState(() {});
}
});
} catch (e) {
print(e);
}
}
getMessages(String userId) {
int i = 0;
print(listOfIds.length);
print(listOfIds);
listOfIds.forEach((element) {
Firestore.instance
.collection('messages')
.document('$userId')
.collection('$userId-$element')
.orderBy('timestamp', descending: true)
.limit(3)
.getDocuments()
.then((QuerySnapshot snapshot) {
print(snapshot.documents[0].data['content']);
snapshot.documents.forEach((f) {
print(f.data['content']);
listOfLastMessages.add(f.data['content']);
setState(() {});
i++;
});
if (i == listOfIds.length) {
setState(() {});
print(listOfLastMessages);
}
});
});
}
#override
void initState() {
readLocal();
_getRequests();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container(
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
// child: (userId != '1')
// ? StreamBuilder(
// stream: Firestore.instance
// .collection('messages')
// .document('5ef4d83175b07cf074525c08')
// .snapshots(),
// builder: (context, snapshot) {
// // if (snapshot.hasData) {
// if (!snapshot.hasData) {
// return Center(
// child: CircularProgressIndicator(
// valueColor: AlwaysStoppedAnimation<Color>(themeColor),
// ),
// );
// } else {
// return Container(
// width: 100,
// height: 200,
// child: ListView.builder(
// padding: EdgeInsets.all(10.0),
// itemBuilder: (context, index) =>
// buildItem(context, snapshot.data.documents),
// itemCount: snapshot.data.documents.length,
// ),
// );
// }
// },
// )
// : LoadingIndicator(
// loading: _isLoading,
// ),
// ),
child: Column(
children: <Widget>[
(_isLoading == false)
? (_areThereChats == true)
? Container(
child: AnimatedList(
key: _listKey,
shrinkWrap: true,
initialItemCount: arrayOfRequests.length,
itemBuilder:
(BuildContext context, index, animation) {
return _buildItem(context, arrayOfRequests[index],
animation, index);
},
),
)
: ChatAndRequestPlaceholder(
text: translate('when_someone_sends_you_a_request'))
: LoadingIndicator(
loading: _isLoading,
),
],
),
),
),
);
}
Widget buildItem(BuildContext context, DocumentSnapshot document) {
if (document['id'] == userId) {
return Container();
} else {
return Container(
child: FlatButton(
child: Row(
children: <Widget>[
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'Nickname: ${document['content']}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
Container(
child: Text(
'About me: ${document['content'] ?? 'Not available'}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
)
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
peerId: document.documentID,
peerAvatar: document['photoUrl'],
),
),
);
},
color: greyColor2,
padding: EdgeInsets.fromLTRB(25.0, 10.0, 25.0, 10.0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
}
_buildItem(BuildContext context, ArrayOfRequestsModel arrayOfSentRequests,
Animation animation, int index) {
return GestureDetector(
onTap: () {
print(arrayOfSentRequests.profilePictureUrl);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
peerId: arrayOfSentRequests.userId,
peerAvatar: arrayOfSentRequests.profilePictureUrl,
),
),
);
},
child: Container(
height: 82,
child: ScaleTransition(
scale: animation,
child: Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Color.fromRGBO(0, 0, 0, 0.07),
offset: Offset(0, 5),
blurRadius: 11,
),
],
),
width: double.infinity,
height: 78,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
height: double.infinity,
child: GestureDetector(
onTap: () {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (context) => ProfileDetailsScreen(),
),
);
},
child: Row(
children: <Widget>[
SizedBox(width: 24),
ClipRRect(
borderRadius: BorderRadius.circular(25),
child: Image(
image: NetworkImage(
arrayOfSentRequests.profilePictureUrl,
),
width: 48,
),
),
SizedBox(width: 10),
Expanded(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
arrayOfSentRequests.nameAndSurname,
style: TextStyle(
color: Colors.black,
fontFamily: 'Avenir',
fontWeight: FontWeight.w700,
fontSize: 18,
),
),
Text(
'11/11/2020', //arrayOfSentRequests.nameAndSurname
style: TextStyle(
color: Colors.grey,
fontFamily: 'Avenir',
fontWeight: FontWeight.w500,
fontSize: 15,
),
),
],
),
),
Container(
child: Text(
// (arrayOfSentRequests.lastMessage !=
// null)
// ? arrayOfSentRequests.lastMessage
// : 'Loading..',
(listOfLastMessages.isNotEmpty)
? listOfLastMessages[index]
.toString()
: 'Loading...',
maxLines: 2,
softWrap: true,
style: TextStyle(
color: Colors.black54,
fontFamily: 'Avenir',
fontSize: 15,
),
),
),
],
),
),
),
SizedBox(width: 10),
],
),
),
),
),
Container(
child: Center(
child: Icon(
Icons.keyboard_arrow_right,
size: 22,
color: Colors.black54,
),
),
),
SizedBox(width: 10),
],
),
),
),
),
),
);
}
}

How to store geolocation data in Flutter with Cloud Firestore

I have a problem querying my geolocation data from my database in Cloud Firestore. I went through the documentation on Youtube and came to the conclusion that it will work best for me when i save the geolocation data in subcollections.
Here is my databse structure:
And if you go into one of the documents in the subcollection:
The database itself has a collection called "tourguides", with each document containing basic info like the name of the tour and the region where the tour is (both are Strings). Each of the documents then have a subcollection called "locations", where each document has the Strings "Name" and "ID" and also a geopoint with latitude and longitude data.
The documents from the "Tourguides" collection are shown in a ListView. Whenever i tap on one of the entries, a map shall open where all the markers from the respective subcollection are shown.
Here is my ListView Builder:
#override
void initState() {
super.initState();
_pointsofinterest = Firestore.instance.collection('tourguides').document('sydney_downtown_guide').col lection('locations').orderBy('name').snapshots();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: _pointsofinterest,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return InkWell(
child: new ListTile(
title: new Text(document['name']),
subtitle: new Text(document['region']),
onTap: () {
return TourMap(
documents: snapshot.data.documents,
initialPosition: const LatLng(-33.868210, 151.208391),
mapController: _mapController,
);
},
),
);
}).toList(),
);
}
},
),
);
}
I put my map into a StatlessWidget (I am not sure. Maybe this has to be a StatefulWidget?):
class TourMap extends StatelessWidget {
const TourMap({
Key key,
#required this.documents,
#required this.initialPosition,
#required this.mapController,
}) : super(key: key);
final List<DocumentSnapshot> documents;
final LatLng initialPosition;
final Completer<GoogleMapController> mapController;
#override
Widget build(BuildContext context) {
return GoogleMap(
initialCameraPosition: CameraPosition(
target: initialPosition,
zoom: 12,
),
markers: documents
.map((document) => Marker(
markerId: MarkerId(document['placeId']),
icon: BitmapDescriptor.defaultMarker,
position: LatLng(
document['geolocation'].latitude,
document['geolocation'].longitude,
),
infoWindow: InfoWindow(
title: document['location_name'],
),
))
.toSet(),
onMapCreated: (mapController) {
this.mapController.complete(mapController);
},
);
}}
Now i dont exactly know how to set up the query in my OnTap function. The Firestore documentation showed that i always have to refer to specfic documents if i go down the collections from my database.
For example (collection/document/collecion). But in my query, the "document" in the middle of the path is always different, depending on which tourguide the user clicks.
Any ideas on that? Looking Forward to your replies!
UPDATE: I slightly configured my database structure! I now use two seperate databases. One database holds information about the available tourguides (just two Strings currently: Name and Region) and the other stores the actual individual Locations.
I now use where-queries to get the correct locations based on the tourguide´s Name they´re belonging to.
The query itself works on the OnTap function now:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return InkWell(
child: new ListTile(
title: new Text(document['name']),
subtitle: new Text(document['region']),
onTap: () {
Firestore.instance.collection('locations').where(
"toActivity",
isEqualTo: document['name'],
)
.snapshots()
.listen((data) =>
data.documents.forEach((doc) => print(doc["location_name"])));
},
),
);
}).toList(),
);
The database structure:
The correct entries are printed into the console if i tap on one of the entries in the ListView. But i will need a Google Map to pop up that shows the appropriate markers based on the "geolocation" values from the database.
Bro, I did. I can retrieve it by two ways.
1. manually by using simple 'initState'.
2. second by using provider (but by using the second method, i have not succeed to show the marker, yet). Hope can help you, although it was long time ago. Here is mine by using 'initState':
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:visitmalang/provider/aktivitas_provider.dart';
import 'package:visitmalang/ui/home/beranda/aktivitas/profil_agen_wisata.dart';
import 'package:visitmalang/ui/widget/textcustom.dart';
class MapAktivitas extends StatefulWidget {
#override
_MapAktivitasState createState() => _MapAktivitasState();
}
class _MapAktivitasState extends State<MapAktivitas> {
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
GoogleMapController mapController;
bool mapToggle = false;
bool geraiToggle = false;
var currentLocation;
var clients = [];
#override
void initState() {
// TODO: implement initState
super.initState();
Geolocator().getCurrentPosition().then((lokasiSekarang) {
setState(() {
currentLocation = lokasiSekarang;
mapToggle = true;
populateClients();
});
});
}
populateClients() {
clients = [];
Firestore.instance.collection('trail').getDocuments().then((docs) {
if (docs.documents.isNotEmpty) {
setState(() {
geraiToggle = true;
});
for (int i = 0; i < docs.documents.length; i++) {
clients.add(docs.documents[i].data);
initMarker(docs.documents[i].data, docs.documents[i].documentID);
}
}
});
}
void initMarker(request, requestId) {
var markerIdVal = requestId;
final MarkerId markerId = MarkerId(markerIdVal);
final Marker marker = Marker(
markerId: markerId,
position: LatLng(
request['koordinat'].latitude, request['koordinat'].longitude),
infoWindow: InfoWindow(title: request['nama']));
setState(() {
markers[markerId] = marker;
});
}
Widget clientCard(client) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: InkWell(
onTap: () {
zoomInMarker(client);
},
child: Stack(
alignment: FractionalOffset(0.5, 0.94),
children: <Widget>[
Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.white,
),
height: 108,
width: 200,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0)),
child: Container(
width: 96,
height: 108,
child: Image.asset(
'assets/trail.png',
fit: BoxFit.cover,
),
),
),
SizedBox(
width: 4,
),
Stack(
alignment: FractionalOffset(0.5, 0.9),
children: <Widget>[
Container(
width: 100,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0),
child: Container(
alignment: Alignment.center,
child: textCustom(client['nama'],
Colors.black87, 14, 'Montserrat'),
),
),
],
),
),
),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: () => Navigator.of(context).push(
CupertinoPageRoute(
builder: (BuildContext context) =>
ProfilAgenWisata(
nama: client['nama'],
deskripsi: client['deskripsi'],
website: client['website'],
email: client['email'],
noTelepon: client['noTelepon'],
whatsApp: client['whatsApp'],
alamat: client['alamat'],
fasilitas: client['fasilitas'],
))),
child: Container(
alignment: Alignment.center,
color: Color(0xFFDB5C48),
height: 40,
width: 88,
child: textCustom(
'Detail', Colors.white, 14, 'Montserrat'),
),
),
)
],
),
],
),
),
),
],
)),
);
}
zoomInMarker(client) {
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
client['koordinat'].latitude, client['koordinat'].longitude),
zoom: 16.0,
bearing: 19.0,
tilt: 15.0),
),
);
}
#override
Widget build(BuildContext context) {
// AktivitasNotifier aktivitasNotifier = Provider.of<AktivitasNotifier>(context);
return Scaffold(
appBar: AppBar(
title: textCustom('Ngetrail', Colors.black87, 18, 'Montserrat'),
centerTitle: true,
elevation: 0.0,
backgroundColor: Colors.transparent.withOpacity(0.0),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black87),
onPressed: (){Navigator.pop(context);},),
),
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height - 80,
child: mapToggle
? GoogleMap(
myLocationEnabled: true,
myLocationButtonEnabled: true,
markers: Set<Marker>.of(markers.values),
compassEnabled: false,
zoomControlsEnabled: false,
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(currentLocation.latitude,
currentLocation.longitude),
zoom: 15),
onMapCreated: (controller) {
setState(() {
mapController = controller;
});
},
)
: Center(
child: textCustom(
'Loading...', Colors.black87, 20, 'Hind')),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
height: 140.0,
width: MediaQuery.of(context).size.width,
child: geraiToggle
? ListView(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.all(8.0),
children: clients.map((element) {
return clientCard(element);
}).toList(),
)
: Container(),
),
SizedBox(
height: 56.0,
)
],
)
],
));
}
}
Here is the second method, by using provider.
First, you have to decide the model (just focus on 'koordinat'):
class ModelAktivitas {
String idAktivitas;
var koordinat;
String nama;
String deskripsi;
String alamat;
String email;
String noTelepon;
String website;
String gambarUtama;
List galeri;
List fasilitas;
ModelAktivitas.fromMap(Map<String, dynamic> data) {
idAktivitas = data['idAktivitas'];
koordinat = data['koordinat'];
nama = data['nama'];
deskripsi = data['deskripsi'];
noTelepon = data['nomorTelepon'];
galeri = data['galeri'];
fasilitas = data['fasilitas'];
alamat = data['alamat'];
email = data['alamat'];
gambarUtama = data['gambarUtama'];
website = data['website'];
}
}
second, make provider for it:
import 'dart:collection';
import 'package:flutter/cupertino.dart';
import 'package:visitmalang/models/aktivitas_model.dart';
class AktivitasNotifier with ChangeNotifier {
List<ModelAktivitas> _listAktivitas = [];
ModelAktivitas _detailAktivitas;
UnmodifiableListView<ModelAktivitas> get listAktivitas =>
UnmodifiableListView(_listAktivitas);
ModelAktivitas get detailAktivitas => _detailAktivitas;
set listAktivitas(List<ModelAktivitas> listAktivitas) {
_listAktivitas = listAktivitas;
notifyListeners();
}
set detailAktivitas(ModelAktivitas aktivitas) {
_detailAktivitas = aktivitas;
notifyListeners();
}
}
After that, add the "get" from your firestore services:
getListAktivitas(AktivitasNotifier aktivitasNotifier) async {
QuerySnapshot snapshot =
await Firestore.instance.collection('trail').getDocuments();
List<ModelAktivitas> _listAktivitas = [];
snapshot.documents.forEach((doc) {
ModelAktivitas modelAktivitas = ModelAktivitas.fromMap(doc.data);
_listAktivitas.add(modelAktivitas);
});
aktivitasNotifier.listAktivitas = _listAktivitas;
}
then, the last step stream it to your UI code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:provider/provider.dart';
import 'package:visitmalang/provider/aktivitas_provider.dart';
import 'package:visitmalang/ui/home/beranda/aktivitas/profil_agen_wisata.dart';
import 'package:visitmalang/ui/widget/textcustom.dart';
import 'package:visitmalang/service/user_service.dart';
class MapAktivitasTandingan extends StatefulWidget {
#override
_MapAktivitasTandinganState createState() => _MapAktivitasTandinganState();
}
class _MapAktivitasTandinganState extends State<MapAktivitasTandingan> {
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
GoogleMapController mapController;
bool mapToggle = false;
bool geraiToggle = false;
var currentLocation;
#override
void initState() {
// TODO: implement initState
super.initState();
Geolocator().getCurrentPosition().then((lokasiSekarang) {
setState(() {
currentLocation = lokasiSekarang;
mapToggle = true;
});
});
AktivitasNotifier aktivitasNotifier =
Provider.of<AktivitasNotifier>(context, listen: false);
getListAktivitas(aktivitasNotifier);
}
#override
Widget build(BuildContext context) {
AktivitasNotifier aktivitasNotifier =
Provider.of<AktivitasNotifier>(context);
return Scaffold(
appBar: AppBar(
title: textCustom('Ngetrail', Colors.black87, 18, 'Montserrat'),
centerTitle: true,
elevation: 0.0,
backgroundColor: Colors.transparent.withOpacity(0.0),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios, color: Colors.black87),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height - 80,
child: mapToggle
? GoogleMap(
myLocationEnabled: true,
myLocationButtonEnabled: true,
markers: {
},
compassEnabled: false,
zoomControlsEnabled: false,
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(currentLocation.latitude,
currentLocation.longitude),
zoom: 15),
onMapCreated: (controller) {
setState(() {
mapController = controller;
});
},
)
: Center(
child: textCustom(
'Loading...', Colors.black87, 20, 'Hind')),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
height: 140.0,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: aktivitasNotifier.listAktivitas.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: InkWell(
onTap: () {
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
aktivitasNotifier
.listAktivitas[index]
.koordinat
.latitude,
aktivitasNotifier
.listAktivitas[index]
.koordinat
.longitude),
zoom: 16.0,
bearing: 19.0,
tilt: 15.0),
),
);
},
child: Stack(
alignment: FractionalOffset(0.5, 0.94),
children: <Widget>[
Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(10.0),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(10.0),
color: Colors.white,
),
height: 108,
width: 200,
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
//
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
bottomLeft:
Radius.circular(10.0)),
child: Container(
width: 96,
height: 108,
child: Image.asset(
'assets/trail.png',
fit: BoxFit.cover,
),
),
),
SizedBox(
width: 4,
),
Stack(
alignment:
FractionalOffset(0.5, 0.9),
children: <Widget>[
Container(
width: 100,
child: Padding(
padding:
const EdgeInsets.only(
top: 8.0),
child: Column(
children: <Widget>[
Padding(
padding:
const EdgeInsets
.symmetric(
horizontal:
8.0),
child: Container(
alignment:
Alignment.center,
child: textCustom(
aktivitasNotifier
.listAktivitas[
index]
.nama,
Colors.black87,
14,
'Montserrat'),
),
),
],
),
),
),
ClipRRect(
borderRadius:
BorderRadius.circular(10),
child: InkWell(
onTap: () => Navigator.of(
context)
.push(CupertinoPageRoute(
builder: (BuildContext
context) =>
ProfilAgenWisata(
nama: aktivitasNotifier
.listAktivitas[
index]
.nama,
website: aktivitasNotifier
.listAktivitas[
index]
.website,
noTelepon: aktivitasNotifier
.listAktivitas[
index]
.noTelepon,
email: aktivitasNotifier
.listAktivitas[
index]
.email,
alamat: aktivitasNotifier
.listAktivitas[
index]
.alamat,
deskripsi: aktivitasNotifier
.listAktivitas[
index]
.deskripsi,
))),
child: Container(
alignment: Alignment.center,
color: Color(0xFFDB5C48),
height: 40,
width: 88,
child: textCustom(
'Detail',
Colors.white,
14,
'Montserrat'),
),
),
)
],
),
],
),
),
),
],
)),
);
},
)),
SizedBox(
height: 56.0,
)
],
)
],
));
}
}

Resources