diff --git a/assets/images/Final_Diagnosis.png b/assets/images/Final_Diagnosis.png new file mode 100644 index 0000000..dc9b6d2 Binary files /dev/null and b/assets/images/Final_Diagnosis.png differ diff --git a/lib/api/repositories/product_repository.dart b/lib/api/repositories/product_repository.dart index d999c86..29341c7 100644 --- a/lib/api/repositories/product_repository.dart +++ b/lib/api/repositories/product_repository.dart @@ -82,7 +82,7 @@ class ProductRepository implements IProduct { List model = []; - for (var item in response.data['products']) { + for (var item in response.data['product']) { model.add(ProductModels.fromJson(item)); } diff --git a/lib/src/app/home/home_page.dart b/lib/src/app/home/home_page.dart index f4cc4d4..0a43fb9 100644 --- a/lib/src/app/home/home_page.dart +++ b/lib/src/app/home/home_page.dart @@ -77,12 +77,6 @@ class HomepageState extends State { ), child: const BannerComponent(hasPromotion: true), ), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: screenHeight * 0.2, - ), - child: const Divider()), - Expert(), ], ), ), diff --git a/lib/src/app/sessions/pharmacy/pharmacy_page.dart b/lib/src/app/sessions/pharmacy/pharmacy_page.dart index 461e0de..56f6c1f 100644 --- a/lib/src/app/sessions/pharmacy/pharmacy_page.dart +++ b/lib/src/app/sessions/pharmacy/pharmacy_page.dart @@ -6,6 +6,7 @@ import 'package:gohealth/api/models/product_models.dart'; import 'package:gohealth/api/repositories/product_repository.dart'; import 'package:gohealth/src/app/sessions/pharmacy/pharmacy_controller.dart'; import 'package:gohealth/src/app/sessions/products/product_page.dart'; +import 'package:url_launcher/url_launcher.dart'; class PharmacyPage extends StatefulWidget { const PharmacyPage({super.key, required this.pharmacy}); @@ -34,6 +35,13 @@ class _PharmacyPageState extends State { } } + void _launchMaps(String address) async { + final url = Uri.parse('https://www.google.com/maps/search/?api=1&query=${Uri.encodeFull(address)}'); + if (await canLaunchUrl(url)) { + await launchUrl(url); + } + } + @override void initState() { super.initState(); @@ -43,115 +51,229 @@ class _PharmacyPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.pharmacy.name!), - ), - body: Padding( - padding: const EdgeInsets.all(0), - child: Column( - children: [ - Container( - height: 200, - decoration: BoxDecoration( - image: DecorationImage( - image: NetworkImage(widget.pharmacy.image ?? - "https://via.placeholder.com/150"), - fit: BoxFit.cover, - ), - ), - ), - SizedBox(height: 16), - Text( - widget.pharmacy.name!, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, + body: CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 200, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + background: Stack( + fit: StackFit.expand, + children: [ + Image.network( + widget.pharmacy.image ?? "https://via.placeholder.com/150", + fit: BoxFit.cover, + ), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.7) + ], + ), + ), + ), + ], ), - ), - SizedBox(height: 8), - Text( - widget.pharmacy.phone!, - style: TextStyle( - fontSize: 16, - color: Colors.grey[600], + title: Text( + widget.pharmacy.name!, + style: TextStyle(color: Colors.white), ), ), - SizedBox(height: 16), - Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: Text( - "Produtos disponíveis na ${widget.pharmacy.name!}", - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Card( + elevation: 4, + child: Container( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 0.9, + minWidth: MediaQuery.of(context).size.width * 0.9, + ), + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Icon(Icons.phone, color: Colors.blue), + SizedBox(width: 8), + Text( + widget.pharmacy.phone!, + style: TextStyle( + color: Color(0xff1d1b20), + fontSize: 12, + fontWeight: FontWeight.w400, + letterSpacing: 0.3, + height: 1.4, + leadingDistribution: + TextLeadingDistribution.even, + ), + ), + ], + ), + SizedBox(width: 16), + Row( + children: [ + Icon(Icons.location_on, color: Colors.blue), + SizedBox(width: 8), + GestureDetector( + onTap: () => _launchMaps(widget.pharmacy.geolocation!.address ?? ""), + child: Text( + (widget.pharmacy.geolocation!.address ?? "").length > 19 + ? "${widget.pharmacy.geolocation!.address!.substring(0, 19)}..." + : widget.pharmacy.geolocation!.address ?? "", + style: TextStyle( + color: Color(0xff1d1b20), + fontSize: 12, + fontWeight: FontWeight.w400, + letterSpacing: 0.3, + height: 1.4, + decoration: TextDecoration.underline, + leadingDistribution: TextLeadingDistribution.even, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ], ), ), ), - ListView.builder( - shrinkWrap: true, - itemCount: products.length, - itemBuilder: (context, index) { + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + hintText: 'Buscar produtos...', + prefixIcon: Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30), + ), + filled: true, + fillColor: Colors.grey[100], + ), + ), + ], + ), + ), + ), + products.isEmpty + ? SliverFillRemaining( + child: Center( + child: CircularProgressIndicator(), + ), + ) + : SliverPadding( + padding: EdgeInsets.all(16), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 0.75, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + ), + delegate: SliverChildBuilderDelegate( + (context, index) { return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( - builder: (context) => ProductPage( - productModels: products[index], - ), + builder: (context) => + ProductPage(productModels: products[index]), ), ); }, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), + child: Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - CircleAvatar( - backgroundColor: Colors.primaries[Random() - .nextInt(Colors.primaries.length)], - radius: 50, - child: products[index].image != null - ? ClipOval( - child: Image.network( - products[index].image!, - fit: BoxFit.cover, + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + color: Colors.primaries[Random() + .nextInt(Colors.primaries.length)], + ), + child: products[index].image != null + ? ClipRRect( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12), + ), + child: Image.network( + products[index].image!, + fit: BoxFit.cover, + width: double.infinity, + ), + ) + : Center( + child: Icon( + Icons.medication, + size: 40, + color: Colors.white, + ), ), - ) - : Text( - products[index].name!, - style: const TextStyle( - color: Colors.white, - fontSize: 10, - ), - ), + ), ), - Text(products[index].name!), - const SizedBox(height: 5), - Text( - "R\$" + products[index].price!.toString(), - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, + Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + products[index].name!, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + SizedBox(height: 8), + Text( + "R\$ ${products[index].price!.toStringAsFixed(2)}", + style: TextStyle( + color: Colors.blue, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + ], ), ), - const SizedBox(height: 5), ], ), ), ); }, + childCount: products.length, ), - ], + ), ), - ), - ), - ], - ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () {}, + label: Text('Carrinho'), + icon: Icon(Icons.shopping_cart), + backgroundColor: Colors.blue, ), ); } diff --git a/lib/src/app/sessions/products/products_list_page.dart b/lib/src/app/sessions/products/products_list_page.dart index 58a7d38..be072ea 100644 --- a/lib/src/app/sessions/products/products_list_page.dart +++ b/lib/src/app/sessions/products/products_list_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; +import 'package:gohealth/api/models/product_models.dart'; import 'package:gohealth/api/repositories/product_repository.dart'; import 'package:gohealth/src/app/home/home_page.dart'; import 'package:gohealth/src/app/sessions/products/product_page.dart'; @@ -29,7 +30,10 @@ class _ProductsListPageState extends State { builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { + } + + // Handle error state + if (snapshot.hasError) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -44,52 +48,143 @@ class _ProductsListPageState extends State { ], ), ); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + } + + // Handle null or empty data + if (!snapshot.hasData || snapshot.data == null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.info_outline, color: Colors.grey, size: 64), + SizedBox(height: 16), + Text( + 'Nenhum produto encontrado', + style: TextStyle(fontSize: 18, color: Colors.grey), + textAlign: TextAlign.center, + ), + ], + ), + ); + } + + // Convert data to List if needed + final products = snapshot.data as List; + + if (products.isEmpty) { SchedulerBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Nenhum produto encontrado'), - duration: Duration(seconds: 3), - backgroundColor: Colors.orange, - ), - ); - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => const Homepage(), + content: Text('Nenhum produto disponível'), + duration: Duration(seconds: 2), ), ); }); - return SizedBox.shrink(); - } else { - return ListView.builder( - itemCount: snapshot.data!.length, + return Center( + child: Text('Lista vazia'), + ); + } + + // Return your list view with the products + return Padding( + padding: EdgeInsets.all(16), + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 0.7, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + ), + itemCount: products.length, itemBuilder: (context, index) { - final product = snapshot.data![index]; - return GestureDetector( + final product = products[index]; + return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => - ProductPage(productModels: snapshot.data![index]), + ProductPage(productModels: product), ), ); }, - child: Card( - child: ListTile( - leading: Image.network( - product.image ?? 'https://via.placeholder.com/150', - width: 50, - ), - title: Text(product.name!), - subtitle: Text('R\$ ${product.price.toString()}'), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.08), + blurRadius: 8, + offset: Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: ClipRRect( + borderRadius: BorderRadius.vertical( + top: Radius.circular(12)), + child: Hero( + tag: product.id!, + child: Image.network( + product.image!, + width: double.infinity, + fit: BoxFit.cover, + ), + ), + ), + ), + Expanded( + flex: 2, + child: Padding( + padding: EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + product.name!, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'R\$ ${product.price!.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w600, + ), + ), + Icon( + Icons.arrow_forward_ios, + size: 16, + color: Colors.grey[400], + ), + ], + ), + ], + ), + ), + ), + ], ), ), ); }, - ); - } + ), + ); }, )); } diff --git a/lib/src/components/checklist/ChecklistPage.dart b/lib/src/components/checklist/ChecklistPage.dart index fe89d66..699bc1c 100644 --- a/lib/src/components/checklist/ChecklistPage.dart +++ b/lib/src/components/checklist/ChecklistPage.dart @@ -31,42 +31,100 @@ class _ChecklistPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Checklist de Sintomas'), + title: const Text( + 'Checklist de Sintomas', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + backgroundColor: Colors.blueAccent, + elevation: 2, ), - body: ListView.builder( - itemCount: widget.symptoms.sintomas!.length, - itemBuilder: (context, index) { - return CheckboxListTile( - title: Text(widget.symptoms.sintomas![index]), - value: _checked[index], - onChanged: (value) { - setState(() { - _checked[index] = value; - }); - }, - ); - }, + body: Column( + children: [ + Container( + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.orange[50], + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.orange[300]!), + ), + child: const Row( + children: [ + Icon(Icons.warning_amber_rounded, color: Colors.orange), + SizedBox(width: 12), + Expanded( + child: Text( + 'Atenção: As informações fornecidas são apenas sugestivas. Consulte sempre um profissional de saúde para diagnóstico preciso.', + style: TextStyle( + color: Colors.orange, + fontSize: 14, + ), + ), + ), + ], + ), + ), + Expanded( + child: ListView.builder( + itemCount: widget.symptoms.sintomas!.length, + itemBuilder: (context, index) { + return Card( + margin: + const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: CheckboxListTile( + title: Text( + widget.symptoms.sintomas![index], + style: TextStyle( + fontSize: 16, + color: Colors.grey[800], + ), + ), + value: _checked[index], + onChanged: (value) { + setState(() { + _checked[index] = value; + }); + }, + activeColor: Colors.blueAccent, + checkColor: Colors.white, + subtitle: Text( + 'Marque se você apresenta este sintoma', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ), + ); + }, + ), + ), + ], ), - floatingActionButton: FloatingActionButton( - onPressed: () { + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + List array = []; - List array = []; - - for (int i = 0; i < _checked.length; i++) { - if (_checked[i] == true) { - array.add(widget.symptoms.sintomas![i]); + for (int i = 0; i < _checked.length; i++) { + if (_checked[i] == true) { + array.add(widget.symptoms.sintomas![i]); + } } - } - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => Diagnois(array: array), - ), - ); - }, - child: const Icon(Icons.send), - ), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Diagnois(array: array), + ), + ); + }, + icon: const Icon(Icons.check_circle_outline), + label: const Text('Confirmar'), + backgroundColor: Colors.blueAccent, + ), ); } } \ No newline at end of file diff --git a/lib/src/components/checklist/Diagnois.dart b/lib/src/components/checklist/Diagnois.dart index c7e9c93..d812ef7 100644 --- a/lib/src/components/checklist/Diagnois.dart +++ b/lib/src/components/checklist/Diagnois.dart @@ -48,33 +48,101 @@ class _DiagnoisState extends State { : Column( children: [ Container( - margin: EdgeInsets.only(top: 16), - child: Image.asset( - 'assets/images/title_image.png', - height: 100, - width: 100, + width: 300, + margin: EdgeInsets.symmetric(vertical: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.asset( + 'assets/images/Final_Diagnosis.png', + fit: BoxFit.cover, + ), + ), ), - ), - Text( - 'Titulo: ${_diagnosticDataRequest!.title}', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.blue, - ), - ), - Text( - 'Probabilidade: ${_diagnosticDataRequest!.score}', - style: TextStyle( - fontSize: 16, - color: Colors.green, - ), - ), - Text( - 'Descrição: ${_diagnosticDataRequest!.description}', - style: TextStyle( - fontSize: 16, - color: Colors.black, + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Colors.blue[50]!, Colors.white], + ), + ), + child: Card( + elevation: 8, + margin: EdgeInsets.all(16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: Colors.blue[100]!, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.medical_services_outlined, + color: Colors.blue[700], + size: 28, + ), + SizedBox(width: 12), + Expanded( + child: Text( + '${_diagnosticDataRequest!.title}', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.blue[700], + letterSpacing: 0.5, + ), + ), + ), + ], + ), + SizedBox(height: 16), + Container( + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.blue[50], + borderRadius: BorderRadius.circular(10), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Diagnóstico Sugerido', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.blue[900], + ), + ), + SizedBox(height: 8), + Text( + '${_diagnosticDataRequest!.description}', + style: TextStyle( + fontSize: 15, + color: Colors.grey[800], + height: 1.5, + ), + ), + ], + ), + ), + ], + ), + ), + ), ), ), ], diff --git a/lib/src/components/checklist/Expert.dart b/lib/src/components/checklist/Expert.dart index 6128a6d..6162c46 100644 --- a/lib/src/components/checklist/Expert.dart +++ b/lib/src/components/checklist/Expert.dart @@ -12,12 +12,27 @@ class _ExpertState extends State { final ExpertDoctor _expertDoctor = ExpertDoctor(); final TextEditingController _textController = TextEditingController(); bool _isLoading = false; + bool _isTextEmpty = true; SymptomsDataRequest symptoms = SymptomsDataRequest(); @override void initState() { super.initState(); + _textController.addListener(_onTextChanged); + } + + void _onTextChanged() { + setState(() { + _isTextEmpty = _textController.text.trim().isEmpty; + }); + } + + @override + void dispose() { + _textController.removeListener(_onTextChanged); + _textController.dispose(); + super.dispose(); } Future _fetchSymptoms() async { @@ -36,59 +51,110 @@ class _ExpertState extends State { @override Widget build(BuildContext context) { - return (_expertDoctor.isConnected() == true) - ? Column( - children: [ - TextField( - controller: _textController, - decoration: InputDecoration( - labelText: 'Digite o que você está sentindo', - border: OutlineInputBorder(), + return Scaffold( + appBar: AppBar( + title: const Text( + 'Consultor de Saúde Virtual', + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.blueAccent, + ), + ), + centerTitle: true, + elevation: 0, + backgroundColor: Colors.white, + ), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Como posso ajudar você hoje?', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.blueAccent, + ), + ), + const SizedBox(height: 12), + Text( + 'Descreva seus sintomas com detalhes para uma análise mais precisa.', + style: TextStyle( + fontSize: 16, + color: Colors.grey[900], + ), + ), + const SizedBox(height: 24), + TextField( + controller: _textController, + maxLines: 3, + style: TextStyle(fontSize: 16, color: Colors.grey[600]), + decoration: InputDecoration( + labelText: 'Sintomas', + hintText: + 'Ex: Estou sentindo dor de cabeça e febre há 2 dias...', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.blueAccent.shade200), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.blueAccent, width: 2), + ), + filled: true, + fillColor: Colors.grey[50], + ), ), - ), - const SizedBox(height: 10), - ElevatedButton( - onPressed: _isLoading || _textController.text.isEmpty - ? null - : () async { - setState(() { - _isLoading = true; - }); - await _fetchSymptoms(); - setState(() { - _isLoading = false; - }); - if (symptoms.sintomas!.isNotEmpty) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - ChecklistPage(symptoms: symptoms)), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Erro: Nenhum sintoma encontrado. Tente novamente.'), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton.icon( + onPressed: (_isLoading || _isTextEmpty) + ? null + : () async { + setState(() { + _isLoading = true; + }); + await _fetchSymptoms(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + ChecklistPage(symptoms: symptoms)), + ); + }, + icon: _isLoading + ? SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, ), - ); - } - }, - child: _isLoading - ? CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(Colors.white), - ) - : const Text('Enviar'), - ), + ) + : Icon(Icons.health_and_safety), + label: Text( + _isLoading ? 'Analisando...' : 'Analisar Sintomas', + style: TextStyle(fontSize: 16), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), ], - ) - : Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Aplicação em manutenção'), - ], - ), + ), + ), + ), ); } } diff --git a/lib/src/components/header_bar.dart b/lib/src/components/header_bar.dart index fde6cb1..88a2e59 100644 --- a/lib/src/components/header_bar.dart +++ b/lib/src/components/header_bar.dart @@ -17,7 +17,7 @@ class HeaderBarState extends StatefulWidget implements PreferredSizeWidget { class _HeaderBarState extends State { final _repository = SharedLocalStorageService(); - + final _searchController = TextEditingController(); String? name; UserModels? profile; String? productLength; @@ -38,144 +38,141 @@ class _HeaderBarState extends State { }); } + void _handleSearch(String value) { + if (value.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Digite algo para pesquisar'), + backgroundColor: Colors.red, + duration: Duration(seconds: 2), + ), + ); + return; + } + + if (value.contains(RegExp(r'[!@#<>?":_`~;[\]\\|=+)(*&^%0-9-]'))) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Caracteres especiais não são permitidos'), + backgroundColor: Colors.red, + duration: Duration(seconds: 2), + ), + ); + return; + } + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductsListPage( + searchText: value, + ), + ), + ); + } + + void _navigateToCart() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CartPage(), + ), + ); + } + @override Widget build(BuildContext context) { return AppBar( - backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + flexibleSpace: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + const Color(0xFF00A3FF), + const Color(0xFF0072BB), + ], + ), + ), + ), + elevation: 4, toolbarHeight: 85.0, - title: Row( - children: [ - Expanded( - child: SizedBox( - height: 43, - child: Padding( - padding: const EdgeInsets.only(left: 0), - child: TextField( - textAlignVertical: TextAlignVertical - .center, // Alinha o texto verticalmente ao centro - decoration: InputDecoration( - hintText: 'Pesquise aqui', - hintStyle: const TextStyle( - color: Colors.white), // Define a cor do texto do hint - prefixIcon: const Icon(Icons.search, - color: Colors.white), // Define a cor do ícone - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Colors.white), // Define a cor da borda - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Colors - .white), // Define a cor da borda quando habilitado - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Colors - .white), // Define a cor da borda quando focado - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 10.0), // Ajusta o preenchimento interno - ), - style: const TextStyle( - color: Colors.white), // Define a cor do texto - onSubmitted: (value) { - if (value.isEmpty) { - return; - } - - if (value.length < 3) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Digite pelo menos 3 caracteres'), - backgroundColor: Colors.red, - duration: Duration(seconds: 2), - ), - ); - return; - } - - if (value.contains( - RegExp(r'[!@#<>?":_`~;[\]\\|=+)(*&^%0-9-]'))) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: - Text('Caracteres especiais não são permitidos'), - backgroundColor: Colors.red, - duration: Duration(seconds: 2), - ), - ); - return; - } - - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => ProductsListPage( - searchText: value, - ), - ), - ); - }, - ), - ), + title: SizedBox( + height: 43, + child: TextField( + controller: _searchController, + textAlignVertical: TextAlignVertical.center, + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: 'Pesquisar produtos...', + prefixIcon: Icon(Icons.search, color: Colors.grey), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide.none, ), + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 0), ), - ], + onSubmitted: _handleSearch, + ), ), actions: [ - IconButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => const CartPage())); - }, - icon: const Icon(Icons.shopping_cart)), Stack( - children: [ + alignment: Alignment.center, + children: [ IconButton( - icon: Icon(Icons.notifications), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => - ProductReserve(userModels: profile!), - ), - ); - }, + icon: Icon( + Icons.shopping_cart_outlined, + color: Colors.white, + size: 28, + ), + onPressed: _navigateToCart, ), - Positioned( - right: 0, - child: Container( - padding: EdgeInsets.all(0.7), - decoration: (profile?.products == true) - ? BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.circular(6), - ) - : null, - constraints: BoxConstraints( - minWidth: 12, - minHeight: 12, - ), - child: Text( - productLength ?? '', - style: TextStyle( - color: Colors.white, - fontSize: 8, + if (productLength != null && productLength!.isNotEmpty) + Positioned( + right: 8, + top: 8, + child: Container( + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + child: Text( + productLength!, + style: TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), ), - textAlign: TextAlign.center, ), ), - ), ], - ) + ), + IconButton( + icon: Icon( + Icons.notifications_outlined, + color: Colors.white, + size: 28, + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProductReserve(userModels: profile!)), + ); + }, + ), + const SizedBox(width: 8), ], - iconTheme: const IconThemeData(color: Colors.white), ); } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } } diff --git a/lib/src/components/side_menu.dart b/lib/src/components/side_menu.dart index 79ecb54..6d07e7c 100644 --- a/lib/src/components/side_menu.dart +++ b/lib/src/components/side_menu.dart @@ -6,6 +6,7 @@ import 'package:gohealth/src/app/home/home_page.dart'; import 'package:gohealth/src/app/home/maps/maps_page.dart'; import 'package:gohealth/src/app/home/profile/profile_page.dart'; import 'package:gohealth/src/app/splash_page.dart'; +import 'package:gohealth/src/components/checklist/Expert.dart'; class SideMenu extends StatefulWidget { const SideMenu({super.key}); @@ -84,9 +85,12 @@ class SideMenuState extends State { }, ), ListTile( - title: const Text('Configuração', style: textStyle), + title: const Text('Diagnostico Online', style: textStyle), onTap: () { - Navigator.pop(context); + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => Expert())); }, ), ListTile( diff --git a/pubspec.lock b/pubspec.lock index fd6b645..006b581 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -821,6 +821,70 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" + url: "https://pub.dev" + source: hosted + version: "6.3.14" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + url: "https://pub.dev" + source: hosted + version: "3.1.3" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 69652ed..733ce5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: flutter_background_service: ^5.0.10 flutter_local_notifications: ^17.2.2 image_picker: ^1.1.2 + url_launcher: ^6.3.1 dev_dependencies: flutter_test: @@ -35,6 +36,7 @@ flutter: - .env - assets/images/First_Session.png - assets/images/Final_Session.png + - assets/images/Final_Diagnosis.png - assets/images/Second_Session.png fonts: - family: Rubik