| @@ -0,0 +1,31 @@ | |||
| class Device { | |||
| int id; | |||
| String name; | |||
| String status; | |||
| String location; | |||
| Device({this.id, this.name, this.status, this.location}); | |||
| Device.clone(Device device) { | |||
| this.id = device.id; | |||
| this.name = device.name; | |||
| this.status = device.status; | |||
| this.location = device.location; | |||
| } | |||
| Device.fromJson(Map<String, dynamic> json) { | |||
| id = json['id']; | |||
| name = json['name']; | |||
| status = json['status']; | |||
| location = json['location']; | |||
| } | |||
| Map<String, dynamic> toJson() { | |||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||
| data['id'] = this.id; | |||
| data['name'] = this.name; | |||
| data['status'] = this.status; | |||
| data['location'] = this.location; | |||
| return data; | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/custom_model/CropPlot.dart'; | |||
| import 'package:farm_tpf/custom_model/Device.dart'; | |||
| import 'package:farm_tpf/custom_model/account.dart'; | |||
| import 'package:farm_tpf/custom_model/password.dart'; | |||
| import 'package:farm_tpf/custom_model/user.dart'; | |||
| @@ -54,4 +55,7 @@ abstract class RestClient { | |||
| @PUT("/api/tb-crops") | |||
| Future<void> updateCrop(@Body() TbCropDTO crop); | |||
| //Device | |||
| @GET("/api/listDeviceOfUserCustomers") | |||
| Future<List<Device>> getDevices(); | |||
| } | |||
| @@ -243,4 +243,24 @@ class _RestClient implements RestClient { | |||
| data: _data); | |||
| return null; | |||
| } | |||
| @override | |||
| getDevices() async { | |||
| const _extra = <String, dynamic>{}; | |||
| final queryParameters = <String, dynamic>{}; | |||
| final _data = <String, dynamic>{}; | |||
| final Response<List<dynamic>> _result = await _dio.request( | |||
| '/api/listDeviceOfUserCustomers', | |||
| queryParameters: queryParameters, | |||
| options: RequestOptions( | |||
| method: 'GET', | |||
| headers: <String, dynamic>{}, | |||
| extra: _extra, | |||
| baseUrl: baseUrl), | |||
| data: _data); | |||
| var value = _result.data | |||
| .map((dynamic i) => Device.fromJson(i as Map<String, dynamic>)) | |||
| .toList(); | |||
| return value; | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ import 'dart:io'; | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/custom_model/CropPlot.dart'; | |||
| import 'package:farm_tpf/custom_model/Device.dart'; | |||
| import 'package:farm_tpf/custom_model/user.dart'; | |||
| import 'package:farm_tpf/custom_model/user_request.dart'; | |||
| import 'package:farm_tpf/data/api/app_exception.dart'; | |||
| @@ -85,4 +86,10 @@ class Repository { | |||
| onError(AppException.handleError(e)); | |||
| } | |||
| } | |||
| //Device | |||
| Future<List<Device>> getDevices() { | |||
| final client = RestClient(dio); | |||
| return client.getDevices(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| import 'dart:async'; | |||
| import 'package:bloc/bloc.dart'; | |||
| import 'package:equatable/equatable.dart'; | |||
| import 'package:farm_tpf/custom_model/Device.dart'; | |||
| import 'package:farm_tpf/data/api/app_exception.dart'; | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:meta/meta.dart'; | |||
| part 'device_event.dart'; | |||
| part 'device_state.dart'; | |||
| class DeviceBloc extends Bloc<DeviceEvent, DeviceState> { | |||
| final Repository repository; | |||
| DeviceBloc({@required this.repository}) : super(DeviceInitial()); | |||
| @override | |||
| Stream<DeviceState> mapEventToState( | |||
| DeviceEvent event, | |||
| ) async* { | |||
| if (event is OpenScreen) { | |||
| yield* _mapOpenScreenToState(); | |||
| } else if (event is ControlDevice) { | |||
| yield* _mapControlDeviceToState( | |||
| event.currentDevices, event.updatedDeviceId); | |||
| } | |||
| } | |||
| Stream<DeviceState> _mapOpenScreenToState() async* { | |||
| yield DisplayDevice.loading(); | |||
| try { | |||
| List<Device> devices = new List<Device>(); | |||
| final response = await repository.getDevices(); | |||
| devices = response; | |||
| devices.sort((a, b) => (a.id).compareTo(b.id)); | |||
| yield DisplayDevice.data(devices); | |||
| } catch (e) { | |||
| yield DisplayDevice.error(AppException.handleError(e)); | |||
| } | |||
| } | |||
| Stream<DeviceState> _mapControlDeviceToState( | |||
| List<Device> currentDevices, int updatedDeviceId) async* { | |||
| List<Device> devices = new List<Device>(); | |||
| currentDevices.forEach((device) { | |||
| if (device.id == updatedDeviceId) { | |||
| var updatedStatus = device.status == "1" ? "0" : "1"; | |||
| device.status = updatedStatus; | |||
| } | |||
| devices.add(Device.clone(device)); | |||
| }); | |||
| yield DisplayDevice.data(devices); | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| part of 'device_bloc.dart'; | |||
| abstract class DeviceEvent extends Equatable { | |||
| const DeviceEvent(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class OpenScreen extends DeviceEvent { | |||
| OpenScreen(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class ControlDevice extends DeviceEvent { | |||
| final List<Device> currentDevices; | |||
| final int updatedDeviceId; | |||
| ControlDevice( | |||
| {@required this.currentDevices, @required this.updatedDeviceId}); | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| part of 'device_bloc.dart'; | |||
| abstract class DeviceState extends Equatable { | |||
| const DeviceState(); | |||
| @override | |||
| List<Object> get props => []; | |||
| } | |||
| class DeviceInitial extends DeviceState {} | |||
| class DisplayDevice extends DeviceState { | |||
| List<Device> devices; | |||
| bool loading; | |||
| String msg; | |||
| DisplayDevice({this.devices, this.loading, this.msg}); | |||
| factory DisplayDevice.loading() { | |||
| return DisplayDevice(devices: null, loading: true, msg: null); | |||
| } | |||
| factory DisplayDevice.data(List<Device> data) { | |||
| return DisplayDevice(devices: data, loading: false, msg: null); | |||
| } | |||
| factory DisplayDevice.error(String msg) { | |||
| return DisplayDevice(devices: null, loading: false, msg: msg); | |||
| } | |||
| @override | |||
| List<Object> get props => [devices, loading, msg]; | |||
| } | |||
| @@ -1,4 +1,9 @@ | |||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||
| import 'package:farm_tpf/presentation/screens/control_device/widget_device_list.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| import 'bloc/device_bloc.dart'; | |||
| class ControlDeviceScreen extends StatefulWidget { | |||
| @override | |||
| @@ -6,12 +11,47 @@ class ControlDeviceScreen extends StatefulWidget { | |||
| } | |||
| class _ControlDeviceScreenState extends State<ControlDeviceScreen> { | |||
| BuildContext _blocContext; | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| _blocContext = context; | |||
| return Scaffold( | |||
| appBar: AppBar( | |||
| title: Text("Điều khiển thiết bị"), | |||
| ), | |||
| appBar: AppBar( | |||
| title: Text("Điều khiển thiết bị"), | |||
| ), | |||
| body: BlocProvider<DeviceBloc>( | |||
| create: (context) => | |||
| DeviceBloc(repository: Repository())..add(OpenScreen()), | |||
| child: _buildContent())); | |||
| } | |||
| Widget _buildContent() { | |||
| return BlocBuilder<DeviceBloc, DeviceState>( | |||
| builder: (context, state) { | |||
| if (state is DisplayDevice) { | |||
| if (state.loading) { | |||
| return Container( | |||
| child: Center( | |||
| child: CircularProgressIndicator(), | |||
| ), | |||
| ); | |||
| } | |||
| if (state.devices != null) { | |||
| return Container( | |||
| child: WidgetDeviceList(devices: state.devices), | |||
| ); | |||
| } | |||
| return Container(); | |||
| } else { | |||
| return Container(); | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,267 @@ | |||
| import 'package:dio/dio.dart'; | |||
| import 'package:farm_tpf/custom_model/Device.dart'; | |||
| import 'package:farm_tpf/presentation/custom_widgets/widget_loading.dart'; | |||
| import 'package:farm_tpf/presentation/screens/control_device/bloc/device_bloc.dart'; | |||
| import 'package:farm_tpf/utils/const_common.dart'; | |||
| import 'package:farm_tpf/utils/pref.dart'; | |||
| import 'package:flutter/cupertino.dart'; | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter/widgets.dart'; | |||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||
| class WidgetDeviceList extends StatefulWidget { | |||
| List<Device> devices; | |||
| WidgetDeviceList({@required this.devices}); | |||
| @override | |||
| _WidgetDeviceListState createState() => _WidgetDeviceListState(); | |||
| } | |||
| class _WidgetDeviceListState extends State<WidgetDeviceList> { | |||
| List<Device> currentDevices = new List<Device>(); | |||
| BuildContext _context; | |||
| var pref = LocalPref(); | |||
| var token; | |||
| var client; | |||
| Future<Null> getSharedPrefs() async { | |||
| token = await pref.getString(DATA_CONST.TOKEN_KEY); | |||
| var options = BaseOptions(baseUrl: ConstCommon.baseUrl); | |||
| options.headers["Authorization"] = "Bearer $token"; | |||
| client = Dio(options); | |||
| } | |||
| @override | |||
| void initState() { | |||
| super.initState(); | |||
| getSharedPrefs(); | |||
| } | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| _context = context; | |||
| if (widget.devices.isNotEmpty) { | |||
| currentDevices = widget.devices; | |||
| return widgetDeviceList( | |||
| widget.devices.map((show) => ItemDeviceVM.fromDevice(show)).toList(), | |||
| _context); | |||
| } else { | |||
| return Container( | |||
| child: Center( | |||
| child: Padding( | |||
| padding: const EdgeInsets.symmetric(horizontal: 32), | |||
| child: Text('Không có dữ liệu'), | |||
| ), | |||
| ), | |||
| ); | |||
| } | |||
| } | |||
| Widget widgetDeviceList(List<ItemDeviceVM> items, BuildContext context) { | |||
| return Container( | |||
| child: GridView.count( | |||
| childAspectRatio: (10 / 7), | |||
| crossAxisCount: 2, | |||
| children: items.map( | |||
| (item) { | |||
| return _widgetItemDevice(item, context); | |||
| }, | |||
| ).toList(), | |||
| )); | |||
| } | |||
| Widget _widgetItemDevice(ItemDeviceVM item, BuildContext context) { | |||
| return GestureDetector( | |||
| onTap: () { | |||
| //Navigator.of(context).push(MaterialPageRoute(builder: (context) => DeviceDetail(device: _device,))); | |||
| }, | |||
| child: Card( | |||
| margin: EdgeInsets.all(8.0), | |||
| shadowColor: Colors.grey, | |||
| elevation: 3, | |||
| child: Column( | |||
| crossAxisAlignment: CrossAxisAlignment.center, | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| children: <Widget>[ | |||
| Column( | |||
| children: <Widget>[ | |||
| powerBtn(item, context), | |||
| Text( | |||
| item.name, | |||
| style: TextStyle( | |||
| color: Colors.black54, | |||
| fontSize: 13, | |||
| fontWeight: FontWeight.bold), | |||
| overflow: TextOverflow.clip, | |||
| maxLines: 2, | |||
| textAlign: TextAlign.center, | |||
| ), | |||
| Text(""), | |||
| widgetStatus(item) | |||
| ], | |||
| ) | |||
| ]), | |||
| )); | |||
| } | |||
| Widget powerBtn(ItemDeviceVM item, BuildContext _context) { | |||
| return BlocConsumer<DeviceBloc, DeviceState>( | |||
| listener: (context, state) {}, | |||
| buildWhen: (prev, current) { | |||
| return current is DisplayDevice; | |||
| }, | |||
| builder: (context, state) { | |||
| if (state is DisplayDevice) { | |||
| if (state.loading) { | |||
| return Container( | |||
| child: Center( | |||
| child: CircularProgressIndicator(), | |||
| ), | |||
| ); | |||
| } | |||
| if (state.devices != null) { | |||
| currentDevices = state.devices; | |||
| if (item.status == "0") { | |||
| //tat | |||
| return IconButton( | |||
| icon: Icon(Icons.power_settings_new), | |||
| color: Colors.grey, | |||
| onPressed: () async { | |||
| //bat | |||
| _controlSwitchDevice(item, context); | |||
| }); | |||
| } else if (item.status == "1") { | |||
| //Bat | |||
| return IconButton( | |||
| icon: Icon(Icons.power_settings_new), | |||
| color: Colors.blue, | |||
| onPressed: () async { | |||
| //tat | |||
| _controlSwitchDevice(item, context); | |||
| }); | |||
| } else { | |||
| //loi | |||
| return IconButton( | |||
| icon: Icon(Icons.power_settings_new), | |||
| color: Colors.grey, | |||
| onPressed: () {}); | |||
| } | |||
| } | |||
| return Container(); | |||
| } else { | |||
| return Container(); | |||
| } | |||
| }, | |||
| ); | |||
| } | |||
| Widget widgetStatus(ItemDeviceVM item) { | |||
| if (item.status == "0") { | |||
| return Text( | |||
| "Đang Tắt", | |||
| style: TextStyle(color: Colors.black54, fontSize: 13), | |||
| ); | |||
| } else if (item.status == "1") { | |||
| return Text( | |||
| "Đang Bật", | |||
| style: TextStyle(color: Colors.green, fontSize: 13), | |||
| ); | |||
| } else { | |||
| return Text( | |||
| "Không kết nối", | |||
| style: TextStyle(color: Colors.red, fontSize: 13), | |||
| ); | |||
| } | |||
| } | |||
| _controlSwitchDevice(ItemDeviceVM item, BuildContext context) { | |||
| //neu status =1 ->tat . Neu Khong = bat | |||
| var deviceOnOff = (item.status == "1") ? "0" : "1"; | |||
| var idDevice = item.id; | |||
| var statusDialogView = (item.status == "1") ? "Tắt" : "Bật"; | |||
| Widget cancelButton = CupertinoDialogAction( | |||
| child: Text("Huỷ"), | |||
| onPressed: () { | |||
| Navigator.of(context).pop(); | |||
| }, | |||
| ); | |||
| Widget continueButton = CupertinoDialogAction( | |||
| child: Text("Ok"), | |||
| onPressed: () async { | |||
| Navigator.of(context).pop(); | |||
| LoadingDialog.showLoadingDialog(_context); | |||
| try { | |||
| String urlTurnOff = | |||
| "/api/equipment-of-customers-on-off/$idDevice/$deviceOnOff"; | |||
| var response = await client.put(urlTurnOff); | |||
| if (200 <= response.statusCode && response.statusCode < 299) { | |||
| BlocProvider.of<DeviceBloc>(_context).add(ControlDevice( | |||
| currentDevices: currentDevices, updatedDeviceId: item.id)); | |||
| Scaffold.of(context) | |||
| ..hideCurrentSnackBar() | |||
| ..showSnackBar( | |||
| SnackBar( | |||
| content: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| children: <Widget>[ | |||
| Text('Điều khiển thành công thiết bị ${item.name}'), | |||
| Icon(Icons.done), | |||
| ], | |||
| ), | |||
| backgroundColor: Colors.green, | |||
| ), | |||
| ); | |||
| } | |||
| } catch (error) { | |||
| Scaffold.of(context) | |||
| ..hideCurrentSnackBar() | |||
| ..showSnackBar( | |||
| SnackBar( | |||
| content: Row( | |||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |||
| children: <Widget>[ | |||
| Text('Điều khiển thiết bị ${item.name} thất bại'), | |||
| Icon(Icons.error), | |||
| ], | |||
| ), | |||
| backgroundColor: Colors.red, | |||
| ), | |||
| ); | |||
| } | |||
| LoadingDialog.hideLoadingDialog(_context); | |||
| }, | |||
| ); | |||
| CupertinoAlertDialog alert = CupertinoAlertDialog( | |||
| title: Text("Điều khiển thiết bị"), | |||
| content: Text("$statusDialogView thiết bị ${item.name}?"), | |||
| actions: [ | |||
| cancelButton, | |||
| continueButton, | |||
| ], | |||
| ); | |||
| showDialog( | |||
| context: context, | |||
| builder: (BuildContext context) { | |||
| return alert; | |||
| }, | |||
| ); | |||
| } | |||
| } | |||
| class ItemDeviceVM { | |||
| Device device; | |||
| num id; | |||
| String name; | |||
| String status; | |||
| String location; | |||
| ItemDeviceVM(this.id, this.name, this.status); | |||
| ItemDeviceVM.fromDevice(Device device) { | |||
| this.device = device; | |||
| id = device.id; | |||
| name = device.name.toString(); | |||
| status = device.status; | |||
| location = device.location; | |||
| } | |||
| } | |||