I was wondering if someone could show me how to implement the Flutter StreamProvider "catchError" property?
Example code below to add to:
StreamProvider<LocationModelNormal>.value(
initialData: LocationModelNormal.initialData(),
stream: locationStreamInstance.specificLocation(_secondWonder),
catchError: ?????????
),
class LocationModelNormal {
final String name;
LocationModelNormal({
this.name
});
factory LocationModelNormal.fromMap(Map<String, dynamic> data) {
return LocationModelNormal(
name: data['name'] ?? '',
);
}
factory LocationModelNormal.initialData() {
return LocationModelNormal(
name: '',
);
}
}
You'll want to model your data using sealed classes:
abstract class Data {}
class Content implements Data {
Content(this.data);
final List<String> data;
}
class Error implements Data {
Error(this.msg);
final String msg;
}
class Loading implements Data {
const Loading();
}
Then used like so in the provider:
StreamProvider<Data>(
builder: (_) async* {
yield Content(['hello', 'world']);
},
initialData: const Loading(),
catchError: (_, err) => Error(err.toString()),
child: Container(),
);
And consumed as such:
Consumer<Data>(
builder: (_, data, __) {
if (data is Loading) {
return const CircularProgressIndicator();
} else if (data is Error) {
return Center(child: Text(data.msg));
} else if (data is Content) {
return ListView.builder(
itemCount: data.data.length,
itemBuilder: (_, index) => Text(data.data[index]),
);
}
throw FallThroughError();
},
);
Easy fix for now.
#override
Widget build(BuildContext context) {
return StreamProvider<UserModel?>.value(
value: AuthenticationService().user,
initialData: null,
catchError: (_, err) => null,
child: const MaterialApp(
home: AuthWrapper(),
),
);
}
}
Remi of course has the most thorough and proper method, since in the case of an error, you need to provide a value in its place or make it nullable. His solution is the most complete.
However, if you have other things already established, and need a down and dirty solution: Below I make it nullable with the ? and return a null value in the case of an error. The return is not technically necessary.
StreamProvider<LocationModelNormal?>.value(
initialData: LocationModelNormal.initialData(), //or null maybe better
stream: locationStreamInstance.specificLocation(_secondWonder),
catchError: (context, e) {
print('error in LocationModelNormal: ${e.toString()}');
//or pop a dialogue...whatever.
return null;
},
),
You can also do this
StreamProvider<DocumentSnapshot>.value(
value: api.myDetails(mail),
child: Builder(
builder: (context){
var snapshot = Provider.of<DocumentSnapshot>(context);
if(snapshot == null){
return customF.loadingWidget();
}else{
return Stack(
children: <Widget>[
getMainListViewUI(),
getAppBarUI(),
SizedBox(
height: MediaQuery.of(context).padding.bottom,
)
],
);
}
}
),
),
Related
I have a method that fetches a PatientLog from SQLite.However, This PatientLog table mapped to an object with a class named PatientLog. Inside this PatientLog class, several other objects such as Speciality, AttendingPhysician, Course, etc. I need to map these PatienLog records to a local object. However, I have to use nested Futures. I need to retrieve the data from this nested Future. Think of Future of Future.
This is my fetch method
Future<List<Future<PatientLog>>> getForms() async {
Database db = await instance.getDatabase;
List<Map<String, dynamic>> forms =
await db.query(_tablePatientLog, orderBy: 'id DESC');
Institute? institute;
AttendingPhysician? attendingPhysician;
Speciality? speciality;
Course? course;
List<Future<PatientLog>> list = forms.map((myMap) async {
int? courseId = myMap['course_id'] as int?;
int? specialityId = myMap['speciality_id'] as int?;
int? attendingId = myMap['attending_id'] as int?;
int? instituteId = myMap['institute_id'] as int?;
if (courseId != null) {
await getCourse(courseId).then((value) => course=value);
}
if (attendingId != null) {
await getAttending(attendingId).then((value) => attendingPhysician=value);
}
if (specialityId != null) {
await getSpeciality(specialityId).then((value) => speciality=value);
}
if (instituteId != null) {
await getInstitute(instituteId).then((value) => institute=value);
}
return PatientLog.fromMap(
myMap, institute, course, attendingPhysician, speciality);
}).toList();
return list;
}
I need to display that information on a screen. I get an error type 'List<Future<PatientLog>>' is not a subtype of type 'Future<Object?>?'
class _DraftsState extends State<Drafts> {
final SQFLiteHelper _helper = SQFLiteHelper.instance;
#override
void initState() {
super.initState();
_refresh();
}
late List<Future<PatientLog>> fromDatabase;
Future<dynamic> _refresh() async {
await _helper.getForms().then((value) async{
setState(() {
fromDatabase = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _helper.getForms(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data!.isEmpty) {
return Center(
child: Text(
"Henüz kaydedilmiş taslak bulunmamaktadır.",
textAlign: TextAlign.center,
style: TEXT_STYLE,
));
}
if (snapshot.hasError) {
return Center(
child: Text(
'Sanırım bir şeyler ters gitti.',
style: TEXT_STYLE,
));
}
if (snapshot.connectionState == ConnectionState.done) {
return RefreshIndicator(
backgroundColor: Colors.grey[700],
color: LIGHT_BUTTON_COLOR,
onRefresh: _refresh,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder(
future: snapshot.data,
builder: (context,innerSnap) {
return Text(innerSnap.toString());/*CustomListTile(
formData: innerSnap.data[index],
index: index,
routeTo: 1,
isDeletable: true,
);*/
}
);
},
),
),
);
}
return const Center(
child: Text("Nothing")//spinkit,
);
}),
);
}
}
At first, when i started writing my calls to get data from firestore, it worked. But when i tried writing more docs to my collection, it failed to bring data for the docs i recently added. Then, when i deleted the first one i added, i stopped receiveing data from firestore all together. I have tried several methods, but have all ended in failure.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class collect extends StatefulWidget {
#override
_collectState createState() => _collectState();
}
class _collectState extends State<collect>
{
Future _data;
void initState()
{
super.initState();
_data = getStuff();
}
Future getStuff()
async {
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection("buses").get();
return qn.docs;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _data,
builder: (_, snapshot)
{
if(snapshot.connectionState == ConnectionState.waiting)
{
return Center(
child:Text("Loading")
);
}
else if(snapshot.connectionState == ConnectionState.done)
{
return ListView.builder(itemCount: snapshot.data.length,itemBuilder:(_, index)
{
return Container(
child: ListTile(
title: Text(snapshot.data[index].data()["name"].toString()),
subtitle: Text(snapshot.data[index].data()["price"].toString()),
),
);
});
}
},
),
);
}
}
```![enter image description here](https://i.stack.imgur.com/L7FqF.jpg)
Define your database call as,
Future getStuff() async {
var docs;
await FirebaseFirestore.instance
.collection("buses")
.get()
.then((querySnapshot) {
docs = querySnapshot.docs;
});
return docs;
}
Then use the FutureBuilder in the build() function as,
return Scaffold(
body: Center(
child: FutureBuilder<dynamic>(
future: getStuff(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Container(
child: ListTile(
title: Text(
snapshot.data[index].data()["name"].toString()),
subtitle: Text(
snapshot.data[index].data()["price"].toString()),
),
);
});
} else {
return CircularProgressIndicator();
}
},
),
),
);
I wrapped the FutureBuilder inside a Center just for clarity, you may remove that Center widget.
I have a problem with my BLoC implementation, I have this code in synchronize.dart:
...
class _SynchronizeState extends State<Synchronize> {
UserBloc userBloc;
//final dbRef = FirebaseDatabase.instance.reference();
#override
Widget build(BuildContext context) {
userBloc = BlocProvider.of(context);
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Container(
...
),
child: StreamBuilder(
stream: dbRef.child('info_tekax').limitToLast(10).onValue,
builder: (context, snapshot) {
if(snapshot.hasData && !snapshot.hasError){
Map data = snapshot.data.snapshot.value;
List keys = [];
data.forEach( (index, data) => keys.add(index) );
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) => SynchronizeItem(title: keys[index], bottom: 10, onPressed: (){ print(keys[index]); })
);
}else{
return Container(
child: Center(
child: Text('Loading...'),
),
);
}
}
),
),
);
}
}
The previos code, works correctly, but i want implemente bloc Pattern, i have userBloc then i want to put this
userBloc.getDevicesForSinchronized()
instead of
dbRef.child('info_tekax').limitToLast(10).onValue,
my problem is this:
void getDevicesForSynchronized() {
return dbRef.child(DEVICES).limitToLast(10).onValue;
}
i get this error **A vaue of type 'Stream' can't be returned from method 'getDevicesForSynchronized' because it has a return type of 'void'
The error is very clear, but i don't know what is type that i need return, try:
Furure<void> getDevicesForSynchronized() async {
return await dbRef.child(DEVICES).limitToLast(10).onValue;
}
or
Furure<void> getDevicesForSynchronized() async {
dynamic result = await dbRef.child(DEVICES).limitToLast(10).onValue;
}
and another solutions, but I don't know how return correctly value for use in the StreamBuilder
From the error message you can see that the return type is Stream. Change your method like:
Future<Stream> getDevicesForSynchronized() async {
return dbRef.child(DEVICES).limitToLast(10).onValue;
}
This my Code. When i run code i'm getting a error like this
"TypeError (type 'String' is not a subtype of type 'int' of 'index')".
Is it something Do with Json Data or the I'm using Wrong Data Type.
Does Future<List<Arrivals>> is written correctly?
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Arrivals> details = [];
Future<List<Arrivals>> _getDetails() async {
var data =
await http.get("https://flight-api-maldives.herokuapp.com/arrivals");
var jsonData = jsonDecode(data.body);
for (var val in jsonData) {
Arrivals arrivals = Arrivals(
val['Scheduled'],
val['Revised'],
val['From'],
val['Flight'],
val['Status'],
);
details.add(arrivals);
print(details.length);
}
return details;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
FutureBuilder(
future: _getDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data != null) {
return Container(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Center(child: Text('snapshot.data[index].Scheduled'),);
}
),
);
}else{
return Center(
child: Text('NO'),
);
}
},
)
],
),
),
);
}
}
class Arrivals {
final String Scheduled;
final String Revised;
final String From;
final String Flight;
final String Status;
Arrivals(this.Scheduled, this.Revised, this.From, this.Flight, this.Status);
}
this is the json data im using:
[
[
{
"Scheduled": "06:35",
"Revised": "06:35",
"From": "Kaadedhdhoo (KDM)",
"Flight": "Maldivian Q2 149",
"Status": "On-Time"
},
{
"Scheduled": "06:40",
"Revised": "06:40",
"From": "Dharavandhoo Island (DRV)",
"Flight": "Maldivian Q2 289",
"Status": "On-Time"
},
]
]
Where is the picture of my error
https://i.stack.imgur.com/qVWLc.png
the main problem is in json. it is list of list.
var jsonData = jsonDecode(data.body);
jsonData = jsonData[0]; // added line
for (var val in jsonData) {
Moreover their is no need of so many extra code to just display list view you can use following simple code to display list view. In addition to that Listview.builder require item count property to specify total number of item.
Replace below build method with your will work for you.
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _getDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot);
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text(snapshot.data[index].Scheduled.toString()),
);
});
} else {
return Center(
child: Text('NO'),
);
}
},
),
);
}
In the JSON data, you've got a list inside a list.
val['Scheduled'],
This line is getting called on a list, so the square brackets are expecting an index rather than a map key
While fetching data from database in flutter snapShot.ConnectionState is always waiting and the circular progress indicator keeps on loading.
I am not getting any errors and I am using FutureBuilder to build my widget.
Class where I build my widget
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/event_provider.dart';
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Provider.of<EventProviders>(context).fetchAndSetEvents(),
builder: (ctx, dataSnapshot) {
if (dataSnapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Consumer<EventProviders>(
child: Text('Not found'),
builder: (ctx, eventData, ch) {
if (eventData.events.length <= 0) {
return ch;
} else {
return ListView.builder(
itemCount: eventData.events.length,
itemBuilder: (ctx, index) {
return Container(
child: Text(eventData.events[index].eventName),
);
},
);
}
},
);
}
},
);
}
}
My future class
Future<void> fetchAndSetEvents() async {
final dataList = await DBHelper.getData('user_events');
_events = dataList
.map(
(data) => EventProvider(
eventName: data['event'],
eventDate: data['date'],
id: data['id'],
),
)
.toList();
notifyListeners();
}
}
Some help will be highly appreciated
Set listen: false
future: Provider.of<EventProviders>(context, listen: false).fetchAndSetEvents(),