diff --git a/lib/BardAIController.dart b/lib/BardAIController.dart new file mode 100644 index 0000000..a005db2 --- /dev/null +++ b/lib/BardAIController.dart @@ -0,0 +1,39 @@ +// ignore_for_file: file_names + +import 'dart:convert'; + +import 'package:plant_sense_1/BardModel.dart'; +import 'package:plant_sense_1/data.dart'; +import 'package:get/get.dart'; +import 'package:http/http.dart' as http; + +class BardAIController extends GetxController { + RxList historyList = RxList([]); + + RxBool isLoading = false.obs; + void sendPrompt(String prompt) async { + isLoading.value = true; + var newHistory = bardModel(system: "user", message: prompt); + historyList.add(newHistory); + final body = { + 'prompt': { + 'text': prompt, + }, + }; + + final request = await http.post( + Uri.parse( + 'https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText?key=$APIKEY'), + headers: { + 'Content-Type': 'application/json', + }, + body: jsonEncode(body), + ); + + final response = jsonDecode(request.body); + final bardReplay = response["candidates"][0]["output"]; + var newHistory2 = bardModel(system: "bard", message: bardReplay); + historyList.add(newHistory2); + isLoading.value = false; + } +} diff --git a/lib/BardModel.dart b/lib/BardModel.dart new file mode 100644 index 0000000..a4e6e56 --- /dev/null +++ b/lib/BardModel.dart @@ -0,0 +1,24 @@ +// ignore_for_file: camel_case_types, no_leading_underscores_for_local_identifiers, file_names + +class bardModel { + String? system; + String? message; + + bardModel({this.system, this.message}); + + bardModel.fromJson(Map json) { + if (json["system"] is String) { + system = json["system"]; + } + if (json["message"] is String) { + message = json["message"]; + } + } + + Map toJson() { + final Map _data = {}; + _data["system"] = system; + _data["message"] = message; + return _data; + } +} diff --git a/lib/biometric_helper.dart b/lib/biometric_helper.dart new file mode 100644 index 0000000..d1b3500 --- /dev/null +++ b/lib/biometric_helper.dart @@ -0,0 +1,22 @@ +import 'package:local_auth/local_auth.dart'; + +class BiometricHelper { + final LocalAuthentication auth = LocalAuthentication(); + + Future hasEnrolledBiometrics() async { + final List availableBiometrics = + await auth.getAvailableBiometrics(); + + if (availableBiometrics.isNotEmpty) { + return true; + } + return false; + } + + Future authenticate() async { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Authenticate to proceed', + options: const AuthenticationOptions(biometricOnly: false)); + return didAuthenticate; + } +} diff --git a/lib/data.dart b/lib/data.dart new file mode 100644 index 0000000..0ad5619 --- /dev/null +++ b/lib/data.dart @@ -0,0 +1,7 @@ +// ignore_for_file: constant_identifier_names, non_constant_identifier_names +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +String? APIKEY = dotenv.env['APIKEY']; +const String BaseURL = + "https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText?key=YOUR_API_KEY"; +String? MOISTURESENSOR = dotenv.env['MOISTURESENSOR']; diff --git a/lib/dictionary_screen.dart b/lib/dictionary_screen.dart new file mode 100644 index 0000000..986b98d --- /dev/null +++ b/lib/dictionary_screen.dart @@ -0,0 +1,90 @@ +// ignore_for_file: must_be_immutable, use_build_context_synchronously + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:plant_sense_1/dictionary_specie_disease.dart'; + +class Species extends StatelessWidget { + List arrTypes; + final String addr; + Species({ + super.key, + required this.addr, + required this.arrTypes, + }); + + Future getSpecies(String dis) async { + List ty = []; + + Uri url = Uri.parse('${addr}api/dict/species/$dis'); + + final response = await http.get(url); + var jsonResponse = jsonDecode(response.body); + if (response.statusCode == 200) { + // If the server returns a 200 OK response, parse the JSON + ty = jsonResponse['Diseases']; + } else { + // If the server did not return a 200 OK response, throw an exception + throw Exception('Failed to load data'); + } + return ty; + } + + @override + Widget build(context) { + return Scaffold( + backgroundColor: const Color.fromARGB(255, 127, 60, 39), + appBar: AppBar( + title: Text( + "Species of Plants", + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), + backgroundColor: Colors.green, + ), + body: ListView.builder( + itemBuilder: (context, index) { + return Center( + child: Column( + children: [ + const SizedBox(height: 50), + TextButton( + onPressed: () async { + List types = await getSpecies(arrTypes[index]); + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SpeciesDisease( + species: arrTypes[index], + addr: addr, + arrTypes: types, + )), + ); + }, + style: FilledButton.styleFrom( + backgroundColor: Colors.green, + ), + child: Text( + arrTypes[index], + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal, + ), + ), + ), + ], + ), + ); + }, + itemCount: arrTypes.length, + )); + } +} diff --git a/lib/dictionary_specie_disease.dart b/lib/dictionary_specie_disease.dart new file mode 100644 index 0000000..6354ea1 --- /dev/null +++ b/lib/dictionary_specie_disease.dart @@ -0,0 +1,92 @@ +// ignore_for_file: must_be_immutable, use_build_context_synchronously + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:plant_sense_1/display_info.dart'; + +class SpeciesDisease extends StatelessWidget { + List arrTypes; + final String addr; + final String species; + SpeciesDisease({ + super.key, + required this.addr, + required this.species, + required this.arrTypes, + }); + + Future getInfo(String disease) async { + dynamic info; + disease = disease.replaceAll(RegExp(' +'), '_'); + Uri url = Uri.parse('${addr}api/info/$disease'); + + final response = await http.get(url); + var jsonResponse = jsonDecode(response.body); + if (response.statusCode == 200) { + // If the server returns a 200 OK response, parse the JSON + info = jsonResponse['Information']; + } else { + // If the server did not return a 200 OK response, throw an exception + throw Exception('Failed to load data'); + } + return info; + } + + @override + Widget build(context) { + return Scaffold( + backgroundColor: const Color.fromARGB(255, 127, 60, 39), + appBar: AppBar( + title: Text( + "Species of Plants", + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), + backgroundColor: Colors.green, + ), + body: ListView.builder( + itemBuilder: (context, index) { + return Center( + child: Column( + children: [ + const SizedBox(height: 50), + TextButton( + // onPressed: (){}, + onPressed: () async { + String info = await getInfo(arrTypes[index]); + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => DiseaseInformation( + info: info, + disease: arrTypes[index], + )), + ); + }, + style: FilledButton.styleFrom( + backgroundColor: Colors.green, + ), + child: Text( + arrTypes[index], + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal, + ), + ), + ), + ], + ), + ); + }, + itemCount: arrTypes.length, + )); + } +} diff --git a/lib/disease_classification.dart b/lib/disease_classification.dart index ace2d86..ac56479 100644 --- a/lib/disease_classification.dart +++ b/lib/disease_classification.dart @@ -4,9 +4,14 @@ import 'dart:io'; import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http; +import 'package:google_fonts/google_fonts.dart'; class DiseaseClassification extends StatefulWidget { - const DiseaseClassification({super.key}); + final String link; + const DiseaseClassification({ + super.key, + required this.link, + }); @override State createState() => _DiseaseClassificationState(); @@ -17,6 +22,11 @@ class _DiseaseClassificationState extends State { List? classes = ['Image Not Selected']; List? scores = ['Please select an Image']; String? response; + + String get link { + return widget.link; + } + void infrenceModel() async { if (_selectedImage == null) { setState(() { @@ -26,11 +36,7 @@ class _DiseaseClassificationState extends State { } else { File file = _selectedImage!; - // for local server - // String addr = 'http://192.168.1.39:8080/'; - - // for hosted api - String addr = 'https://plant-sense-api.onrender.com/'; + String addr = link; Uri url = Uri.parse('${addr}api/test'); String contentType = 'image/jpeg'; @@ -42,21 +48,18 @@ class _DiseaseClassificationState extends State { var res = await http.post(url, body: imgBytes, headers: headers); var jsonResponse = jsonDecode(res.body); - if(res.statusCode == 200) - { + + if (res.statusCode == 200) { setState(() { - scores = jsonResponse['score']; - classes = jsonResponse['labels']; - }); - } - else - { + scores = jsonResponse['score']; + classes = jsonResponse['labels']; + }); + } else { setState(() { - scores = jsonResponse['Server is ']; - classes = jsonResponse['Down']; + scores = jsonResponse['Server is ']; + classes = jsonResponse['Down']; }); } - } } @@ -64,20 +67,25 @@ class _DiseaseClassificationState extends State { Widget build(context) { return Scaffold( backgroundColor: const Color.fromARGB(255, 127, 60, 39), + appBar: AppBar( + title: Text( + "Disease Classification", + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 30, + color: Colors.white, + fontStyle: FontStyle.normal), + ), + backgroundColor: Colors.green, + ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.start, + children: [ const SizedBox( height: 50, ), - const Text( - "DISEASE CLASSIFICATION", - style: TextStyle(color: Colors.green, fontSize: 30), - ), - const SizedBox( - height: 25, - ), ImageInput( onPickImage: (image) { _selectedImage = image; @@ -89,9 +97,13 @@ class _DiseaseClassificationState extends State { FilledButton( onPressed: infrenceModel, style: FilledButton.styleFrom(backgroundColor: Colors.green), - child: const Text( + child: Text( 'Find Possible Diseases', - style: TextStyle(fontSize: 25), + style: GoogleFonts.ebGaramond( + color: Colors.white, + fontSize: 30, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic), ), ), const SizedBox( @@ -104,7 +116,11 @@ class _DiseaseClassificationState extends State { return Center( child: Text( classes?[index] + ' ' + scores?[index], - style: const TextStyle(color: Colors.white, fontSize: 15), + style: GoogleFonts.ebGaramond( + fontSize: 15, + fontStyle: FontStyle.normal, + color: Colors.white, + fontWeight: FontWeight.w300), ), ); }, diff --git a/lib/display_info.dart b/lib/display_info.dart new file mode 100644 index 0000000..41c0b8c --- /dev/null +++ b/lib/display_info.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class DiseaseInformation extends StatelessWidget { + final String disease; + final String info; + const DiseaseInformation( + {super.key, required this.disease, required this.info}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + disease, + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 20, + color: Colors.white, + fontStyle: FontStyle.italic), + ), + backgroundColor: Colors.green, + ), + body: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromARGB(255, 41, 36, 36), + Color.fromARGB(255, 127, 60, 39), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + child: Center( + child: Column(mainAxisSize: MainAxisSize.min, children: [ + const SizedBox(height: 40), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Text( + info, + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 20, + color: Colors.white, + fontStyle: FontStyle.italic, + ), + textAlign: TextAlign.center, + ), + ), + ) + ]), + ), + ), + ); + } +} diff --git a/lib/entry_screen.dart b/lib/entry_screen.dart index 0be76c0..e406b27 100644 --- a/lib/entry_screen.dart +++ b/lib/entry_screen.dart @@ -1,5 +1,13 @@ +// ignore_for_file: await_only_futures, use_build_context_synchronously import 'package:flutter/material.dart'; import 'package:plant_sense_1/disease_classification.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:plant_sense_1/dictionary_screen.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:plant_sense_1/help.dart'; +import 'package:plant_sense_1/moistureSensor.dart'; +import 'biometric_helper.dart'; class Entry extends StatefulWidget { const Entry({super.key}); @@ -9,6 +17,45 @@ class Entry extends StatefulWidget { } class _EntryState extends State { + // for local server + // final String addr = 'http://172.16.35.179:5000/'; + + // for hosted api + final String addr = 'https://plant-sense-api.onrender.com/'; + + Future getSpecies() async { + List ty = []; + + Uri url = Uri.parse('${addr}api/dict/species'); + + final response = await http.get(url); + var jsonResponse = jsonDecode(response.body); + // print(jsonResponse); + if (response.statusCode == 200) { + // If the server returns a 200 OK response, parse the JSON + setState(() { + ty = jsonResponse['Species']; + }); + } else { + // If the server did not return a 200 OK response, throw an exception + throw Exception('Failed to load data'); + } + return ty; + } + + bool showBiometric = false; + bool isAuthenticated = false; + @override + void initState() { + isBiometricsAvailable(); + super.initState(); + } + + isBiometricsAvailable() async { + showBiometric = await BiometricHelper().hasEnrolledBiometrics(); + setState(() {}); + } + @override Widget build(context) { return Center( @@ -20,24 +67,132 @@ class _EntryState extends State { width: 200, // color: const Color.fromARGB(150, 255, 255, 255), ), + + + const SizedBox( height: 70, ), - const Text( - 'PLANT SENSE', - style: TextStyle(color: Colors.white, fontSize: 30), + Text( + 'Plant Sense', + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 40, + color: Colors.white, + fontStyle: FontStyle.italic), + ), + + + + + + const SizedBox(height: 30), + + + + FilledButton( + onPressed: () async { + if (!isAuthenticated) { + isAuthenticated = await BiometricHelper().authenticate(); + setState(() {}); + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => DiseaseClassification(link: addr)), + ); + }, + style: FilledButton.styleFrom(backgroundColor: Colors.green), + child: Text( + 'Disease Classification', + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), + ), + + + const SizedBox(height: 30), + + + FilledButton( + onPressed: () async { + if (!isAuthenticated) { + isAuthenticated = await BiometricHelper().authenticate(); + setState(() {}); + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const MoistureSensor()), + ); + }, + style: FilledButton.styleFrom(backgroundColor: Colors.green), + child: Text( + 'Moisture Sensor', + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), ), + const SizedBox(height: 30), + FilledButton( - onPressed: () { + onPressed: () async { + List types = await getSpecies(); + if (!isAuthenticated) { + isAuthenticated = await BiometricHelper().authenticate(); + setState(() {}); + } Navigator.push( context, MaterialPageRoute( - builder: (context) => const DiseaseClassification()), + builder: (context) => Species( + addr: addr, + arrTypes: types, + )), + ); + }, + style: FilledButton.styleFrom(backgroundColor: Colors.green), + child: Text( + 'Dictionary', + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), + ), + + + const SizedBox(height: 30), + + + FilledButton( + onPressed: () async { + if (!isAuthenticated) { + isAuthenticated = await BiometricHelper().authenticate(); + setState(() {}); + } + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const HomePage()), ); }, style: FilledButton.styleFrom(backgroundColor: Colors.green), - child: const Text('DISEASE CLASSIFICATION'), + child: Text( + 'Help', + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w300, + color: Colors.white, + fontSize: 25, + fontStyle: FontStyle.normal), + ), ), ], ), diff --git a/lib/help.dart b/lib/help.dart new file mode 100644 index 0000000..d41d196 --- /dev/null +++ b/lib/help.dart @@ -0,0 +1,111 @@ +import 'package:plant_sense_1/BardAIController.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + BardAIController controller = Get.put(BardAIController()); + TextEditingController textField = TextEditingController(); + return Scaffold( + backgroundColor: const Color(0xfff2f1f9), + appBar: AppBar( + centerTitle: true, + leading: SvgPicture.asset( + "assets/bard_logo.svg", + width: 10, + ), + title: Text( + "Support", + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 40, + color: Colors.white, + fontStyle: FontStyle.italic), + ), + backgroundColor: Colors.green, + actions: [ + IconButton( + onPressed: () { + controller.sendPrompt("Hello what can you do for me "); + }, + icon: const Icon(Icons.security)) + ], + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Expanded( + child: ListView( + children: [ + Obx(() => Column( + children: controller.historyList + .map( + (e) => Container( + margin: const EdgeInsets.symmetric(vertical: 10), + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Text(e.system == "user" ? "👨‍💻" : "🤖"), + const SizedBox(width: 10), + Flexible(child: Text(e.message)), + ], + ), + ), + ) + .toList(), + )) + ], + )), + Container( + decoration: BoxDecoration( + color: Colors.blueAccent.withOpacity(0.5), + borderRadius: BorderRadius.circular(10), + ), + height: 60, + child: Row(children: [ + Expanded( + child: TextFormField( + controller: textField, + decoration: const InputDecoration( + hintText: "Ask me anything", + border: OutlineInputBorder( + borderSide: BorderSide.none, + )), + ), + ), + Obx( + () => controller.isLoading.value + ? const CircularProgressIndicator() + : IconButton( + onPressed: () { + if (textField.text != "") { + controller.sendPrompt(textField.text); + textField.clear(); + } + }, + icon: const Icon( + Icons.send, + color: Colors.white, + )), + ), + const SizedBox(width: 10) + ]), + ), + const SizedBox(height: 10), + ], + ), + ), + ); + } +} diff --git a/lib/image_input.dart b/lib/image_input.dart index 2913ec3..38e15d9 100644 --- a/lib/image_input.dart +++ b/lib/image_input.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; +import 'package:google_fonts/google_fonts.dart'; class ImageInput extends StatefulWidget { const ImageInput({super.key, required this.onPickImage}); @@ -36,7 +37,7 @@ class _ImageInputState extends State { _takenImage = File(pickedImage.path); }); - widget.onPickImage(_takenImage!); + widget.onPickImage(_takenImage!); } void _selectAgain() { @@ -55,9 +56,13 @@ class _ImageInputState extends State { FilledButton.icon( onPressed: _takePictureFromCamera, icon: const Icon(Icons.camera), - label: const Text( + label: Text( 'Take picture', - style: TextStyle(fontSize: 25), + style: GoogleFonts.ebGaramond( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic), ), style: FilledButton.styleFrom( backgroundColor: Colors.green, @@ -69,9 +74,13 @@ class _ImageInputState extends State { FilledButton.icon( onPressed: _takePictureFromGallery, icon: const Icon(Icons.camera), - label: const Text( - 'Select Picture From Gallery', - style: TextStyle(fontSize: 25), + label: Text( + 'Select From Gallery', + style: GoogleFonts.ebGaramond( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic), ), style: FilledButton.styleFrom( backgroundColor: Colors.green, diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d472d4b --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'entry_screen.dart'; + +Future main() async { + await dotenv.load(); + runApp( + MaterialApp( + home: Scaffold( + body: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [ + Color.fromARGB(255, 41, 36, 36), + Color.fromARGB(255, 127, 60, 39), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + child: const Entry(), + ), + ), + debugShowCheckedModeBanner: false, + ), + ); +} diff --git a/lib/moistureSensor.dart b/lib/moistureSensor.dart new file mode 100644 index 0000000..d7eb89e --- /dev/null +++ b/lib/moistureSensor.dart @@ -0,0 +1,146 @@ +// ignore_for_file: file_names + +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'dart:core'; +import 'package:dart_date/dart_date.dart'; +import 'package:plant_sense_1/data.dart'; + +class MoistureSensor extends StatefulWidget { + const MoistureSensor({super.key}); + + @override + State createState() => _MoistureSensorState(); +} + +class _MoistureSensorState extends State { + List reading = ["No Data Available!"]; + String wetSoil = "Soil is wet!"; + String drySoil = "Soil is dry!"; + List timestamp = ['No Data Available!']; + List entryNumber = ["No Data Available!"]; + + void takeReading() async { + String? addr = MOISTURESENSOR; + Uri url = Uri.parse(addr!); + + final response = await http.get(url); + var jsonResponse = jsonDecode(response.body); + var feeds = jsonResponse['feeds']; + int size = feeds.length; + + timestamp.clear(); + entryNumber.clear(); + reading.clear(); + + for (int i = size - 1; i >= max(0, size - 10); i--) { + setState(() { + timestamp.add(DateTime.parse(feeds[i]['created_at']).toHumanString()); + entryNumber.add(feeds[i]['entry_id'].toString()); + reading.add(feeds[i]['field1'] == 1 ? drySoil : wetSoil); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color.fromARGB(255, 127, 60, 39), + appBar: AppBar( + title: Text( + "Moisture Sensor", + style: GoogleFonts.ebGaramond( + fontWeight: FontWeight.w700, + fontSize: 30, + color: Colors.white, + fontStyle: FontStyle.normal), + ), + backgroundColor: Colors.green, + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: ListView.builder( + shrinkWrap: true, + // controller: ScrollController, + itemCount: reading.length, + scrollDirection: Axis.vertical, + itemBuilder: (context, index) { + return Container( + color: Colors.yellow, + child: Column( + children: [ + const SizedBox(height: 10), + Text( + "Entry Number : ${entryNumber[index]}", + style: GoogleFonts.ebGaramond( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + ), + textAlign: TextAlign.left, + ), + const SizedBox(height: 10), + Text( + "Timestamp : ${timestamp[index]}", + style: GoogleFonts.ebGaramond( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + ), + textAlign: TextAlign.left, + ), + const SizedBox(height: 10), + Text( + reading[index], + style: GoogleFonts.ebGaramond( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + ), + textAlign: TextAlign.justify, + ), + const Divider( + color: Colors.black, + thickness: 2, + ), + ], + ), + ); + }, + ), + ), + const SizedBox( + height: 20, + ), + FilledButton( + onPressed: takeReading, + style: FilledButton.styleFrom(backgroundColor: Colors.green), + child: Text( + 'Refresh', + style: GoogleFonts.ebGaramond( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.italic, + ), + ), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ); + } +} diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..8fcbc71 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,31 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plant_sense_1/entry_screen.dart'; + +// import 'package:plant_sense_1/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const Entry()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}