Chris
Chris

Reputation: 175

Flutter: StreamProvider catchError how to

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: '',
    );
  }
}

Upvotes: 7

Views: 4370

Answers (4)

jbryanh
jbryanh

Reputation: 2013

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;
            },
),

Upvotes: 0

TJBlack31
TJBlack31

Reputation: 785

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(),
  ),
);
}
}

Upvotes: 0

SilenceCodder
SilenceCodder

Reputation: 3164

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,
                  )
                ],
              );
            }
          }
        ),
      ),

Upvotes: -1

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 276957

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();
  },
);

Upvotes: 18

Related Questions