| @@ -0,0 +1,9 @@ | |||
| { | |||
| "id": 1, | |||
| "name": "Rau muống", | |||
| "sku": "SKURAUMUONG", | |||
| "manufacturer": "Bộ nông nghiệp", | |||
| "unit": "g", | |||
| "tbSuppliesTypeId": 1, | |||
| "tbCustomerId": 1 | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/models/Supply.dart'; | |||
| import 'package:farm_tpf/models/account.dart'; | |||
| import 'package:farm_tpf/models/index.dart'; | |||
| import 'package:farm_tpf/models/password.dart'; | |||
| import 'package:farm_tpf/models/user.dart'; | |||
| import 'package:farm_tpf/models/user_request.dart'; | |||
| @@ -29,4 +31,7 @@ abstract class RestClient { | |||
| @PUT("/api/update-my-profile") | |||
| Future<Account> updateProfile(@Body() Account account); | |||
| @GET("/api/tb-supplies-by-type/{type}") | |||
| Future<List<Supply>> getSupplies(@Path() String type); | |||
| } | |||
| @@ -126,4 +126,25 @@ class _RestClient implements RestClient { | |||
| final value = Account.fromJson(_result.data); | |||
| return value; | |||
| } | |||
| @override | |||
| getSupplies(type) async { | |||
| ArgumentError.checkNotNull(type, 'type'); | |||
| const _extra = <String, dynamic>{}; | |||
| final queryParameters = <String, dynamic>{}; | |||
| final _data = <String, dynamic>{}; | |||
| final Response<List<dynamic>> _result = await _dio.request( | |||
| '/api/tb-supplies-by-type/$type', | |||
| queryParameters: queryParameters, | |||
| options: RequestOptions( | |||
| method: 'GET', | |||
| headers: <String, dynamic>{}, | |||
| extra: _extra, | |||
| baseUrl: baseUrl), | |||
| data: _data); | |||
| var value = _result.data | |||
| .map((dynamic i) => Supply.fromJson(i as Map<String, dynamic>)) | |||
| .toList(); | |||
| return value; | |||
| } | |||
| } | |||
| @@ -2,6 +2,8 @@ import 'package:farm_tpf/data/api/dio_provider.dart'; | |||
| import 'package:farm_tpf/data/api/rest_client.dart'; | |||
| import 'package:farm_tpf/models/PagedResult.dart'; | |||
| import 'package:farm_tpf/models/Plot.dart'; | |||
| import 'package:farm_tpf/models/Supply.dart'; | |||
| import 'package:farm_tpf/models/index.dart'; | |||
| import 'package:farm_tpf/models/user.dart'; | |||
| import 'package:farm_tpf/models/user_request.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| @@ -25,6 +27,11 @@ class Repository { | |||
| return value; | |||
| } | |||
| Future<List<Supply>> getSupplies(String type) async { | |||
| final client = RestClient(dio); | |||
| return client.getSupplies(type); | |||
| } | |||
| Object getInstanceClass() { | |||
| var instanceClass; | |||
| if (1 == 1) { | |||
| @@ -18,5 +18,5 @@ Map<String, dynamic> _$PlotToJson(Plot instance) => <String, dynamic>{ | |||
| 'id': instance.id, | |||
| 'times': instance.times, | |||
| 'activityExecuteDate': instance.activityExecuteDate, | |||
| 'isExceedLimit': instance.isExceedLimit | |||
| 'isExceedLimit': instance.isExceedLimit, | |||
| }; | |||
| @@ -19,5 +19,5 @@ Map<String, dynamic> _$ResourceHelperToJson(ResourceHelper instance) => | |||
| 'id': instance.id, | |||
| 'name': instance.name, | |||
| 'description': instance.description, | |||
| 'isSelected': instance.isSelected | |||
| 'isSelected': instance.isSelected, | |||
| }; | |||
| @@ -0,0 +1,20 @@ | |||
| import 'package:json_annotation/json_annotation.dart'; | |||
| part 'Supply.g.dart'; | |||
| @JsonSerializable() | |||
| class Supply { | |||
| Supply(); | |||
| num id; | |||
| String name; | |||
| String sku; | |||
| String manufacturer; | |||
| String unit; | |||
| num tbSuppliesTypeId; | |||
| num tbCustomerId; | |||
| bool isSelected = false; | |||
| factory Supply.fromJson(Map<String, dynamic> json) => _$SupplyFromJson(json); | |||
| Map<String, dynamic> toJson() => _$SupplyToJson(this); | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| // GENERATED CODE - DO NOT MODIFY BY HAND | |||
| part of 'Supply.dart'; | |||
| // ************************************************************************** | |||
| // JsonSerializableGenerator | |||
| // ************************************************************************** | |||
| Supply _$SupplyFromJson(Map<String, dynamic> json) { | |||
| return Supply() | |||
| ..id = json['id'] as num | |||
| ..name = json['name'] as String | |||
| ..sku = json['sku'] as String | |||
| ..manufacturer = json['manufacturer'] as String | |||
| ..unit = json['unit'] as String | |||
| ..tbSuppliesTypeId = json['tbSuppliesTypeId'] as num | |||
| ..tbCustomerId = json['tbCustomerId'] as num | |||
| ..isSelected = false; | |||
| } | |||
| Map<String, dynamic> _$SupplyToJson(Supply instance) => <String, dynamic>{ | |||
| 'id': instance.id, | |||
| 'name': instance.name, | |||
| 'sku': instance.sku, | |||
| 'manufacturer': instance.manufacturer, | |||
| 'unit': instance.unit, | |||
| 'tbSuppliesTypeId': instance.tbSuppliesTypeId, | |||
| 'tbCustomerId': instance.tbCustomerId, | |||
| }; | |||
| @@ -1,2 +1,2 @@ | |||
| export 'Plot.dart' ; | |||
| export 'ResourceHelper.dart' ; | |||
| export 'Plot.dart'; | |||
| export 'ResourceHelper.dart'; | |||
| @@ -3,6 +3,7 @@ import 'package:farm_tpf/presentation/screens/actions/plant/sc_plant.dart'; | |||
| import 'package:farm_tpf/presentation/screens/plot/sc_plot.dart'; | |||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | |||
| import 'package:farm_tpf/presentation/screens/resources/sc_resource_helper.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| class HomePage extends StatelessWidget { | |||
| @@ -49,8 +50,9 @@ class HomePage extends StatelessWidget { | |||
| Navigator.of(context) | |||
| .push(MaterialPageRoute( | |||
| builder: (_) => ResourceHelperScreen( | |||
| resourceName: "test", | |||
| selectedPlotId: 1531, | |||
| titleName: "phân bón", | |||
| type: ConstCommon.supplyTypeDung, | |||
| selectedId: 3, | |||
| ), | |||
| fullscreenDialog: false)) | |||
| .then((value) { | |||
| @@ -0,0 +1,49 @@ | |||
| import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/models/Supply.dart'; | |||
| import 'package:farm_tpf/utils/bloc/infinity_scroll_bloc.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'supply_event.dart'; | |||
| part 'supply_state.dart'; | |||
| class SupplyBloc extends Bloc<SupplyEvent, SupplyState> { | |||
| final Repository repository; | |||
| SupplyBloc({@required this.repository}) : super(SupplyInitial()); | |||
| @override | |||
| Stream<SupplyState> mapEventToState( | |||
| SupplyEvent event, | |||
| ) async* { | |||
| if (event is DataFetched) { | |||
| try { | |||
| final response = await repository.getSupplies(event.type); | |||
| List<Supply> supplies = response.map((supply) { | |||
| if (supply.id == event.selectedId) { | |||
| supply.isSelected = true; | |||
| } | |||
| return supply; | |||
| }).toList(); | |||
| yield SupplySuccess(items: supplies); | |||
| } catch (_) { | |||
| yield SupplyFailure(); | |||
| } | |||
| } else if (event is OnRefresh) { | |||
| try { | |||
| final response = await repository.getSupplies(event.type); | |||
| List<Supply> supplies = response.map((supply) { | |||
| if (supply.id == event.selectedId) { | |||
| supply.isSelected = true; | |||
| } | |||
| return supply; | |||
| }).toList(); | |||
| yield SupplySuccess(items: supplies); | |||
| } catch (_) { | |||
| yield SupplyFailure(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| part of 'supply_bloc.dart'; | |||
| abstract class SupplyEvent extends Equatable { | |||
| const SupplyEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class DataFetched extends SupplyEvent { | |||
| final int selectedId; | |||
| final String type; | |||
| DataFetched({this.selectedId, @required this.type}); | |||
| } | |||
| class OnRefresh extends SupplyEvent { | |||
| final int selectedId; | |||
| final String type; | |||
| OnRefresh({this.selectedId, @required this.type}); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| part of 'supply_bloc.dart'; | |||
| abstract class SupplyState extends Equatable { | |||
| const SupplyState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class SupplyInitial extends SupplyState {} | |||
| class SupplyFailure extends SupplyState {} | |||
| class SupplySuccess<T> extends SupplyState { | |||
| final List<T> items; | |||
| const SupplySuccess({this.items}); | |||
| SupplySuccess copyWith({List<T> items}) { | |||
| return SupplySuccess(items: items ?? this.items); | |||
| } | |||
| @override | |||
| List<Object> get props => [items]; | |||
| } | |||
| @@ -1,17 +1,21 @@ | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/models/Supply.dart'; | |||
| import 'package:farm_tpf/models/index.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:farm_tpf/presentation/screens/resources/bloc/supply_bloc.dart'; | |||
| import 'package:farm_tpf/utils/const_string.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| class ResourceHelperScreen extends StatefulWidget { | |||
| final String resourceName; | |||
| final int selectedPlotId; | |||
| final String type; | |||
| final int selectedId; | |||
| final String titleName; | |||
| ResourceHelperScreen( | |||
| {@required this.resourceName, @required this.selectedPlotId}); | |||
| {@required this.type, | |||
| @required this.selectedId, | |||
| @required this.titleName}); | |||
| @override | |||
| _ResourceHelperScreenState createState() => _ResourceHelperScreenState(); | |||
| } | |||
| @@ -20,89 +24,79 @@ class _ResourceHelperScreenState extends State<ResourceHelperScreen> { | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocProvider( | |||
| create: (context) => | |||
| InfinityScrollBloc(repository: Repository())..add(DataFetched()), | |||
| create: (context) => SupplyBloc(repository: Repository()) | |||
| ..add(DataFetched(type: widget.type, selectedId: widget.selectedId)), | |||
| child: HoldInfinityWidget( | |||
| selectedPlotId: widget.selectedPlotId, | |||
| selectedId: widget.selectedId, | |||
| type: widget.type, | |||
| titleName: widget.titleName, | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| class HoldInfinityWidget extends StatelessWidget { | |||
| final int selectedPlotId; | |||
| HoldInfinityWidget({@required this.selectedPlotId}); | |||
| final int selectedId; | |||
| final String type; | |||
| final String titleName; | |||
| HoldInfinityWidget( | |||
| {@required this.selectedId, | |||
| @required this.type, | |||
| @required this.titleName}); | |||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| key: _scaffoldKey, | |||
| appBar: AppBar(title: Text("Chọn danh sách")), | |||
| appBar: AppBar(title: Text("Chọn $titleName")), | |||
| body: InfinityView( | |||
| selectedPlotId: selectedPlotId, | |||
| selectedId: selectedId, | |||
| type: type, | |||
| )); | |||
| } | |||
| } | |||
| class InfinityView extends StatefulWidget { | |||
| final int selectedPlotId; | |||
| InfinityView({@required this.selectedPlotId}); | |||
| final int selectedId; | |||
| final String type; | |||
| InfinityView({@required this.selectedId, @required this.type}); | |||
| @override | |||
| _InfinityViewState createState() => _InfinityViewState(); | |||
| } | |||
| class _InfinityViewState extends State<InfinityView> { | |||
| final _scrollController = ScrollController(); | |||
| final _scrollThreshold = 250.0; | |||
| InfinityScrollBloc _infinityScrollBloc; | |||
| SupplyBloc _supplyBloc; | |||
| @override | |||
| void initState() { | |||
| _scrollController.addListener(() { | |||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||
| final currentScroll = _scrollController.position.pixels; | |||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||
| _infinityScrollBloc.add(DataFetched()); | |||
| } | |||
| }); | |||
| _infinityScrollBloc = BlocProvider.of<InfinityScrollBloc>(context); | |||
| _supplyBloc = BlocProvider.of<SupplyBloc>(context); | |||
| _supplyBloc | |||
| .add(DataFetched(type: widget.type, selectedId: widget.selectedId)); | |||
| super.initState(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return BlocBuilder<InfinityScrollBloc, InfinityScrollState>( | |||
| return BlocBuilder<SupplyBloc, SupplyState>( | |||
| builder: (context, state) { | |||
| if (state is InfinityScrollFailure) { | |||
| if (state is SupplyFailure) { | |||
| return Center(child: Text(label_error_get_data)); | |||
| } | |||
| if (state is InfinityScrollSuccess) { | |||
| if (state is SupplySuccess) { | |||
| if (state.items.isEmpty) { | |||
| return Center(child: Text(label_list_empty)); | |||
| } | |||
| Plot selectedPlot; | |||
| List<Plot> plots = state.items.map((e) => e as Plot).toList(); | |||
| plots.forEach((plot) { | |||
| plot.id == widget.selectedPlotId ? selectedPlot = plot : Plot(); | |||
| }); | |||
| return RefreshIndicator( | |||
| child: ListView.builder( | |||
| itemBuilder: (BuildContext context, int index) { | |||
| return index >= state.items.length | |||
| ? BottomLoader() | |||
| : ItemInfinityWidget( | |||
| item: state.items[index], | |||
| selectedPlot: selectedPlot, | |||
| ); | |||
| }, | |||
| itemCount: state.hasReachedMax | |||
| ? state.items.length | |||
| : state.items.length + 1, | |||
| controller: _scrollController, | |||
| ), | |||
| itemBuilder: (BuildContext context, int index) { | |||
| return index >= state.items.length | |||
| ? BottomLoader() | |||
| : ItemInfinityWidget(item: state.items[index]); | |||
| }, | |||
| itemCount: state.items.length), | |||
| onRefresh: () async { | |||
| _infinityScrollBloc.add(OnRefresh()); | |||
| _supplyBloc.add(OnRefresh( | |||
| type: widget.type, selectedId: widget.selectedId)); | |||
| }); | |||
| } | |||
| return Center( | |||
| @@ -114,34 +108,30 @@ class _InfinityViewState extends State<InfinityView> { | |||
| @override | |||
| void dispose() { | |||
| _scrollController.dispose(); | |||
| super.dispose(); | |||
| } | |||
| } | |||
| class ItemInfinityWidget extends StatelessWidget { | |||
| final Plot item; | |||
| final Plot selectedPlot; | |||
| final Supply item; | |||
| const ItemInfinityWidget({Key key, @required this.item, this.selectedPlot}) | |||
| : super(key: key); | |||
| const ItemInfinityWidget({Key key, @required this.item}) : super(key: key); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return GestureDetector( | |||
| child: Card( | |||
| color: item.id % 3 == 0 ? Colors.white : Colors.greenAccent[100], | |||
| child: Material( | |||
| child: RadioListTile( | |||
| title: Text(item.activityExecuteDate.toString()), | |||
| subtitle: Text(item.id.toString()), | |||
| value: item, | |||
| groupValue: selectedPlot, | |||
| onChanged: (Plot value) { | |||
| print("selected value: ${value.id}"); | |||
| Navigator.of(context).pop(value.id); | |||
| }), | |||
| )), | |||
| child: RadioListTile( | |||
| title: Text(item.name.toString()), | |||
| subtitle: Text(item.manufacturer.toString()), | |||
| value: item, | |||
| groupValue: item.isSelected ? item : null, | |||
| onChanged: (Supply value) { | |||
| print("selected value: ${value.id}"); | |||
| Navigator.of(context).pop(value.id); | |||
| }), | |||
| )), | |||
| onTap: () {}); | |||
| } | |||
| } | |||
| @@ -1,4 +1,9 @@ | |||
| class ConstCommon { | |||
| static int kExpiredTime = 12 * 60 * 60 * 1000; //24h | |||
| static const String baseUrl = "https://aquaman.aztrace.vn"; | |||
| static const String baseUrl = "https://ironman.aztrace.vn"; | |||
| static const String supplyTypeSeed = "GIONG"; | |||
| static const String supplyTypeDung = "PHANBON"; | |||
| static const String supplyTypeSubStrate = "GIATHE"; | |||
| static const String supplyTypeProtectPlant = "THUOCBVTV"; | |||
| } | |||