| @@ -38,4 +38,7 @@ abstract class RestClient { | |||
| //Common | |||
| @PUT("/api/update-fcmToken") | |||
| Future<void> updateFcmToken(@Body() String token); | |||
| @GET("/api/tb-crops?page={page}&size={size}") | |||
| Future<List<Plot>> getPlots({@Path() int page = 0, @Path() int size = 20}); | |||
| } | |||
| @@ -9,7 +9,7 @@ part of 'rest_client.dart'; | |||
| class _RestClient implements RestClient { | |||
| _RestClient(this._dio, {this.baseUrl}) { | |||
| ArgumentError.checkNotNull(_dio, '_dio'); | |||
| this.baseUrl ??= ConstCommon.baseUrl; | |||
| this.baseUrl ??= 'https://ironman.aztrace.vn'; | |||
| } | |||
| final Dio _dio; | |||
| @@ -164,4 +164,25 @@ class _RestClient implements RestClient { | |||
| data: _data); | |||
| return null; | |||
| } | |||
| @override | |||
| getPlots({page = 0, size = 20}) async { | |||
| const _extra = <String, dynamic>{}; | |||
| final queryParameters = <String, dynamic>{}; | |||
| queryParameters.removeWhere((k, v) => v == null); | |||
| final _data = <String, dynamic>{}; | |||
| final Response<List<dynamic>> _result = await _dio.request( | |||
| '/api/tb-crops?page=$page&size=$size', | |||
| queryParameters: queryParameters, | |||
| options: RequestOptions( | |||
| method: 'GET', | |||
| headers: <String, dynamic>{}, | |||
| extra: _extra, | |||
| baseUrl: baseUrl), | |||
| data: _data); | |||
| var value = _result.data | |||
| .map((dynamic i) => Plot.fromJson(i as Map<String, dynamic>)) | |||
| .toList(); | |||
| return value; | |||
| } | |||
| } | |||
| @@ -11,6 +11,11 @@ import 'package:farm_tpf/utils/const_common.dart'; | |||
| class Repository { | |||
| final dio = DioProvider.instance(); | |||
| Future<List<Plot>> getPlots({int page, int size}) { | |||
| final client = RestClient(dio); | |||
| return client.getPlots(page: page, size: size); | |||
| } | |||
| Future<User> signInWithCredentials(String username, String password) { | |||
| final client = RestClient(dio); | |||
| return client.login(UserRequest(username: username, password: password)); | |||
| @@ -11,6 +11,6 @@ class Plot { | |||
| String activityExecuteDate; | |||
| bool isExceedLimit; | |||
| Plot fromJson(Map<String, dynamic> json) => _$PlotFromJson(json); | |||
| factory Plot.fromJson(Map<String, dynamic> json) => _$PlotFromJson(json); | |||
| Map<String, dynamic> toJson() => _$PlotToJson(this); | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/data/api/app_exception.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'plot_event.dart'; | |||
| part 'plot_state.dart'; | |||
| class PlotBloc extends Bloc<PlotEvent, PlotState> { | |||
| final Repository repository; | |||
| PlotBloc({@required this.repository}) : super(PlotInitial()); | |||
| static int pageSize = 4; | |||
| @override | |||
| Stream<PlotState> mapEventToState( | |||
| PlotEvent event, | |||
| ) async* { | |||
| if (event is DataFetched && | |||
| !(state is PlotSuccess && (state as PlotSuccess).hasReachedMax)) { | |||
| try { | |||
| if (state is PlotInitial) { | |||
| final response = await repository.getPlots(page: 0, size: pageSize); | |||
| yield PlotSuccess( | |||
| items: response, | |||
| page: 0, | |||
| hasReachedMax: response.length < pageSize ? true : false); | |||
| } | |||
| if (state is PlotSuccess) { | |||
| final currentState = state as PlotSuccess; | |||
| int page = currentState.page + 1; | |||
| final response = | |||
| await repository.getPlots(page: page, size: pageSize); | |||
| yield response.isEmpty | |||
| ? currentState.copyWith(hasReachedMax: true) | |||
| : PlotSuccess( | |||
| items: currentState.items + response, | |||
| page: currentState.page + 1, | |||
| hasReachedMax: false); | |||
| } | |||
| } catch (e) { | |||
| var errorString = AppException.handleError(e); | |||
| yield PlotFailure(errorString: errorString); | |||
| } | |||
| } | |||
| if (event is OnRefresh) { | |||
| try { | |||
| final response = await repository.getPlots(page: 0, size: pageSize); | |||
| yield PlotSuccess( | |||
| items: response, | |||
| page: 0, | |||
| hasReachedMax: response.length < pageSize ? true : false); | |||
| } catch (e) { | |||
| yield PlotFailure(errorString: AppException.handleError(e)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| part of 'plot_bloc.dart'; | |||
| abstract class PlotEvent extends Equatable { | |||
| const PlotEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class DataFetched extends PlotEvent {} | |||
| class OnRefresh extends PlotEvent {} | |||
| @@ -0,0 +1,33 @@ | |||
| part of 'plot_bloc.dart'; | |||
| abstract class PlotState extends Equatable { | |||
| const PlotState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class PlotInitial extends PlotState {} | |||
| class PlotFailure extends PlotState { | |||
| final String errorString; | |||
| PlotFailure({@required this.errorString}); | |||
| } | |||
| class PlotSuccess<T> extends PlotState { | |||
| final List<T> items; | |||
| final int page; | |||
| final bool hasReachedMax; | |||
| const PlotSuccess({this.items, this.page, this.hasReachedMax}); | |||
| PlotSuccess copyWith({List<T> items, int page, bool hasReachedMax}) { | |||
| return PlotSuccess( | |||
| items: items ?? this.items, | |||
| page: page ?? this.page, | |||
| hasReachedMax: hasReachedMax ?? this.hasReachedMax); | |||
| } | |||
| @override | |||
| List<Object> get props => [items, hasReachedMax]; | |||
| } | |||
| @@ -3,12 +3,13 @@ import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/models/Plot.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | |||
| import 'package:farm_tpf/utils/bloc/infinity_scroll_bloc.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'package:farm_tpf/utils/const_string.dart'; | |||
| import 'package:font_awesome_flutter/font_awesome_flutter.dart'; | |||
| import 'bloc/plot_bloc.dart'; | |||
| class PlotListScreen extends StatefulWidget { | |||
| @override | |||
| _PlotListScreenState createState() => _PlotListScreenState(); | |||
| @@ -19,7 +20,7 @@ class _PlotListScreenState extends State<PlotListScreen> { | |||
| Widget build(BuildContext context) { | |||
| return BlocProvider( | |||
| create: (context) => | |||
| InfinityScrollBloc(repository: Repository())..add(DataFetched()), | |||
| PlotBloc(repository: Repository())..add(DataFetched()), | |||
| child: HoldInfinityWidget(), | |||
| ); | |||
| } | |||
| @@ -50,7 +51,7 @@ class InfinityView extends StatefulWidget { | |||
| class _InfinityViewState extends State<InfinityView> { | |||
| final _scrollController = ScrollController(); | |||
| final _scrollThreshold = 250.0; | |||
| InfinityScrollBloc _infinityScrollBloc; | |||
| PlotBloc _plotBloc; | |||
| @override | |||
| void initState() { | |||
| @@ -58,21 +59,21 @@ class _InfinityViewState extends State<InfinityView> { | |||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||
| final currentScroll = _scrollController.position.pixels; | |||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||
| _infinityScrollBloc.add(DataFetched()); | |||
| _plotBloc.add(DataFetched()); | |||
| } | |||
| }); | |||
| _infinityScrollBloc = BlocProvider.of<InfinityScrollBloc>(context); | |||
| _plotBloc = BlocProvider.of<PlotBloc>(context); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<InfinityScrollBloc, InfinityScrollState>( | |||
| return BlocBuilder<PlotBloc, PlotState>( | |||
| builder: (context, state) { | |||
| if (state is InfinityScrollFailure) { | |||
| if (state is PlotFailure) { | |||
| return Center(child: Text(state.errorString)); | |||
| } | |||
| if (state is InfinityScrollSuccess) { | |||
| if (state is PlotSuccess) { | |||
| if (state.items.isEmpty) { | |||
| return Center(child: Text(label_list_empty)); | |||
| } | |||
| @@ -89,7 +90,7 @@ class _InfinityViewState extends State<InfinityView> { | |||
| controller: _scrollController, | |||
| ), | |||
| onRefresh: () async { | |||
| _infinityScrollBloc.add(OnRefresh()); | |||
| _plotBloc.add(OnRefresh()); | |||
| }); | |||
| } | |||
| return Center( | |||