fix: fixing the grafik UI for sensor data
This commit is contained in:
parent
fc4350f1dd
commit
04769c4f00
BIN
agrilink_vocpro/assets/images/valve.png
Normal file
BIN
agrilink_vocpro/assets/images/valve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -2,7 +2,7 @@ class AppConstant {
|
||||||
static const String appName = 'Kebun Pintar';
|
static const String appName = 'Kebun Pintar';
|
||||||
static const String appVersion = '1.0.0';
|
static const String appVersion = '1.0.0';
|
||||||
|
|
||||||
static const String baseUrl = 'https://agrilinkvocpropisdev.id/api/v1';
|
static const String baseUrl = 'http://192.168.11.41:3333/api/';
|
||||||
|
|
||||||
static const String mqttServer = 'armadillo.rmq.cloudamqp.com';
|
static const String mqttServer = 'armadillo.rmq.cloudamqp.com';
|
||||||
static const String mqttUsername = 'obyskxhx:obyskxhx';
|
static const String mqttUsername = 'obyskxhx:obyskxhx';
|
||||||
|
|
|
||||||
28
agrilink_vocpro/lib/core/widgets/show_info.dart
Normal file
28
agrilink_vocpro/lib/core/widgets/show_info.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
Future<dynamic> showInfo(BuildContext context, String title, String content) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(title),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
content,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: const Text('OK'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
66
agrilink_vocpro/lib/data/model/relay_response.dart
Normal file
66
agrilink_vocpro/lib/data/model/relay_response.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
class RelayResponse {
|
||||||
|
bool? success;
|
||||||
|
List<Relay>? data;
|
||||||
|
|
||||||
|
RelayResponse({this.success, this.data});
|
||||||
|
|
||||||
|
RelayResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
success = json['success'];
|
||||||
|
if (json['data'] != null) {
|
||||||
|
data = <Relay>[];
|
||||||
|
json['data'].forEach((v) {
|
||||||
|
data!.add(Relay.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['success'] = success;
|
||||||
|
if (this.data != null) {
|
||||||
|
data['data'] = this.data!.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Relay {
|
||||||
|
int? id;
|
||||||
|
int? number;
|
||||||
|
String? enabledAt;
|
||||||
|
String? disabledAt;
|
||||||
|
bool? currentStatus;
|
||||||
|
String? createdAt;
|
||||||
|
String? updatedAt;
|
||||||
|
|
||||||
|
Relay(
|
||||||
|
{this.id,
|
||||||
|
this.number,
|
||||||
|
this.enabledAt,
|
||||||
|
this.disabledAt,
|
||||||
|
this.currentStatus,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt});
|
||||||
|
|
||||||
|
Relay.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
number = json['number'];
|
||||||
|
enabledAt = json['enabled_at'];
|
||||||
|
disabledAt = json['disabled_at'];
|
||||||
|
currentStatus = json['current_status'];
|
||||||
|
createdAt = json['created_at'];
|
||||||
|
updatedAt = json['updated_at'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
|
data['id'] = id;
|
||||||
|
data['number'] = number;
|
||||||
|
data['enabled_at'] = enabledAt;
|
||||||
|
data['disabled_at'] = disabledAt;
|
||||||
|
data['current_status'] = currentStatus;
|
||||||
|
data['created_at'] = createdAt;
|
||||||
|
data['updated_at'] = updatedAt;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_contant.dart';
|
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
||||||
|
import 'package:agrilink_vocpro/data/model/relay_response.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
|
@ -9,8 +10,17 @@ class AppService {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> getGreenHouseData() async {
|
Future<RelayResponse> getRelayStatus() async {
|
||||||
try {} on DioException catch (e) {
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 3));
|
||||||
|
final result = await _dioWithoutInterceptor.get('get-relay');
|
||||||
|
if (result.statusCode == 200) {
|
||||||
|
final data = RelayResponse.fromJson(result.data);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load data');
|
||||||
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,125 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||||
import 'package:agrilink_vocpro/domain/service/mqtt_service.dart';
|
import 'package:agrilink_vocpro/domain/service/app_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ControlProvider extends ChangeNotifier {
|
class ControlProvider extends ChangeNotifier {
|
||||||
final MQTTService _mqttService = MQTTService();
|
final AppService _appService = AppService();
|
||||||
|
|
||||||
bool _control_1 = false;
|
bool _control_1 = false;
|
||||||
|
|
||||||
bool get control_1 => _control_1;
|
bool get control_1 => _control_1;
|
||||||
|
|
||||||
|
bool _control_2 = false;
|
||||||
|
bool get control_2 => _control_2;
|
||||||
|
|
||||||
ControlProvider() {
|
ControlProvider() {
|
||||||
connectMqtt();
|
getRelayStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultState mqttState = ResultState.initial;
|
// ResultState mqttState = ResultState.initial;
|
||||||
ResultState subscribeState = ResultState.initial;
|
// ResultState subscribeState = ResultState.initial;
|
||||||
|
|
||||||
Future<void> connectMqtt() async {
|
ResultState relayState = ResultState.initial;
|
||||||
mqttState = ResultState.loading;
|
|
||||||
subscribeState = ResultState.loading;
|
// Future<void> connectMqtt() async {
|
||||||
|
// mqttState = ResultState.loading;
|
||||||
|
// subscribeState = ResultState.loading;
|
||||||
|
// notifyListeners();
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// final result = await _mqttService.setupMqtt();
|
||||||
|
// if (result == ResultState.hasData) {
|
||||||
|
// mqttState = result;
|
||||||
|
// final result2 = await _mqttService.subscribeToRelayStatus();
|
||||||
|
// // if (result2 == true) {
|
||||||
|
// // subscribeState = ResultState.hasData;
|
||||||
|
// // _control_1 = true;
|
||||||
|
// // } else {
|
||||||
|
// // subscribeState = ResultState.hasData;
|
||||||
|
// // _control_1 = false;
|
||||||
|
// // }
|
||||||
|
// } else {
|
||||||
|
// mqttState = ResultState.error;
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// mqttState = ResultState.error;
|
||||||
|
// print(e);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
|
||||||
|
Future<void> getRelayStatus() async {
|
||||||
|
relayState = ResultState.loading;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = await _mqttService.setupMqtt();
|
final result = await _appService.getRelayStatus();
|
||||||
if (result == ResultState.hasData) {
|
if (result.success == true) {
|
||||||
mqttState = result;
|
for (var element in result.data!) {
|
||||||
final result2 = await _mqttService.subscribeToRelayStatus();
|
if (element.number == 1) {
|
||||||
// if (result2 == true) {
|
switchControl1(element.currentStatus ?? false);
|
||||||
// subscribeState = ResultState.hasData;
|
}
|
||||||
// _control_1 = true;
|
if (element.number == 2) {
|
||||||
// } else {
|
switchControl2(element.currentStatus ?? false);
|
||||||
// subscribeState = ResultState.hasData;
|
}
|
||||||
// _control_1 = false;
|
}
|
||||||
// }
|
relayState = ResultState.hasData;
|
||||||
|
notifyListeners();
|
||||||
} else {
|
} else {
|
||||||
mqttState = ResultState.error;
|
relayState = ResultState.error;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
mqttState = ResultState.error;
|
relayState = ResultState.error;
|
||||||
print(e);
|
notifyListeners();
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> disconnectMqtt() async {
|
|
||||||
try {
|
|
||||||
await _mqttService.disconnectMqtt();
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
disconnectMqtt();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void switchControl1() {
|
|
||||||
_control_1 = !_control_1;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<ResultState> publishMessage(String topic, String message) async {
|
|
||||||
try {
|
|
||||||
final result = await _mqttService.publishMessage(topic, message);
|
|
||||||
return result;
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
print(e);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> subscribeToTopic(String topic) async {
|
// Future<void> disconnectMqtt() async {
|
||||||
try {
|
// try {
|
||||||
await _mqttService.subscribeToTopic(topic);
|
// await _mqttService.disconnectMqtt();
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
print(e);
|
// print(e);
|
||||||
rethrow;
|
// rethrow;
|
||||||
}
|
// }
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// void dispose() {
|
||||||
|
// disconnectMqtt();
|
||||||
|
// super.dispose();
|
||||||
|
// }
|
||||||
|
|
||||||
|
void switchControl1(bool value) {
|
||||||
|
_control_1 = value;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void switchControl2(bool value) {
|
||||||
|
_control_2 = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<ResultState> publishMessage(String topic, String message) async {
|
||||||
|
// try {
|
||||||
|
// final result = await _mqttService.publishMessage(topic, message);
|
||||||
|
// return result;
|
||||||
|
// } catch (e) {
|
||||||
|
// print(e);
|
||||||
|
// rethrow;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<void> subscribeToTopic(String topic) async {
|
||||||
|
// try {
|
||||||
|
// await _mqttService.subscribeToTopic(topic);
|
||||||
|
// } catch (e) {
|
||||||
|
// print(e);
|
||||||
|
// rethrow;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
import 'package:agrilink_vocpro/core/state/result_state.dart';
|
||||||
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
|
import 'package:agrilink_vocpro/features/control/provider/control_provider.dart';
|
||||||
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
@ -11,6 +13,7 @@ class ControlScreen extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final provider = Provider.of<ControlProvider>(context, listen: true);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Control', style: AppTheme.labelMedium),
|
title: Text('Control', style: AppTheme.labelMedium),
|
||||||
|
|
@ -18,39 +21,114 @@ class ControlScreen extends StatelessWidget {
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
),
|
),
|
||||||
body: Consumer<ControlProvider>(
|
body: RefreshIndicator(
|
||||||
builder: (context, provider, child) {
|
onRefresh: () async =>
|
||||||
return SafeArea(
|
await context.read<ControlProvider>().getRelayStatus(),
|
||||||
child: ListView(
|
child: SafeArea(
|
||||||
children: [
|
child: ListView(
|
||||||
Center(
|
children: [
|
||||||
child: provider.mqttState == ResultState.loading
|
SizedBox(height: 16.h),
|
||||||
? const CupertinoActivityIndicator()
|
GridView(
|
||||||
: provider.mqttState == ResultState.hasData
|
padding: EdgeInsets.all(16.r),
|
||||||
? const Text('Terhubung ke Broker')
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
: const Text('Gagal terhubung ke Broker'),
|
shrinkWrap: true,
|
||||||
),
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
SizedBox(height: 16.h),
|
crossAxisCount: 2,
|
||||||
ListTile(
|
crossAxisSpacing: 16.r,
|
||||||
title: Text('Control 1', style: AppTheme.labelMedium),
|
mainAxisSpacing: 16.r,
|
||||||
subtitle: const Text('Control 1 description'),
|
childAspectRatio: 1.4.h,
|
||||||
trailing: Switch(
|
|
||||||
value: provider.control_1,
|
|
||||||
onChanged: (value) {
|
|
||||||
provider.control_1 == false
|
|
||||||
? provider.publishMessage(
|
|
||||||
'smartfarming/relay/232', 'ON')
|
|
||||||
: provider.publishMessage(
|
|
||||||
'smartfarming/relay/232', 'OFF');
|
|
||||||
provider.switchControl1();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
children: [
|
||||||
// Control lainnya...
|
ControlButtonWidget(
|
||||||
],
|
title: 'Katup Air',
|
||||||
),
|
subtitle: 'Relay 1',
|
||||||
);
|
isActive: provider.control_1,
|
||||||
},
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ControlButtonWidget(
|
||||||
|
title: 'Lampu Utama',
|
||||||
|
subtitle: 'Relay 2',
|
||||||
|
isActive: provider.control_2,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ControlButtonWidget extends StatelessWidget {
|
||||||
|
const ControlButtonWidget({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.isActive,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final bool isActive;
|
||||||
|
final Function() onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(16.r),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16.r),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withOpacity(0.2),
|
||||||
|
spreadRadius: 1.r,
|
||||||
|
blurRadius: 16.r,
|
||||||
|
offset: Offset(0, 12.r),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(title, style: AppTheme.labelMedium),
|
||||||
|
Text(subtitle, style: AppTheme.labelSmall),
|
||||||
|
const Spacer(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Consumer<ControlProvider>(
|
||||||
|
builder: (context, provider, child) {
|
||||||
|
switch (provider.relayState) {
|
||||||
|
case ResultState.loading:
|
||||||
|
return CircleAvatar(
|
||||||
|
radius: 20.r,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: const CupertinoActivityIndicator(),
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return InkWell(
|
||||||
|
highlightColor: Colors.black,
|
||||||
|
onTap: onTap,
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 20.r,
|
||||||
|
backgroundColor: isActive
|
||||||
|
? AppColor.secondary
|
||||||
|
: Colors.grey.shade400,
|
||||||
|
child: const Icon(
|
||||||
|
BootstrapIcons.power,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class ConductivityScreen extends StatelessWidget {
|
||||||
padding: EdgeInsets.only(right: 16),
|
padding: EdgeInsets.only(right: 16),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.electric_bolt_rounded,
|
Icons.electric_bolt_rounded,
|
||||||
color: Colors.blue,
|
color: Colors.teal,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -38,7 +38,7 @@ class ConductivityScreen extends StatelessWidget {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.electric_bolt_rounded,
|
Icons.electric_bolt_rounded,
|
||||||
size: 64.r,
|
size: 64.r,
|
||||||
color: Colors.blue,
|
color: Colors.teal,
|
||||||
),
|
),
|
||||||
Text('$value µS/cm', style: AppTheme.headline1),
|
Text('$value µS/cm', style: AppTheme.headline1),
|
||||||
],
|
],
|
||||||
|
|
@ -70,7 +70,12 @@ class ConductivityScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: const GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.teal,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -110,13 +110,19 @@ class HumidityScreen extends StatelessWidget {
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: 2.h,
|
aspectRatio: 2.h,
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 16.w),
|
margin: EdgeInsets.symmetric(horizontal: 16.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(
|
border:
|
||||||
color: Colors.grey.shade300, width: 1.w)),
|
Border.all(color: Colors.grey.shade300, width: 1.w)),
|
||||||
child: const GarphicWidget()),
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.blue.shade200,
|
||||||
|
Colors.blue,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,18 @@ class LightScreen extends StatelessWidget {
|
||||||
const Text('Grafik'),
|
const Text('Grafik'),
|
||||||
SizedBox(height: 16.h),
|
SizedBox(height: 16.h),
|
||||||
AspectRatio(
|
AspectRatio(
|
||||||
aspectRatio: 1.5.h,
|
aspectRatio: 1.8.h,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.yellow.shade100,
|
||||||
|
Colors.orange.shade200,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
// Row(
|
// Row(
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class NitrogenScreen extends StatelessWidget {
|
||||||
size: 64.r,
|
size: 64.r,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
Text('$value µS/cm', style: AppTheme.headline1),
|
Text('$value ppm', style: AppTheme.headline1),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 32.h),
|
SizedBox(height: 32.h),
|
||||||
|
|
@ -71,7 +71,12 @@ class NitrogenScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.blue.shade200,
|
||||||
|
Colors.blue,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,12 @@ class PhScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.amber.shade200,
|
||||||
|
Colors.orange,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,12 @@ class PhosphorusScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.blue.shade200,
|
||||||
|
Colors.blue,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,12 @@ class PotassiumScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: const GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.teal,
|
||||||
|
Colors.green,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
|
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/widgets/show_info.dart';
|
||||||
|
import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart';
|
||||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -34,10 +37,8 @@ class SoilMoistureScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16.w),
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
|
||||||
height: MediaQuery.of(context).size.height * 0.05,
|
|
||||||
),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 280.h,
|
height: 280.h,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|
@ -80,7 +81,47 @@ class SoilMoistureScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
SizedBox(height: 16.h),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Soil Temperature',
|
||||||
|
style: AppTheme.labelMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
iconSize: 20.r,
|
||||||
|
color: Colors.blue,
|
||||||
|
onPressed: () {
|
||||||
|
showInfo(
|
||||||
|
context,
|
||||||
|
'Soil Temperature',
|
||||||
|
AppConstant.soilTempInfo,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(BootstrapIcons.info_circle))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 16.h),
|
||||||
|
const Text('Grafik'),
|
||||||
|
SizedBox(height: 16.h),
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 1.8.h,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
|
),
|
||||||
|
child: GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.blue.shade200,
|
||||||
|
Colors.blue,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
import 'package:agrilink_vocpro/core/constant/app_constant.dart';
|
||||||
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
|
import 'package:agrilink_vocpro/core/widgets/show_info.dart';
|
||||||
import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart';
|
import 'package:agrilink_vocpro/features/home/widgets/graphic_widget.dart';
|
||||||
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
import 'package:bootstrap_icons/bootstrap_icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -85,8 +86,7 @@ class SoilTemperatureScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
SizedBox(height: 16.h),
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -218,7 +218,12 @@ class SoilTemperatureScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: const GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.amber,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
@ -226,31 +231,4 @@ class SoilTemperatureScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> showInfo(BuildContext context, String title, String content) {
|
|
||||||
return showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(title),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
content,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: const Text('OK'),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ class TemperatureScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
progressBar: const GaugeBasicProgressBar(
|
progressBar: const GaugeBasicProgressBar(
|
||||||
gradient: GaugeAxisGradient(colors: [
|
gradient: GaugeAxisGradient(colors: [
|
||||||
Colors.blue,
|
Colors.white12,
|
||||||
Colors.orange,
|
Colors.orange,
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|
@ -211,7 +211,12 @@ class TemperatureScreen extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
border: Border.all(color: Colors.grey.shade300, width: 1.w),
|
||||||
),
|
),
|
||||||
child: const GarphicWidget(),
|
child: const GarphicWidget(
|
||||||
|
gradientColors: [
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.amber,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,191 +1,115 @@
|
||||||
import 'package:agrilink_vocpro/core/constant/app_color.dart';
|
import 'package:agrilink_vocpro/core/constant/app_theme.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
class GarphicWidget extends StatelessWidget {
|
class GarphicWidget extends StatelessWidget {
|
||||||
const GarphicWidget({super.key, thi});
|
const GarphicWidget({super.key, required this.gradientColors});
|
||||||
|
|
||||||
|
final List<Color> gradientColors;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BarChart(
|
return Padding(
|
||||||
BarChartData(
|
padding: EdgeInsets.all(8.r),
|
||||||
barTouchData: barTouchData,
|
child: LineChart(
|
||||||
titlesData: titlesData,
|
mainData(),
|
||||||
borderData: borderData,
|
|
||||||
barGroups: barGroups,
|
|
||||||
gridData: const FlGridData(show: false),
|
|
||||||
alignment: BarChartAlignment.spaceAround,
|
|
||||||
maxY: 20,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
BarTouchData get barTouchData => BarTouchData(
|
Widget bottomTitleWidgets(double value, TitleMeta meta) {
|
||||||
enabled: false,
|
TextStyle style = AppTheme.labelSmall;
|
||||||
touchTooltipData: BarTouchTooltipData(
|
Widget text;
|
||||||
getTooltipColor: (group) => Colors.transparent,
|
switch (value.toInt()) {
|
||||||
tooltipPadding: EdgeInsets.zero,
|
case 1:
|
||||||
tooltipMargin: 8,
|
text = Text('01.00', style: style);
|
||||||
getTooltipItem: (
|
break;
|
||||||
BarChartGroupData group,
|
case 6:
|
||||||
int groupIndex,
|
text = Text('06.00', style: style);
|
||||||
BarChartRodData rod,
|
break;
|
||||||
int rodIndex,
|
case 12:
|
||||||
) {
|
text = Text('12.00', style: style);
|
||||||
return BarTooltipItem(
|
break;
|
||||||
rod.toY.round().toString(),
|
case 18:
|
||||||
const TextStyle(
|
text = Text('18.00', style: style);
|
||||||
color: AppColor.textDark,
|
break;
|
||||||
fontWeight: FontWeight.bold,
|
case 23:
|
||||||
),
|
text = Text('23.00', style: style);
|
||||||
);
|
break;
|
||||||
},
|
default:
|
||||||
),
|
text = Text('', style: style);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SideTitleWidget(
|
||||||
|
axisSide: meta.axisSide,
|
||||||
|
child: text,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget getTitles(double value, TitleMeta meta) {
|
|
||||||
const style = TextStyle(
|
|
||||||
color: AppColor.textDark,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 14,
|
|
||||||
);
|
|
||||||
String text;
|
|
||||||
switch (value.toInt()) {
|
|
||||||
case 0:
|
|
||||||
text = 'Mon';
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
text = 'Tue';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
text = 'Wed';
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
text = 'Thu';
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
text = 'Fri';
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
text = 'Sat';
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
text = 'Sun';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = '';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return SideTitleWidget(
|
|
||||||
axisSide: meta.axisSide,
|
|
||||||
space: 4,
|
|
||||||
child: Text(text, style: style),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlTitlesData get titlesData => const FlTitlesData(
|
LineChartData mainData() {
|
||||||
show: true,
|
return LineChartData(
|
||||||
bottomTitles: AxisTitles(
|
gridData: const FlGridData(
|
||||||
sideTitles: SideTitles(
|
show: false,
|
||||||
showTitles: true,
|
),
|
||||||
reservedSize: 30,
|
titlesData: FlTitlesData(
|
||||||
getTitlesWidget: getTitles,
|
show: true,
|
||||||
|
rightTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
topTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
|
),
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 30,
|
||||||
|
interval: 1,
|
||||||
|
getTitlesWidget: bottomTitleWidgets,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leftTitles: const AxisTitles(
|
||||||
|
sideTitles: SideTitles(showTitles: false),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
leftTitles: AxisTitles(
|
borderData: FlBorderData(
|
||||||
sideTitles: SideTitles(showTitles: false),
|
show: false,
|
||||||
|
border: Border.all(color: const Color(0xff37434d)),
|
||||||
),
|
),
|
||||||
topTitles: AxisTitles(
|
minX: 0,
|
||||||
sideTitles: SideTitles(showTitles: false),
|
maxX: 24,
|
||||||
),
|
minY: 0,
|
||||||
rightTitles: AxisTitles(
|
maxY: 100,
|
||||||
sideTitles: SideTitles(showTitles: false),
|
lineBarsData: [
|
||||||
),
|
LineChartBarData(
|
||||||
);
|
spots: const [
|
||||||
|
FlSpot(0, 38),
|
||||||
FlBorderData get borderData => FlBorderData(
|
FlSpot(1, 42),
|
||||||
show: false,
|
FlSpot(2, 50),
|
||||||
);
|
FlSpot(3, 53),
|
||||||
|
FlSpot(4, 58),
|
||||||
LinearGradient get _barsGradient => const LinearGradient(
|
FlSpot(5, 64),
|
||||||
colors: [
|
FlSpot(7, 49),
|
||||||
AppColor.secondary,
|
],
|
||||||
AppColor.ternary,
|
isCurved: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: gradientColors,
|
||||||
|
),
|
||||||
|
barWidth: 5,
|
||||||
|
isStrokeCapRound: true,
|
||||||
|
dotData: const FlDotData(
|
||||||
|
show: false,
|
||||||
|
),
|
||||||
|
belowBarData: BarAreaData(
|
||||||
|
show: true,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: gradientColors
|
||||||
|
.map((color) => color.withOpacity(0.3))
|
||||||
|
.toList()),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
begin: Alignment.bottomCenter,
|
|
||||||
end: Alignment.topCenter,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
List<BarChartGroupData> get barGroups => [
|
}
|
||||||
BarChartGroupData(
|
|
||||||
x: 0,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 8,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 1,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 10,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 2,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 14,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 3,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 15,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 4,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 13,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 5,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 10,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
BarChartGroupData(
|
|
||||||
x: 6,
|
|
||||||
barRods: [
|
|
||||||
BarChartRodData(
|
|
||||||
toY: 16,
|
|
||||||
gradient: _barsGradient,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
showingTooltipIndicators: [0],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,12 @@ class ListDataFromCensorDht extends StatelessWidget {
|
||||||
DataDisplayerWidget(
|
DataDisplayerWidget(
|
||||||
title: 'Temperature',
|
title: 'Temperature',
|
||||||
subtitle: 'suhu greenhouse',
|
subtitle: 'suhu greenhouse',
|
||||||
value: '28',
|
value: '43',
|
||||||
unit: '°C',
|
unit: '°C',
|
||||||
icon: BootstrapIcons.thermometer_half,
|
icon: BootstrapIcons.thermometer_half,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await context.push('${AppRoute.temperature}/28');
|
await context.push('${AppRoute.temperature}/43');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
DataDisplayerWidget(
|
DataDisplayerWidget(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user