Skip to content

Commit

Permalink
Merge pull request zino-hofmann#691 from artflutter/flutter-bloc5-exm…
Browse files Browse the repository at this point in the history
…aple-update

Updated `flutter_bloc` example to work with `flutter_bloc` lib v5
  • Loading branch information
micimize authored Jul 26, 2020
2 parents dead29e + c9c6adb commit db8efe4
Show file tree
Hide file tree
Showing 11 changed files with 1,552 additions and 84 deletions.
4 changes: 1 addition & 3 deletions examples/flutter_bloc/lib/blocs/repos/my_repos_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ class MyGithubReposBloc extends Bloc<MyGithubReposEvent, MyGithubReposState> {
// this a bit of a hack
List<Repo> githubRepositories;

MyGithubReposBloc({@required this.githubRepository});

MyGithubReposState get initialState => new ReposLoading();
MyGithubReposBloc({@required this.githubRepository}) : super(ReposLoading());

@override
Stream<MyGithubReposState> mapEventToState(
Expand Down
38 changes: 7 additions & 31 deletions examples/flutter_bloc/lib/extended_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class _ExtendedBlocState extends State<ExtendedBloc> {
void initState() {
super.initState();
_refreshCompleter = Completer<void>();
bloc = BlocProvider.of<RepositoriesBloc>(context);
bloc = BlocProvider.of<RepositoriesBloc>(context)..run();
}

Future _handleRefreshStart(Bloc bloc) {
Expand Down Expand Up @@ -54,22 +54,21 @@ class _ExtendedBlocState extends State<ExtendedBloc> {
builder: (_, state) {
Widget child = Container();

if (state is GraphqlLoadingState) {
if (bloc.isLoading) {
child = Center(child: CircularProgressIndicator());
}

if (state is GraphqlErrorState<Map<String, dynamic>>) {
if (bloc.hasError) {
_handleRefreshEnd();
child = ListView(children: [
Text(
parseOperationException(state.error),
bloc.getError,
style: TextStyle(color: Theme.of(context).errorColor),
)
]);
}

if (state is GraphqlLoaded ||
state is GraphqlFetchMoreState) {
if (bloc.hasData) {
_handleRefreshEnd();
final itemCount =
state.data['viewer']['repositories']['nodes'].length;
Expand All @@ -96,9 +95,7 @@ class _ExtendedBlocState extends State<ExtendedBloc> {
final pageInfo =
state.data['viewer']['repositories']['pageInfo'];

if (index == itemCount - 1 &&
state is GraphqlLoaded &&
pageInfo['hasNextPage']) {
if (bloc.shouldFetchMore(index, 1)) {
bloc.fetchMore(after: pageInfo['endCursor']);
}

Expand All @@ -109,8 +106,7 @@ class _ExtendedBlocState extends State<ExtendedBloc> {
title: Text(node['name']),
);

if (state is GraphqlFetchMoreState &&
index == itemCount - 1) {
if (bloc.isFetchingMore && index == itemCount - 1) {
tile = Column(
children: [
tile,
Expand All @@ -137,23 +133,3 @@ class _ExtendedBlocState extends State<ExtendedBloc> {
);
}
}

String parseOperationException(OperationException error) {
if (error.clientException != null) {
final exception = error.clientException;

if (exception is NetworkException) {
return 'Failed to connect to ${exception.uri}';
} else {
return exception.toString();
}
}

if (error.graphqlErrors != null && error.graphqlErrors.isNotEmpty) {
final errors = error.graphqlErrors;

return errors.first.message;
}

return 'Unknown error';
}
74 changes: 62 additions & 12 deletions examples/flutter_bloc/lib/extended_bloc/graphql/bloc.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:graphql/client.dart';
import 'package:meta/meta.dart';
import 'package:graphql/internal.dart';

import 'package:graphql_flutter_bloc_example/extended_bloc/graphql/event.dart';
import 'package:graphql_flutter_bloc_example/extended_bloc/graphql/state.dart';
import 'event.dart';
import 'state.dart';

abstract class GraphqlBloc<T> extends Bloc<GraphqlEvent<T>, GraphqlState<T>> {
GraphQLClient client;
ObservableQuery result;
WatchQueryOptions options;

GraphqlBloc({@required this.client, @required this.options}) {
GraphqlBloc({@required this.client, @required this.options})
: super(GraphqlInitialState<T>()) {
result = client.watchQuery(options);

result.stream.listen((QueryResult result) {
if (state is GraphqlRefetchState &&
result.source == QueryResultSource.Cache) {
return;
}

if (result.loading && result.data == null) {
add(GraphqlLoadingEvent<T>(result: result));
}
Expand All @@ -31,19 +36,43 @@ abstract class GraphqlBloc<T> extends Bloc<GraphqlEvent<T>, GraphqlState<T>> {
}

if (result.hasException) {
add(GraphqlErrorEvent(error: result.exception, result: result));
add(GraphqlErrorEvent<T>(error: result.exception, result: result));
}
});

_runQuery();
}

void dispose() {
result.close();
}

void run() {
add(GraphqlRunQueryEvent<T>());
}

void refetch() {
add(GraphqlRefetchEvent<T>());
}

bool shouldFetchMore(int i, int threshold) => false;

bool get isFetchingMore => state is GraphqlFetchMoreState;

bool get isLoading => state is GraphqlLoadingState;

bool get isRefetching => state is GraphqlRefetchState;

T parseData(Map<String, dynamic> data);

bool get hasData => (state is GraphqlLoadedState<T> ||
state is GraphqlFetchMoreState<T> ||
state is GraphqlRefetchState<T>);

bool get hasError => state is GraphqlErrorState<T>;

String get getError => hasError
? parseOperationException((state as GraphqlErrorState<T>).error)
: null;

Future<void> _runQuery() async {
result.fetchResults();
}
Expand All @@ -54,17 +83,18 @@ abstract class GraphqlBloc<T> extends Bloc<GraphqlEvent<T>, GraphqlState<T>> {

void _refetch() => result.refetch();

@override
GraphqlState<T> get initialState => GraphqlInitialState<T>();

@override
Stream<GraphqlState<T>> mapEventToState(GraphqlEvent<T> event) async* {
if (event is GraphqlRunQueryEvent<T>) {
_runQuery();
}

if (event is GraphqlLoadingEvent<T>) {
yield GraphqlLoadingState<T>(result: event.result);
}

if (event is GraphqlLoadedEvent<T>) {
yield GraphqlLoaded<T>(data: event.data, result: event.result);
yield GraphqlLoadedState<T>(data: event.data, result: event.result);
}

if (event is GraphqlErrorEvent<T>) {
Expand All @@ -76,9 +106,29 @@ abstract class GraphqlBloc<T> extends Bloc<GraphqlEvent<T>, GraphqlState<T>> {
_refetch();
}

if (state is GraphqlLoaded && event is GraphqlFetchMoreEvent<T>) {
if (state is GraphqlLoadedState && event is GraphqlFetchMoreEvent<T>) {
yield GraphqlFetchMoreState<T>(data: state.data, result: null);
_fetchMore(event.options);
}
}
}

String parseOperationException(OperationException error) {
if (error.clientException != null) {
final exception = error.clientException;

if (exception is NetworkException) {
return 'Failed to connect to ${exception.uri}';
} else {
return exception.toString();
}
}

if (error.graphqlErrors != null && error.graphqlErrors.isNotEmpty) {
final errors = error.graphqlErrors;

return errors.first.message;
}

return 'Unknown error';
}
61 changes: 46 additions & 15 deletions examples/flutter_bloc/lib/extended_bloc/graphql/event.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,63 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:graphql/client.dart';
import 'package:meta/meta.dart';

part 'event.freezed.dart';

abstract class GraphqlEvent<T> {}

class GraphqlErrorEvent<T> extends GraphqlEvent<T> {
final OperationException error;
final QueryResult result;
@freezed
abstract class GraphqlErrorEvent<T> extends GraphqlEvent<T>
implements _$GraphqlErrorEvent<T> {
GraphqlErrorEvent._();

factory GraphqlErrorEvent({
@required OperationException error,
@required QueryResult result,
}) = _GraphqlErrorEvent<T>;
}

@freezed
abstract class GraphqlRunQueryEvent<T> extends GraphqlEvent<T>
implements _$GraphqlRunQueryEvent<T> {
GraphqlRunQueryEvent._();

GraphqlErrorEvent({@required this.error, @required this.result});
factory GraphqlRunQueryEvent() = _GraphqlRunQueryEvent<T>;
}

class GraphqlLoadingEvent<T> extends GraphqlEvent<T> {
final QueryResult result;
@freezed
abstract class GraphqlLoadingEvent<T> extends GraphqlEvent<T>
implements _$GraphqlLoadingEvent<T> {
GraphqlLoadingEvent._();

GraphqlLoadingEvent({@required this.result});
factory GraphqlLoadingEvent({
@required QueryResult result,
}) = _GraphqlLoadingEvent<T>;
}

class GraphqlLoadedEvent<T> extends GraphqlEvent<T> {
final T data;
final QueryResult result;
@freezed
abstract class GraphqlLoadedEvent<T> extends GraphqlEvent<T>
implements _$GraphqlLoadedEvent<T> {
GraphqlLoadedEvent._();

GraphqlLoadedEvent({@required this.data, @required this.result});
factory GraphqlLoadedEvent({@required T data, @required QueryResult result}) =
_GraphqlLoadedEvent<T>;
}

class GraphqlRefetchEvent<T> extends GraphqlEvent<T> {}
@freezed
abstract class GraphqlRefetchEvent<T> extends GraphqlEvent<T>
implements _$GraphqlRefetchEvent<T> {
GraphqlRefetchEvent._();

factory GraphqlRefetchEvent() = _GraphqlRefetchEvent<T>;
}

class GraphqlFetchMoreEvent<T> extends GraphqlEvent<T> {
final FetchMoreOptions options;
@freezed
abstract class GraphqlFetchMoreEvent<T> extends GraphqlEvent<T>
implements _$GraphqlFetchMoreEvent<T> {
GraphqlFetchMoreEvent._();

GraphqlFetchMoreEvent({@required this.options});
factory GraphqlFetchMoreEvent({
@required FetchMoreOptions options,
}) = _GraphqlFetchMoreEvent<T>;
}
Loading

0 comments on commit db8efe4

Please sign in to comment.