| 40af80373277a16a0592f58ccbdc7f07 | |||||
| eb141365fb69a4e0decadd2584d53516 |
| class EnvironmentParameter { | |||||
| int id; | |||||
| String name; | |||||
| num index; | |||||
| int activityId; | |||||
| String executeDate; | |||||
| bool status; | |||||
| EnvironmentParameter( | |||||
| {this.id, | |||||
| this.name, | |||||
| this.index, | |||||
| this.activityId, | |||||
| this.executeDate, | |||||
| this.status}); | |||||
| EnvironmentParameter.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| name = json['name']; | |||||
| index = json['index']; | |||||
| activityId = json['activityId']; | |||||
| executeDate = json['executeDate']; | |||||
| status = json['status']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['name'] = this.name; | |||||
| data['index'] = this.index; | |||||
| data['activityId'] = this.activityId; | |||||
| data['executeDate'] = this.executeDate; | |||||
| data['status'] = this.status; | |||||
| return data; | |||||
| } | |||||
| } |
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||
| import 'package:farm_tpf/custom_model/CropPlot.dart'; | import 'package:farm_tpf/custom_model/CropPlot.dart'; | ||||
| import 'package:farm_tpf/custom_model/Device.dart'; | import 'package:farm_tpf/custom_model/Device.dart'; | ||||
| import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | |||||
| import 'package:farm_tpf/custom_model/Harvest.dart'; | import 'package:farm_tpf/custom_model/Harvest.dart'; | ||||
| import 'package:farm_tpf/custom_model/WaterType.dart'; | import 'package:farm_tpf/custom_model/WaterType.dart'; | ||||
| import 'package:farm_tpf/custom_model/account.dart'; | import 'package:farm_tpf/custom_model/account.dart'; | ||||
| //Device | //Device | ||||
| @GET("/api/listDeviceOfUserCustomers") | @GET("/api/listDeviceOfUserCustomers") | ||||
| Future<List<Device>> getDevices(); | Future<List<Device>> getDevices(); | ||||
| //Get environment parameter | |||||
| @GET("/api/list-environment-updates-display/{cropId}?page={page}&size={size}") | |||||
| Future<List<EnvironmentParameter>> getEnvironmentParameters( | |||||
| @Path() int cropId, | |||||
| {@Path() int page = 0, | |||||
| @Path() int size = 20}); | |||||
| } | } |
| return value; | return value; | ||||
| } | } | ||||
| @override | |||||
| getEnvironmentParameters(cropId, {page = 0, size = 20}) async { | |||||
| ArgumentError.checkNotNull(cropId, 'cropId'); | |||||
| 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/list-environment-updates-display/$cropId?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) => | |||||
| EnvironmentParameter.fromJson(i as Map<String, dynamic>)) | |||||
| .toList(); | |||||
| return value; | |||||
| } | |||||
| RequestOptions newRequestOptions(Options options) { | RequestOptions newRequestOptions(Options options) { | ||||
| if (options is RequestOptions) { | if (options is RequestOptions) { | ||||
| return options; | return options; |
| import 'package:dio_http_cache/dio_http_cache.dart'; | import 'package:dio_http_cache/dio_http_cache.dart'; | ||||
| import 'package:farm_tpf/custom_model/CropPlot.dart'; | import 'package:farm_tpf/custom_model/CropPlot.dart'; | ||||
| import 'package:farm_tpf/custom_model/Device.dart'; | import 'package:farm_tpf/custom_model/Device.dart'; | ||||
| import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | |||||
| import 'package:farm_tpf/custom_model/Harvest.dart'; | import 'package:farm_tpf/custom_model/Harvest.dart'; | ||||
| import 'package:farm_tpf/custom_model/WaterType.dart'; | import 'package:farm_tpf/custom_model/WaterType.dart'; | ||||
| import 'package:farm_tpf/custom_model/user.dart'; | import 'package:farm_tpf/custom_model/user.dart'; | ||||
| final client = RestClient(dio); | final client = RestClient(dio); | ||||
| return client.getDevices(); | return client.getDevices(); | ||||
| } | } | ||||
| //Environment Parameter | |||||
| Future<List<EnvironmentParameter>> getEnvironmentParameters( | |||||
| {@required int cropId, int page, int size}) { | |||||
| final client = RestClient(dio); | |||||
| return client.getEnvironmentParameters(cropId, page: page, size: size); | |||||
| } | |||||
| } | } |
| if (state is PlotSuccess) { | if (state is PlotSuccess) { | ||||
| final currentState = state as PlotSuccess; | final currentState = state as PlotSuccess; | ||||
| int page = currentState.page + 1; | int page = currentState.page + 1; | ||||
| yield PlotLoading(); | |||||
| final response = | final response = | ||||
| await repository.getPlots(page: page, size: pageSize); | await repository.getPlots(page: page, size: pageSize); | ||||
| yield response.isEmpty | yield response.isEmpty |
| MaterialPageRoute( | MaterialPageRoute( | ||||
| builder: (BuildContext context) => PlotDetailScreen( | builder: (BuildContext context) => PlotDetailScreen( | ||||
| cropId: item.id, | cropId: item.id, | ||||
| initialIndex: 1, | |||||
| initialIndex: 0, | |||||
| ))); | ))); | ||||
| }); | }); | ||||
| } | } |
| 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_parameter_event.dart'; | |||||
| part 'plot_parameter_state.dart'; | |||||
| class PlotParameterBloc extends Bloc<PlotParameterEvent, PlotParameterState> { | |||||
| final Repository repository; | |||||
| PlotParameterBloc({@required this.repository}) | |||||
| : super(PlotParameterInitial()); | |||||
| static int pageSize = 20; | |||||
| @override | |||||
| Stream<PlotParameterState> mapEventToState( | |||||
| PlotParameterEvent event, | |||||
| ) async* { | |||||
| if (event is DataFetched && | |||||
| !(state is PlotParameterSuccess && | |||||
| (state as PlotParameterSuccess).hasReachedMax)) { | |||||
| try { | |||||
| if (state is PlotParameterInitial) { | |||||
| yield PlotParameterLoading(); | |||||
| final response = await repository.getEnvironmentParameters( | |||||
| cropId: event.cropId, page: 0, size: pageSize); | |||||
| yield PlotParameterSuccess( | |||||
| items: response, | |||||
| page: 0, | |||||
| hasReachedMax: response.length < pageSize ? true : false); | |||||
| } | |||||
| //TODO: check paging api | |||||
| // if (state is PlotParameterSuccess) { | |||||
| // final currentState = state as PlotParameterSuccess; | |||||
| // int page = currentState.page + 1; | |||||
| // final response = await repository.getEnvironmentParameters( | |||||
| // cropId: event.cropId, page: page, size: pageSize); | |||||
| // yield response.isEmpty | |||||
| // ? currentState.copyWith(hasReachedMax: true) | |||||
| // : PlotParameterSuccess( | |||||
| // items: currentState.items + response, | |||||
| // page: currentState.page + 1, | |||||
| // hasReachedMax: false); | |||||
| // } | |||||
| } catch (e) { | |||||
| var errorString = AppException.handleError(e); | |||||
| yield PlotParameterFailure(errorString: errorString); | |||||
| } | |||||
| } | |||||
| if (event is OnRefresh) { | |||||
| try { | |||||
| yield PlotParameterLoading(); | |||||
| final response = await repository.getEnvironmentParameters( | |||||
| cropId: event.cropId, page: 0, size: pageSize); | |||||
| yield PlotParameterSuccess( | |||||
| items: response, | |||||
| page: 0, | |||||
| hasReachedMax: response.length < pageSize ? true : false); | |||||
| } catch (e) { | |||||
| yield PlotParameterFailure(errorString: AppException.handleError(e)); | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| part of 'plot_parameter_bloc.dart'; | |||||
| abstract class PlotParameterEvent extends Equatable { | |||||
| const PlotParameterEvent(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class DataFetched extends PlotParameterEvent { | |||||
| final int cropId; | |||||
| DataFetched({@required this.cropId}); | |||||
| } | |||||
| class OnRefresh extends PlotParameterEvent { | |||||
| final int cropId; | |||||
| OnRefresh({@required this.cropId}); | |||||
| } |
| part of 'plot_parameter_bloc.dart'; | |||||
| abstract class PlotParameterState extends Equatable { | |||||
| const PlotParameterState(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class PlotParameterInitial extends PlotParameterState {} | |||||
| class PlotParameterLoading extends PlotParameterState {} | |||||
| class PlotParameterFailure extends PlotParameterState { | |||||
| final String errorString; | |||||
| PlotParameterFailure({@required this.errorString}); | |||||
| } | |||||
| class PlotParameterSuccess<T> extends PlotParameterState { | |||||
| final List<T> items; | |||||
| final int page; | |||||
| final bool hasReachedMax; | |||||
| const PlotParameterSuccess({this.items, this.page, this.hasReachedMax}); | |||||
| PlotParameterSuccess copyWith({List<T> items, int page, bool hasReachedMax}) { | |||||
| return PlotParameterSuccess( | |||||
| items: items ?? this.items, | |||||
| page: page ?? this.page, | |||||
| hasReachedMax: hasReachedMax ?? this.hasReachedMax); | |||||
| } | |||||
| @override | |||||
| List<Object> get props => [items, hasReachedMax]; | |||||
| } |
| backgroundColor: COLOR_CONST.ITEM_BG, | backgroundColor: COLOR_CONST.ITEM_BG, | ||||
| body: TabBarView( | body: TabBarView( | ||||
| children: [ | children: [ | ||||
| PlotParameterScreen(), | |||||
| PlotParameterScreen( | |||||
| cropId: widget.cropId, | |||||
| ), | |||||
| PlotActionScreen( | PlotActionScreen( | ||||
| cropId: widget.cropId, | cropId: widget.cropId, | ||||
| cropCode: widget.cropCode, | cropCode: widget.cropCode, |
| import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | |||||
| import 'package:farm_tpf/data/repository/repository.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/const_string.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
| import 'package:farm_tpf/utils/formatter.dart'; | |||||
| import 'bloc/plot_parameter_bloc.dart'; | |||||
| class PlotParameterScreen extends StatefulWidget { | class PlotParameterScreen extends StatefulWidget { | ||||
| final int cropId; | |||||
| PlotParameterScreen({@required this.cropId}); | |||||
| @override | @override | ||||
| _PlotParameterScreenState createState() => _PlotParameterScreenState(); | _PlotParameterScreenState createState() => _PlotParameterScreenState(); | ||||
| } | } | ||||
| class _PlotParameterScreenState extends State<PlotParameterScreen> { | class _PlotParameterScreenState extends State<PlotParameterScreen> { | ||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| return Container(); | |||||
| return BlocProvider( | |||||
| create: (context) => PlotParameterBloc(repository: Repository()) | |||||
| ..add(DataFetched(cropId: widget.cropId)), | |||||
| child: HoldInfinityWidget( | |||||
| cropId: widget.cropId, | |||||
| ), | |||||
| ); | |||||
| } | |||||
| } | |||||
| class HoldInfinityWidget extends StatelessWidget { | |||||
| final int cropId; | |||||
| HoldInfinityWidget({@required this.cropId}); | |||||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Scaffold( | |||||
| key: _scaffoldKey, | |||||
| body: InfinityView( | |||||
| cropId: cropId, | |||||
| )); | |||||
| } | |||||
| } | |||||
| class InfinityView extends StatefulWidget { | |||||
| final int cropId; | |||||
| InfinityView({@required this.cropId}); | |||||
| @override | |||||
| _InfinityViewState createState() => _InfinityViewState(); | |||||
| } | |||||
| class _InfinityViewState extends State<InfinityView> { | |||||
| final _scrollController = ScrollController(); | |||||
| final _scrollThreshold = 250.0; | |||||
| PlotParameterBloc _plotParameterBloc; | |||||
| @override | |||||
| void initState() { | |||||
| _scrollController.addListener(() { | |||||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||||
| final currentScroll = _scrollController.position.pixels; | |||||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||||
| _plotParameterBloc.add(DataFetched(cropId: widget.cropId)); | |||||
| } | |||||
| }); | |||||
| _plotParameterBloc = BlocProvider.of<PlotParameterBloc>(context); | |||||
| super.initState(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Column( | |||||
| children: <Widget>[ | |||||
| Expanded(child: BlocBuilder<PlotParameterBloc, PlotParameterState>( | |||||
| builder: (context, state) { | |||||
| if (state is PlotParameterFailure) { | |||||
| return Center(child: Text(state.errorString)); | |||||
| } | |||||
| if (state is PlotParameterSuccess) { | |||||
| if (state.items.isEmpty) { | |||||
| return Center(child: Text(label_list_empty)); | |||||
| } | |||||
| return RefreshIndicator( | |||||
| child: ListView.builder( | |||||
| physics: AlwaysScrollableScrollPhysics(), | |||||
| itemBuilder: (BuildContext context, int index) { | |||||
| return index >= state.items.length | |||||
| ? BottomLoader() | |||||
| : ItemInfinityWidget(item: state.items[index]); | |||||
| }, | |||||
| itemCount: state.hasReachedMax | |||||
| ? state.items.length | |||||
| : state.items.length + 1, | |||||
| controller: _scrollController, | |||||
| ), | |||||
| onRefresh: () async { | |||||
| _plotParameterBloc.add(OnRefresh(cropId: widget.cropId)); | |||||
| }); | |||||
| } | |||||
| return Center( | |||||
| child: LoadingListPage(), | |||||
| ); | |||||
| }, | |||||
| )) | |||||
| ], | |||||
| ); | |||||
| } | |||||
| @override | |||||
| void dispose() { | |||||
| _scrollController.dispose(); | |||||
| super.dispose(); | |||||
| } | |||||
| } | |||||
| class ItemInfinityWidget extends StatelessWidget { | |||||
| final EnvironmentParameter item; | |||||
| const ItemInfinityWidget({Key key, @required this.item}) : super(key: key); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return Card( | |||||
| child: ListTile( | |||||
| title: Text( | |||||
| "${item.name ?? ''}", | |||||
| style: TextStyle(fontWeight: FontWeight.bold), | |||||
| ), | |||||
| subtitle: Text( | |||||
| item.index.formatNumtoStringDecimal(), | |||||
| style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold), | |||||
| ), | |||||
| trailing: Text(item.executeDate.format_DDMMYY_HHmm()), | |||||
| ), | |||||
| ); | |||||
| } | } | ||||
| } | } |