How to solve range error in growable list? - flutter

When I am calling getUserById() function everything works fine and the growable string works fine but when I use the same variable inside a text widget then it shows RangeError.
The problem is occuring only when I try to use a growable list of string named as dataAsString inside the build.
Here is the Code with Error. Go to the scaffold where I have commented the line of error
import 'package:Healthwise/helpers/user.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../helpers/backEnd.dart';
import '../helpers/frontEnd.dart';
class ResultPage extends StatefulWidget {
const ResultPage({Key? key}) : super(key: key);
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
// String docId = '';
String objectToString = '';
String e = '';
//This variable is creating the problem...
// I have tried this by using a final instead of var, but nothing worked.
var dataAsString = <String>[];
#override
void initState() {
super.initState();
// getUsers();
getUserById();
}
getUserById() {
final String id = itemName;
userRef.doc(id).get().then((DocumentSnapshot doc) {
// final x = doc.data();
// docId= doc.id;
objectToString = doc.data().toString();
String temp = '';
// print(doc.data());
// print(doc.id);
int i = 1;
// int j = 0;
bool end = false;
//We are just parsing the object into string.
while (objectToString[i] != '}') {
if (objectToString[i - 1] == ' ' && objectToString[i - 2] == ':') {
while (objectToString[i] != ',' && end != true) {
// print(z[i]);
temp += objectToString[i];
if (objectToString[i + 1] != '}') {
i++;
} else {
end = true;
}
}
//Here I add all the strings to list...
// This line works fine.
dataAsString.add(temp);
temp = '';
// j++;
print("The code below this line prints everything perfectly");
print(dataAsString.length);
print(dataAsString[0]);
}
i++;
}
// print(dataAsString[0]);
// print(dataAsString[1]);
// print("+++++++++++");
// print(dataAsString[2]);
// for (var k in dataAsString) {
// print(k);
// }
// print(dataAsString);
// setState(() {});
});
}
// getUsers() {
// userRef.get().then((QuerySnapshot snapshot) {
// snapshot.docs.forEach((DocumentSnapshot doc) {
// print(doc.data());
// print(doc.id);
// print(doc.exists);
// });
// });
// }
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: primary_color, title: Text("Apple")),
body: Container(
child: Column(children: [
ListTile(
//The problem arises here and the app do not crash if I run the same code with just a //string in place of dataAsString[0]
title: Text(dataAsString[0]),
),
ListTile(
title: Text(dataAsString[1]),
),
ListTile(
title: Text(dataAsString[2]),
),
]),
),
);
}
}

You cant have data on dataAsString because you are using future(.then) on fetching data. You can do value check like
Text( dataAsString.isNotEmpty? dataAsString[0]: "0 is empty"),
Text( dataAsString.length>2? dataAsString[1]: "1 index is empty"),
Text( dataAsString.length>3? dataAsString[2]: "3rd item is empty"),

It's cause your dataAsString ins't ready on first Flutter rendered frame. So, you need to wait for the getUserById finish to access the dataAsString values.
You can use a builder to check if the data is ready in a readable way.
Builder(
builder: (context) {
if (dataAsString.length >= 2) {
return Column(children: [
ListTile(
title: Text(dataAsString[0]),
),
ListTile(
title: Text(dataAsString[1]),
),
ListTile(
title: Text(dataAsString[2]),
),
]);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
Warning: it'll work cause you're calling setState when you finish the async task.

Related

Random parameter for FutureBuilder

Here I have a StatefulWidget in which I want to get a random pet each time from a random url. Also, I have a condition for the random pet, if the condition is true, the pet will be shown, otherwise the random url and random pet should be selected again. I attached my code below, and the problem is the url only changes when the condition is false, but I want it to be randomly selected each time.
Putting the API.get_pets(init_random_url); in the future parameter of the FutureBuilder will solve the random selection but if the condition is false the URL and the pet would change two or three times, after searching about it and reading FutureBuilder documentation I put it in the initState and requestAgain and build, but I recognized the selectedURL in the build function does not work and the widget is stucked in the same URL until the condition gets false value.
import 'dart:developer';
import 'package:double_back_to_close/toast.dart';
import 'package:flutter/material.dart';
import 'package:pet_store/widgets/guess_game_random_card.dart';
import 'webservice/API.dart';
import 'main.dart';
import 'dart:math';
import 'utils/utils.dart';
Random random = new Random();
class Guess_Game extends StatefulWidget {
const Guess_Game({Key? key}) : super(key: key);
#override
State<Guess_Game> createState() => _Guess_GameState();
}
class _Guess_GameState extends State<Guess_Game> {
void initState() {
super.initState();
init_random_url = randomly_select_URL();
GuessGameFuture = API.get_pets(init_random_url);
}
void requestAgain() {
setState(() {
init_random_url = randomly_select_URL();
GuessGameFuture = API.get_pets(init_random_url);
});
}
#override
Widget build(BuildContext context) {
init_random_url = randomly_select_URL();
return Scaffold(
body: Center(
child:
FutureBuilder<List<dynamic>>(
future: GuessGameFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
var category = pet_data![random_pet].category.toString();
var photoURL = pet_data![random_pet].photoUrls;
// Here is the condition that ensure pet category is in the list and has an image
if (checkCategoryInList(category, items) &&
photoURL.length != 0) {
return Random_Card(
pet_data: pet_data,
random_pet: random_pet,
dropdownvalue: dropdownvalue);
} else {
if (photoURL.length == 0) {
print(" NO PHOTO SUBMITTED FOR THIS PET");
} else {
print(category + "NOT IN CATEGORY");
}
WidgetsBinding.instance.addPostFrameCallback((_) {
requestAgain();
});
}
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const CircularProgressIndicator();
},
)
else
const Text(
"Please select your guess",
style: TextStyle(fontSize: 17, color: Colors.indigo),
),
),
),
}
}
Add this line to build
GuessGameFuture = API.get_pets(randomly_select_URL());
and Change requestAgain function to this:
void requestAgain() {
setState(() {
GuessGameFuture = API.get_pets(randomly_select_URL());
});
}
Also you can use FutureProvider and riverpod library.
Hope it helps

Flutter + syncfusion charts: Is it possible to have a dataSource that is not a List? (type 'BanditData' is not a subtype of type 'List<BanditData>')

I am trying to display a bar chart in my app with the syncfusion library. It contains 6 bars where the height is defined by a score and the name is a player name. I have the following methods: getBanditBarData() which gets the data from a database in creates a list of BanditData-objects (BanditData class shown below), and barChart() which creates a List of ChartSeries that I can return in the series parameter of my SfCartesianChart.
My problem is that the item of the dataSource: item-line in my barChart()-method gives the following exception:
_TypeError (type 'BanditData' is not a subtype of type 'List<BanditData>')
I've tried nesting an additional List around each BanditData object in the list, and even removing the for-loop of the method. Both changes result in similar errors somewhere in the same method.
Future<List<BanditData>> getBanditBarData() async {
var scores = await database.totalScore();
List<BanditData> banditData = [];
for (var score in scores) {
BanditData bandit = BanditData(score['name'], "", score['score']);
banditData.add(bandit);
}
return banditData;
}
List<ChartSeries> barChart(data) {
var barList = <ChartSeries>[];
for (var item in data) {
barList.add(BarSeries<BanditData, String>(
dataSource: item,
xValueMapper: (BanditData b, _) => removeBanditSuffix(b.name),
yValueMapper: (BanditData b, _) => b.score,
animationDuration: 2000));
}
return barList;
}
The BanditData-class is very simple and looks like this:
class BanditData {
BanditData(this.name, this.date, this.score);
final String name;
final String date;
final int score;
}
The setup shown above works when I render my line chart. The methods are very similar:
Future<List<List<BanditData>>> getBanditLineData() async {
var dates = await database.getDistinctDatesList();
var scores = await database.createScoreDataStruct();
List<List<BanditData>> banditData = [];
for (var i = 0; i < scores.length; i++) {
List<BanditData> temp = [];
var intList = scores[i]['scores'];
for (var j = 0; j < scores[i]['scores'].length; j++) {
BanditData bandit = BanditData(scores[i]['name'], dates[j], intList[j]);
temp.add(bandit);
}
banditData.add(temp);
}
return banditData;
}
List<ChartSeries> lineChart(data) {
var lineList = <ChartSeries>[];
for (var item in data) {
lineList.add(LineSeries<BanditData, String>(
dataSource: item,
xValueMapper: (BanditData b, _) => b.date,
yValueMapper: (BanditData b, _) => b.score,
enableTooltip: true,
name: removeBanditSuffix(item[1].name),
width: 3.0,
animationDuration: 2000,
));
}
return lineList;
}
If necessary, here is some more code showing how I build the chart. The above methods is placed inside MyStatsPageState, but figured it would be better to split it up for readability.
Ideally, I should be able to replace series: lineChart(lineData) with series: barChart(barData):
import 'database.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class MyStatsPage extends StatefulWidget {
const MyStatsPage({Key? key}) : super(key: key);
#override
MyStatsPageState createState() {
return MyStatsPageState();
}
}
class MyStatsPageState extends State<MyStatsPage> {
late Future<List<List<BanditData>>> _banditLineData;
late Future<List<BanditData>> _banditBarData;
final database = Database();
bool displayLineChart = true;
#override
void initState() {
_banditLineData = getBanditLineData();
_banditBarData = getBanditBarData();
super.initState();
getBanditBarData();
}
#override
Widget build(BuildContext context) {
const appTitle = "Stats";
return Scaffold(
appBar: AppBar(
title: const Text(
appTitle,
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w700),
)),
body: FutureBuilder(
future: Future.wait([_banditLineData, _banditBarData]),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
if (snapshot.hasError) {
return ErrorWidget(Exception(
'Error occured when fetching data from database'));
} else if (!snapshot.hasData) {
return const Center(child: Text('No data found.'));
} else {
final lineData = snapshot.data![0];
final barData = snapshot.data![1];
return Padding(
padding: const EdgeInsets.all(5.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: SfCartesianChart(
primaryXAxis: CategoryAxis(),
enableAxisAnimation: true,
series: lineChart(lineData),
)),
],
));
}
}
}));
}
We have checked the code snippet attached in the query and found that it is a sample-level issue, it occurs due to you have passed the BanditData instead of List. Because dataSource property always supports the list value only. To resolve this, convert the barData to nested lists, or assign a value to the BarSeries dataSource like below.
Code snippet:
List<ChartSeries> barChart(data) {
var barList = <ChartSeries>[];
for (var item in data) {
barList.add(BarSeries<BanditData, String>(
dataSource: [item],
// Other required properties
));
}
return barList;
}

Sometimes variable changes, sometimes it doesn't

first of all, sorry for the lack of information in the title, I just couldn't think of anything better to write.
My problem is that I'm parsing JSON data from an online API and sometimes it displays what I want it to display, and sometimes it doesn't... here is the code:
(The first code block is the class of what I'll be using in the second block, which is the important part and probably where I think the problem is coming from)
import 'dart:convert';
import 'package:http/http.dart' as http;
class CanteenInfo {
final String canteenUrl = 'https://sigarra.up.pt/feup/pt/mob_eme_geral.cantinas';
List canteenMenu = [];
var data;
String workingHours = "yes";
CanteenInfo() {
getWebsiteData();
}
Future getWebsiteData() async {
var response = await http.get(Uri.parse(canteenUrl));
if (response.statusCode == 200) {
data = json.decode(response.body);
workingHours = data[3]["horario"];
print("Hours: " + workingHours);
}
else {
throw Exception('Failed to read $canteenUrl');
}
}
}
class WorkingHours extends StatefulWidget {
const WorkingHours({Key? key}) : super(key: key);
#override
State<WorkingHours> createState() => _WorkingHours();
}
class _WorkingHours extends State<WorkingHours> {
String hours = "yes";
CanteenInfo canteen = CanteenInfo();
void getHours() {
setState(() {
hours = canteen.getHours();
});
}
#override
Widget build(BuildContext context) {
getHours();
return Scaffold(
appBar: AppBar(
title: Text('EasyFood'),
),
body: Center(
child: Container (
margin: const EdgeInsets.only(top: 100),
child: Column(
children: <Widget>[
Text(
'Lunch: ' + hours,
style: const TextStyle(
fontSize: 20
)
),
],
),
),
),
);
}
}
If I haven't made my problem clear, when I run the code, sometimes it displays the text as "Lunch: yes" and sometimes as the correct thing, which is "Lunch: 11:30 às 14:00".
By my understanding, I think what's happening is that sometimes the code can get the API information and time and has time to change the variable before the page loads, and sometimes it doesn't. Either way, I don't know how to fix it so if anyone has any idea I would relly appreciate the help :)
Thanks alot for taking the time to read this
I'm not sure if getHours method exists in CanteenInfo.
But you probably need to override initState method in your stateful widget and call
canteen.getWebsiteData().then((value) {
setState(() => hours = canteen.workingHours);
});
or something like that depending on which method returns data from API
A good decision will be to wait CanteenInfo object "canteen" to be initialized, smth like this:
var flag = false;
loadData()async{
flag = true;
canteen = CanteenInfo();
await getHours();
setState((){flag=false;});
}
Widget build(BuildContext context) {
if (hours == "yes" && !flag)
loadData();
if (flag)
return Scaffold(body:CircularProgressIndicator());
return Scaffold(
appBar: AppBar(
title: Text('EasyFood'),
),
body: Center(
child: Container (
margin: const EdgeInsets.only(top: 100),
child: Column(
children: <Widget>[
Text(
'Lunch: ' + hours,
style: const TextStyle(
fontSize: 20
)
),
],
),
),
),
);
}
I'm not sure if class object initialization works correct here, maybe you also need to add async/await
class CanteenInfo {
final String canteenUrl = 'https://sigarra.up.pt/feup/pt/mob_eme_geral.cantinas';
List canteenMenu = [];
var data;
String workingHours = "yes";
getWebsiteData() async {
var response = await http.get(Uri.parse(canteenUrl));
if (response.statusCode == 200) {
data = json.decode(response.body);
workingHours = data[3]["horario"];
print("Hours: " + workingHours);
}
else {
throw Exception('Failed to read $canteenUrl');
}
}
getHours() {
return workingHours;
}
}
// ------------------------
class WorkingHours extends StatefulWidget {
const WorkingHours({Key? key}) : super(key: key);
#override
State<WorkingHours> createState() => _WorkingHours();
}
// ------------------------
class _WorkingHours extends State<WorkingHours> {
String hours = "yes";
CanteenInfo canteen = CanteenInfo();
#override
void initState() {
super.initState();
canteen.getWebsiteData().then(() {
hours = canteen.getHours();
});
}
}

Flutter Bloc is not updating the state/ not working

I am developing a mobile application using Flutter. I am using the flutter bloc package, https://pub.dev/packages/flutter_bloc for managing and setting up the bloc. But when the state change it is not updating the widgets or views.
I have a bloc class file called home_bloc.dart with the following implementation.
class HomeEvent {
static const int FETCH_ARTICLES = 1;
static const int TOGGLE_IS_FILTERING = 2;
int _event = 0;
String _filterKeyword = "";
int get event => _event;
void set event(int event) {
this._event = event;
}
String get filterKeyword => _filterKeyword;
void set filterKeyword(String filterKeyword) {
this._filterKeyword = filterKeyword;
}
}
class HomeBloc extends Bloc<HomeEvent, HomeState> {
Repository _repository = Repository();
HomeState state = HomeState();
#override
HomeState get initialState => state;
#override
Stream<HomeState> mapEventToState(HomeEvent event) async* {
switch (event.event) {
case HomeEvent.FETCH_ARTICLES:
{
List<dynamic> articles = List<dynamic>();
fetchArticles(filter: event.filterKeyword).listen((dynamic article) {
articles.add(article);
});
state = state.copyWith(articles: articles);
break;
}
case HomeEvent.TOGGLE_IS_FILTERING:
{
state.isFiltering = ! state.isFiltering;
state = state.copyWith();
break;
}
default:
{
state = state.initial();
break;
}
}
yield state;
}
Stream<dynamic> fetchArticles({String filter = ""}) async* {
List<dynamic> list = (this.state.articles.length > 0)
? this.state.articles
: await _repository.getArticles();
if (filter.isNotEmpty) {
for (var article in list) {
if (article is String) {
yield article;
} else if (article.title.contains(filter)) {
yield article;
}
}
} else {
for (var article in list) {
yield article;
}
}
}
}
class HomeState {
bool _isFiltering = false;
List<dynamic> _articles = List<dynamic>();
bool get isFiltering => _isFiltering;
void set isFiltering(bool isFiltering) {
this._isFiltering = isFiltering;
}
List<dynamic> get articles => _articles;
void set articles(List<dynamic> list) {
this._articles = list;
}
HomeState initial() {
HomeState state = HomeState();
state.isFiltering = false;
state.articles = List<dynamic>();
return state;
}
HomeState copyWith({ bool isFiltering, List<dynamic> articles }) {
HomeState state = HomeState();
state.isFiltering = isFiltering != null? isFiltering: this._isFiltering;
state.articles = articles!=null && articles.length > 0? articles: this._articles;
return state;
}
}
This is my repository class returning dummy data.
class Repository {
Future<List<dynamic>> getArticles() async {
List<dynamic> list = List<dynamic>();
list.add("A");
Article article1 = Article();
article1.id = 1;
article1.title = "A start is born";
list.add(article1);
Article article2 = Article();
article2.id = 2;
article2.title = "Asking for help";
list.add(article2);
Article article3 = Article();
article3.id = 3;
article3.title = "Angel is comming";
list.add(article3);
list.add("B");
Article article4 = Article();
article4.id = 4;
article4.title = "Baby Boss";
list.add(article4);
Article article5 = Article();
article5.id = 5;
article5.title = "Beginner guide to Staying at Home";
list.add(article5);
list.add("C");
Article article6 = Article();
article6.id = 6;
article6.title = "Care each other";
list.add(article6);
Article article7 = Article();
article7.id = 7;
article7.title = "Controlling the world";
list.add(article7);
Article article8 = Article();
article8.id = 8;
article8.title = "Chasing the dream";
list.add(article8);
return list;
}
}
This is my HomePage widget
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
IconData _searchIcon = Icons.search;
Widget _appBarTitle;
HomeBloc _homeBloc;
#override
void initState() {
super.initState();
this._homeBloc = BlocProvider.of(context);
WidgetsBinding.instance.addPostFrameCallback((_) => this.fetchArticles());
WidgetsBinding.instance
.addPostFrameCallback((_) => this.buildAppBarTitle());
}
#override
Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(title: Text("Home"),),
body: Container(
child: buildListView(context, state),
),
);
},
);
}
#override
void dispose() {
super.dispose();
}
void buildAppBarTitle() {
this.setState(() {
if (_searchIcon == Icons.search) {
this._appBarTitle = Text("Home");
} else {
this._appBarTitle = TextField(
onChanged: (String inputValue) {
debugPrint("Search term has changed $inputValue");
//homeBloc.fetchArticles(filter: inputValue);
},
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
hintText: "Search",
),
);
}
});
}
Widget buildAppBarSearchIcon() {
return IconButton(
icon: Icon(
_searchIcon,
color: Colors.white,
),
onPressed: () {
if (this._searchIcon == Icons.search) {
//display the search text field and close icons
this.setState(() {
this._searchIcon = Icons.close;
this.buildAppBarTitle();
//homeBloc.toggleFiltering();
});
} else {
this.fetchArticles();
this.setState(() {
this._searchIcon = Icons.search;
this.buildAppBarTitle();
//homeBloc.toggleFiltering();
});
}
});
}
Widget buildListView(
BuildContext context, HomeState state) {
if (state.articles.length > 0) {
var listView = ListView.builder(
itemCount: state.articles.length,
itemBuilder: (context, index) {
var item = state.articles[index];
if (item is String) {
return buildListFirstInitialView(item);
}
Article article = item as Article;
return buildListArticleView(article);
});
return listView;
} else {
return Center(
child: Text("No resources found."),
);
}
}
Widget buildListFirstInitialView(String initial) {
return ListTile(
title: Text(initial),
);
}
Widget buildListArticleView(Article article) {
return ListTile(
title: Text(article.title),
);
}
Widget buildBottomNavigationBar() {
return BottomNavigationBar(
currentIndex: 0,
onTap: (int position) {},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
]);
}
void fetchArticles({String filter = ""}) {
HomeEvent event = HomeEvent();
event.event = HomeEvent.FETCH_ARTICLES;
_homeBloc.add(event);
}
}
As you can see this is my HomePage widget is doing. It will fetch the articles after the widget is built. Then the list view will be updated with the dummy data.
My main.dart file.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: BlocProvider(
create: (context) => HomeBloc(),
child: HomePage(),
),
);
}
}
When I run my app, it is not updating the list view with the dummy data. Instead, it is always showing the message for no records found.
Why is it not working?
Bloc will never change If the state didn't change, you might be confused that you assigned a state, but the fact is that Bloc is a Stream, you'll need to yield a state instead of assigning It directly.
Hopefully, you had implemented a copyWith, so you could do It as below:
yield state.copyWith(articles: articles)
For keeping your structure, you could still use:
// state = state.copyWith(articles: articles);
newState = state.copyWith(articles: articles);
...
yield newState;
Because state variable is used by Bloc, you must use another variable to prevent Bloc from comparing the same variable (state == state will always true, so the state never changed.)
your state class is not equatable ... Dart can't tell when the state has changed
You must use:
import 'package:equatable/equatable.dart';
I also don't think you should SET the state property on your bloc class. You should only yield it and let the bloc update it...
please check the docs as I could be wrong
I had the same issue and solved this problem changing:
yield status;
to
yield status.toList();
At the end of my mapEventToState() method.
And probably you had to make for all yield that is passing a List.
If it worked for you let me know

How to write this Flutter code more efficiently?

As you can see in first part I'm checking that a certain value contains in a document from Firestore and returns a boolean value. Now I'm calling that function in a build and based on that return value I'm changing a chip color (second part).
Now the problem is maybe because I'm calling it in a build function so its being called continuously and on that build and it costing me a ton of reads in Firestore or maybe the function is inefficient. How can I write this more efficiently?
checkAtt(String name, id , date) async{
var ref = _db.collection('subjects').document(id).collection('Att').document(date);
var docref = await ref.get();
return docref.data.containsKey(name)
?true
:false;
}
class PresentChip extends StatefulWidget {
final candidate;
PresentChip(
this.candidate, {
Key key,
}) : super(key: key);
#override
_PresentChipState createState() => _PresentChipState();
}
class _PresentChipState extends State<PresentChip> {
var isSelected = false;
var c = false;
#override
Widget build(BuildContext context) {
final SelectSub selectSub = Provider.of<SelectSub>(context);
final Date date = Provider.of<Date>(context);
db.checkAtt(widget.candidate, selectSub.selectsub, date.datenew).then((result){
print(result);
setState(() {
c = result;
});
});
return Container(
child: ChoiceChip(
label: Text('Present'),
selected: isSelected,
onSelected: (selected) {
db.gibAtt(
widget.candidate, selectSub.selectsub, date.datenew.toString());
setState(() {
isSelected = selected;
});
},
backgroundColor: !c ?Colors.red :Colors.green ,
selectedColor: !c ?Colors.red :Colors.green ,
));
}
}
Assuming you only want to read once from firestore, you need a FutureBuilder.
return Container(
child: FutureBuilder(
future: db.checkAtt(widget.candidate, selectSub.selectsub, date.datenew),
builder: (context, snapshot) {
if(snapshot.hasData)
return ChoiceChip(
...
backgroundColor: !snapshot.data ?Colors.red :Colors.green,
selectedColor: !snapshot.data ?Colors.red :Colors.green,
);
//Return another widget if the future has no data
return Text('Future has no data');
}
)
);
If you need your UI to react to changes from firestore, use a StreamBuilder.
You can remove the following bloc from your build method:
db.checkAtt(widget.candidate, selectSub.selectsub, date.datenew).then((result){
print(result);
setState(() {
c = result;
});
});