Skip to content

Instantly share code, notes, and snippets.

@pkozlovskiy
Last active May 14, 2020 20:16
Show Gist options
  • Save pkozlovskiy/bbe976d6b571890a02a91b21df105ba1 to your computer and use it in GitHub Desktop.
Save pkozlovskiy/bbe976d6b571890a02a91b21df105ba1 to your computer and use it in GitHub Desktop.
class ProjectsPage extends StatefulWidget {
static const String route = '/projects';
const ProjectsPage({Key key}) : super(key: key);
@override
_ProjectsPageState createState() => _ProjectsPageState();
}
class _ProjectsPageState extends State<ProjectsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Center(child: Text(S.of(context).projectsPageTitle))),
body: ProjectsListView(context),
);
}
}
class ProjectsListView extends StatefulWidget {
ProjectsListView(BuildContext context);
@override
_ProjectsListViewState createState() => _ProjectsListViewState();
}
class _ProjectsListViewState extends State<ProjectsListView> {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
@override
void initState() {
super.initState();
Provider.of<ProjectsBloc>(context, listen: false)
.add(FetchData(ProjectsParams()));
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Provider.of<ProjectsBloc>(context),
builder: (context, snapshot) {
final state = snapshot.data as DataState;
if (state is DataLoadingState) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => _refreshIndicatorKey.currentState.show());
}
if (state is LoadDataErrorState) {
WidgetsBinding.instance.addPostFrameCallback((_) async => {
await PlatformExceptionAlertDialog(
title: S.of(context).error,
exception: PlatformException(code: '', message: ''),
).show(context)
});
}
final List dataList = state?.lastData ?? [];
return RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh:
Provider.of<ProjectsBloc>(context, listen: false).refresh,
child: Stack(
children: <Widget>[
ListView.builder(
itemCount: dataList.length,
itemBuilder: (context, index) => GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
ProjectPage.route,
arguments: dataList[index]),
child: ProjectListItem(dataList[index])),
),
Visibility(
visible: state is DataLoadedState && dataList.isEmpty,
child: Center(
child: Text(S.of(context).emptyProjects),
),
),
],
));
},
);
}
}
class ProjectListItem extends StatelessWidget {
final Project project;
ProjectListItem(this.project);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Text('${project.title}'),
],
),
);
}
}
import 'package:bloc/bloc.dart';
import '../error/exceptions.dart';
import 'data_event.dart';
import 'data_state.dart';
import 'data_usecase.dart';
abstract class DataBloc<Type, Params extends DataParams>
extends Bloc<DataEvent, DataState> with FailureMapper {
final DataUseCase _useCase;
Type lastData;
Params lastParams;
DataBloc(this._useCase);
@override
DataState get initialState => DataInitState<Type>(lastData);
@override
Stream<DataState> mapEventToState(DataEvent event) async* {
if (event is FetchData) {
if (lastParams != event.params) {
lastData = null;
}
lastParams = event.params;
yield DataLoadingState<Type>(lastData);
final result = await _useCase(event.params);
yield result.fold(
(failure) =>
LoadDataErrorState<Type>(lastData, mapFailureToMessage(failure)),
(data) {
lastData = data;
return DataLoadedState<Type>(lastData);
});
}
}
Future<void> refresh({Params params}) async {
var dataState = await first;
if (dataState is DataLoadingState) return Future.value(null);
add(FetchData(params ?? lastParams));
return skip(1).firstWhere((state) => state is! DataLoadingState);
}
}
import 'data_usecase.dart';
abstract class DataEvent {}
class FetchData<Params extends DataParams> extends DataEvent {
final Params params;
FetchData(this.params);
}
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
@immutable
abstract class DataState<Type> extends Equatable {
final Type lastData;
DataState(this.lastData);
@override
List<Object> get props => [lastData];
@override
String toString() {
return super.toString() + 'Data = $lastData';
}
}
class DataInitState<Type> extends DataState<Type> {
DataInitState(Type lastData) : super(lastData);
}
class DataLoadingState<Type> extends DataState<Type> {
DataLoadingState(Type lastData) : super(lastData);
}
class DataLoadedState<Type> extends DataState<Type> {
DataLoadedState(Type lastData) : super(lastData);
}
class LoadDataErrorState<Type> extends DataState<Type> {
final String message;
LoadDataErrorState(Type lastData, this.message) : super(lastData);
@override
List<Object> get props => [message, lastData];
}
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import '../error/failures.dart';
import '../usecases/usecase.dart';
abstract class DataUseCase<Type, Params extends DataParams>
extends UseCase<Type, Params> {
@override
Future<Either<Failure, Type>> call(Params params) async {
return getData(params);
}
Future<Either<Failure, Type>> getData(Params params);
}
class DataParams extends Equatable {
final bool forceRefresh;
DataParams(this.forceRefresh);
@override
List get props => [forceRefresh];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment