firebase data not loading in flutter app on emulator - android

I am trying to fetch data from various collections in Firebase for a flutter app. However, I've noticed that as the number of documents in my collections increase, the longer it takes for it to display in my app on an emulator using Android Studio. Specifically, for the collection with 1 doc, it loads instantly, for the collection with 2 docs, it takes a little longer, and for my collection with 5 docs, it doesn't load at all. How can I solve this problem? All docs have the same number of fields and I am mapping the same function on all collections to access and display their data.
This is the code I use to fetch the data, and I call this class in my main.dart file. This current code does have a rangeError currently since I'm not checking whether the currentIndex is within the bounds, but I believe the issues are unrelated since I cannot get the first question to display in case the subject is 'biology', which is the collection with 5 docs.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import './questions.dart';
class QuizPage extends StatefulWidget {
final String subjectHolder;
const QuizPage(this.subjectHolder, {Key? key}) : super(key: key);
#override
State<QuizPage> createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
String subject = 'biology';
#override
void initState(){
subject = widget.subjectHolder;
super.initState();
}
Stream<List<Question>> readQuestions() =>
FirebaseFirestore.instance.collection(subject).snapshots()
.map((snapshot) => snapshot.docs.map((doc) => Question.fromJson(doc.data())).toList());
var currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<Question>>(
stream: readQuestions(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final questions = snapshot.data!;
return Center(
child: Column(children: [
Text("${questions[currentIndex].question}"),
ElevatedButton(onPressed: () {
setState(() {
currentIndex +=1;
});
},
child: Text("${questions[currentIndex].option_a}")),
ElevatedButton(onPressed: () {
setState(() {
currentIndex +=1;
});
},
child: Text("${questions[currentIndex].option_b}")),
ElevatedButton(onPressed: () {
setState(() {
currentIndex +=1;
});
},
child: Text("${questions[currentIndex].option_c}")),
]
),
);
}
else {
return Center(child: CircularProgressIndicator());
}
}),
);
}
}

Related

Firebase data Cannot be retrieved to Flutter

I am trying to retrieve data from a Realtime database in Firebase to Flutter. The data should be parsed to be used in the building of a listview inside a future builder. However, after I execute the code I got an error that displayed on the Emulator screen. My understanding is that there is a type mismatch inside the code of firebaseCalls method. Below is my code Main.dart, data model, Firebase data, and Error Message. Any help to figure out the issue is appreciated. Thanks in advance!
Main.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'datamodel.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final ref = FirebaseDatabase.instance.ref();
Future<List<Menu>> firebaseCalls(DatabaseReference ref) async {
DataSnapshot dataSnapshot = await ref.child('Task').get();
String? jsondata =dataSnapshot.value as String?; // just in case String is not working
//String jsondata = dataSnapshot.children;// value;//[0]['Task'];// should be dataSnapshot.value
// Decode Json as a list
final list = json.decode(jsondata!);// as List<dynamic>;
return list.map((e) => Menu.fromJson(e)).toList();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
//drawer: _drawer(data),
appBar: AppBar(
title: const Text('الصف السادس العلمي'),
),
body: FutureBuilder(
future: firebaseCalls(ref), // async work
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return new Text('Press button to start');
case ConnectionState.waiting:
return new Text('Loading....');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) =>
_buildTiles(snapshot.data[index]),
);
}
} // builder
)
)
);
}
////////////////////////////////////////////
Widget _buildTiles(Menu list) {
if (list.subMenu?.length == 0)
return new ListTile(
leading: Icon(list.icon),
title: Text(
list.name!,
style: TextStyle(
fontSize: list.font?.toDouble(), fontWeight: FontWeight.bold),
),
onTap: () => debugPrint("I was clicked"),
);
return new ExpansionTile(
leading: Icon(list.icon),
title: Text(
list.name!,
style: TextStyle(
fontSize: list.font?.toDouble(), fontWeight: FontWeight.bold),
),
children: list.subMenu!.map(_buildTiles).toList(),
);
}//_buildTiles
}
datamodel.dart
import 'package:flutter/material.dart';
class Menu {
String? name; // I added ?
IconData? icon;// I added ?
int? font;// I added ?
List<Menu>? subMenu= [];// I added ?
Menu({this.name, this.subMenu, this.icon,this.font});
Menu.fromJson(Map<String, dynamic> json) {
name = json['name'];
font = json['font'];
icon = json['icon'];
if (json['subMenu'] != null) {
//subMenu?.clear(); // I added ? it also recomand using !
json['subMenu'].forEach((v) {
//subMenu?.add(new Menu.fromJson(v));
subMenu?.add(Menu.fromJson(v));
});
}
}
}
Database:
Error message:
The problem is here:
DataSnapshot dataSnapshot = await ref.child('Task').get();
String? jsondata =dataSnapshot.value as String?;
If we look at the screenshot of the database you shared, it's clear that the value under the /Task path is not a string. It is in fact an entire object structure, which means you get back a Map<String, Object> from dataSnapshot.value. And since that's not a String, you get the error that you get.
The proper way to get the value of the entire Task node is with something like:
Map values = dataSnapshot.value;
And then you can get for example the name with:
print(values["name"]);
Alternatively you get get the child snapshot, and only then get the string value from it, with something like:
String? name = dataSnapshot.child("name")?.value as String?;

Saving information to dart flutter database and pulling information to listTile

I have an application. In the application the user will save data. When you log into a particular page, the record of logging into that page will be saved in the database.
My problem is this: I examined the sqflite database structure, but I could not understand it. It's a strange building. What I need to do is to save data in only 1 column and pull them and put them in listTile.
But as I said, I couldn't do what I wanted because I couldn't understand the sqflite structure.
How can I do it? How do I use sqflite?
The sqflite library provides the sqlite database to flutter. Your question leads me to assume that you first need to read a bit more about what it is and what is used for.
Once you are familiar with the fundamentals you will be able to grasp the usage of the library fairly easily.
For your application though, I would suggest going for simpler options. You might find a key-value store like shared_preferences, to be easier to grasp and get started with. Just put the data as a JSON list in the store and retrieve it for display when building the ListView.
EDIT:
Use the following as a starting point and take it further as per your requirement:
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
Database? db;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
db = await openDatabase(
'my_db.db',
version: 1,
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT)');
},
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Map<String, Object?>>? _records;
bool _loading = false;
int nextRecordId = 1;
#override
void initState() {
super.initState();
getRecords();
}
void getRecords() {
setState(() {
_loading = true;
});
db?.query('Test').then((value) {
setState(() {
_records = value;
_loading = false;
});
});
}
void _insertRandomRecord() {
db
?.insert(
'Test',
{
'id': '$nextRecordId',
'name': 'Random record $nextRecordId',
},
conflictAlgorithm: ConflictAlgorithm.replace)
.then((value) {
nextRecordId++;
getRecords();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _loading
? const Center(
child: CircularProgressIndicator.adaptive(),
)
: ListView.builder(
itemBuilder: (context, index) {
final record = _records![index];
return ListTile(
title: Text(record['name'] as String),
);
},
itemCount: _records?.length ?? 0,
),
floatingActionButton: FloatingActionButton(
onPressed: _insertRandomRecord,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

My elevated buttons are greyed out and i dont understand why

i think the on Pressed function in elevated button is null but i dont understand why
my main file where i am using List and Map to create and switch questions and answers
answers are on the buttons and they are printed on them but they are greyed out
import './quiz.dart';
import './result.dart';
void main() => runApp(TestApp());
#override
class TestApp extends StatefulWidget {
const TestApp({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _TestAppState();
}
}
class _TestAppState extends State<TestApp> {
var _i = 0;
final _question = const [
{
'q1': 'whats the capital of India',
'a1': ['Delhi', 'Mumbai', 'Chennai', 'Bangalore'],
},
{
'q1': 'whats the Language of India',
'a1': ['Sanskrit', 'Bengali', 'Hindi', 'Kannada'],
},
{
'q1': 'whats the continent India is located in',
'a1': ['Africa', 'Asia', 'America', 'Australia'],
},
{
'q1': 'whats second most spoken language in India',
'a1': ['Hindi', 'Gujarati', 'Marathi', 'English'],
},
];
_answeredQ() {
setState(() {
_i = _i + 1;
});
// return 0;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Test App!"),
),
body: _i < _question.length
? Quiz(qMap: _question, aFunction: _answeredQ(), index: _i)
: Result(),
),
);
}
}
**here's my Quiz class using as a custom widget
import './questionText.dart';
import './answer.dart';
class Quiz extends StatelessWidget {
final List<Map<String, Object>> qMap;
final aFunction;
final int index;
Quiz({required this.qMap, required this.aFunction, required this.index});
#override
Widget build(BuildContext context) {
return Column(
children: [
Question(
qMap[index]['q1'],
),
...(qMap[index]['a1'] as List<String>).map((ans) {
return AnswerW(aFunction, ans);
}).toList()
],
);
}
}
and here's the button custom widget class
class AnswerW extends StatelessWidget {
final selAns;
final String answerText;
AnswerW( this.selAns, this.answerText);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: EdgeInsets.all(10),
child: ElevatedButton(onPressed: selAns,
child: Text(answerText),
),
);
}
}
In ? Quiz(qMap: _question, aFunction: _answeredQ(), index: _i) You are passing the return value of _answeredQ(), not the actual function itself. You can change this to just _answeredQ (without the "()") or aFunction: () => _answeredQ()
FWIW It's good in dart to take advantage of strong typing. It provides you with better error messages and better linting. Because you don't have any types for most of your variables they can be anything, and the linter has a hard time trying to figure out if you have a type mismatch.

Refreshing or rebuilding flutter widget after it has been fully initialised

I am trying to make a Telegram client for android using the tdlib flutter port. I am currently attempting to make a contact list of sorts, by requesting it from telegram and making a listview of textbuttons.
The only issue is that since the library is async, I get the contact list after the layout has been initialized. Is it possible to somehow rebuild the layout or update it to make the list load properly.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fima/services/telegram_service.dart';
import 'package:tdlib/td_api.dart' show TdError;
import 'package:provider/provider.dart';
import 'package:tdlib/td_api.dart' as TdApi;
class ContactListScreen extends StatefulWidget {
#override
_ContactListScreenState createState() => _ContactListScreenState();
}
class _ContactListScreenState extends State<ContactListScreen> {
final String title = 'Contact list';
bool _loadingStep = false;
String _Error;
String route = "initRoute";
List<TextButton> contacts = [];
#override
void initState() {
super.initState();
_getContacts(onError: _handelError,);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
backgroundColor: Color(0xD3232323),
),
body: Container(
child:
ListView (
children: contacts,
),
),
);
}
Future _getContacts(
{
void Function(TdError) onError,
}) async {
final result = await context.read<TelegramService>().send(
TdApi.GetContacts(
),
);
if (result is TdError && onError != null) {
onError(result);
}
TdApi.Users users = result;
for (var i = 0; i < users.totalCount; i++) {
final result = await context.read<TelegramService>().send(
TdApi.GetUser(userId: users.userIds[i]),
);
TdApi.User user = result;
print(user.firstName + " " + user.lastName);
final contact = TextButton(
onPressed: () {
print("Test");
},
child: Text(user.firstName + " " + user.lastName),
);
setState(() {
contacts.add(contact);
});
}
}
void _handelError(TdError error) async {
setState(() {
_loadingStep = false;
_Error = error.message;
});
}
}
I have attempted to use setState, but without much success, could anyone be so kind as to provide me with the solution to this problem?
Using the FutureBuilder might help. It is a widget that builds itself based on the latest snapshot of interaction with a Future.
You can modify your build to return a FutureBuilder something like this:
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getContacts,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
//Use snapshot data to build by returning your Container with List
}
else{
//Return a CircularProgressIndicator
}
}
}
Refer the documentation on the FutureBuilder class here.

Checking one CheckBox in a ListView checks all of the rest using Flutter

I am completly new to Flutter and Stackoverflow. This is my first question to be in fact so please forgive me if I totaly fail at asking this question. I am trying to make a simple Flutter app that provides a ListView of questions and a checkbox beside each. The user can then choose which question they want to answer. My problem is that when the user checks any of the checkboxes then all get checked and vise versa. The questions themselves are retrieved from a backendless database. The code below is what i have so far. I would really appreciate any help anyone can provide me.
import 'package:flutter/material.dart';
class Questions extends StatefulWidget {
final List<Map> questionList;
Questions(this.questionList);
#override
_QuestionsState createState() => _QuestionsState();
}
class _QuestionsState extends State<Questions> {
bool _questionSelected = true;
Widget _buildQuestionItem(BuildContext context, int index) {
return ListTile(
title: Text(widget.questionList[index]['question']),
trailing: Checkbox(
value: _questionSelected,
onChanged: (bool val){
setState(() {
_questionSelected = val;
});
},
),
);
}
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.all(10),
itemBuilder: _buildQuestionItem,
itemCount: widget.questionList.length,
);
}
}
UPDATED:
Thankful for Mohammed Ashab Uddin suggestions I feel that I am close to getting this thing to work but I am still getting an error
"RangeError (index): Invalid value: Valid value range is empty: 0"
I think I should have posted the main.dart code where I set the value of the questionList perhaps it is an order of code execution that causes this error so please find my code for main.dart below in hopes it would help in figuring out this issue.
import 'package:flutter/material.dart';
import 'package:backendless_sdk/backendless_sdk.dart';
import 'package:flutter/rendering.dart';
import 'questions.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'RT Database Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Questions'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
static const String API_HOST = "https://api.backendless.com";
static const String APP_ID = "<APP_ID>";
static const String ANDROID_APP_KEY = "<ANDROID_APP_KEY>";
static const String IOS_APP_KEY = "<IOS_APP_KEY>";
IDataStore<Map> questionsStore = Backendless.data.of('Questions');
List<Map> questionsList = [];
var _questionSelected = false;
#override
void initState() {
super.initState();
_initBackendless();
_enableRealTime();
getQuestions();
}
void _initBackendless() {
Backendless.setUrl(API_HOST);
Backendless.initApp(APP_ID, ANDROID_APP_KEY, IOS_APP_KEY);
}
void _enableRealTime() {
EventHandler<Map> rtHandlers = questionsStore.rt();
rtHandlers.addCreateListener((question) {
setState(() {
questionsList = List.from(questionsList);
questionsList.add(question);
});
});
rtHandlers.addUpdateListener((question) {
setState(() {
questionsList = List.from(questionsList
.map((m) => m['objectId'] == question['objectId'] ? question : m));
});
});
rtHandlers.addDeleteListener((question) {
setState(() {
questionsList = List.from(questionsList);
questionsList.removeWhere((m) => m['objectId'] == question['objectId']);
});
});
}
void _selectQuestion(bool newValue) {
setState(() {
_questionSelected = newValue;
});
}
void getQuestions() {
DataQueryBuilder queryBuilder = DataQueryBuilder()
..pageSize = 100
..sortBy = ['created'];
questionsStore
.find(queryBuilder)
.then((response) => setState(() => questionsList = response));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My Life History"),
),
body: FractionallySizedBox(
heightFactor: 0.5,
child: Questions(questionsList),
),
);
}
}
The variable _questionSelected is a global variable. All the checkbox widgets are using this variable as the value. Therefore, when the variable changes on the onChanged() function, all the values are also changed to the value of _questionSelected.
In this case, you need to keep track of all the values of the checkbox widget. So, you should use an array rather than a single variable.
What I usually do is, create a new list that will contain only the selected elements.
Remove an element if it is not selected and add an element if it is selected.
//generate a list of false values with the length of questionList
List<bool> _questionSelected;
initState(){
_questionSelected = List<bool>.filled(questionList.length, false, growable: true);
super.initState();
}
Widget _buildQuestionItem(BuildContext context, int index) {
return ListTile(
title: Text(widget.questionList[index]['question']),
trailing: Checkbox(
value: _questionSelected[index],
onChanged: (bool val){
setState(() {
_questionSelected[index] = val;
});
},
),
);
}

Categories

Resources