-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #103 from fga-eps-mds/feat#63/tela_conteudo
Feat#63/tela de visualizar conteúdo
- Loading branch information
Showing
15 changed files
with
462 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import 'package:aranduapp/ui/content/viewmodel/content_viewmodel.dart'; | ||
import 'package:get_it/get_it.dart'; | ||
|
||
void setupContentDI() { | ||
GetIt.I.registerFactory(() => ContentViewModel()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import 'dart:convert'; | ||
|
||
class ContentRequest { | ||
final String title; | ||
final String content; | ||
final String trailID; | ||
|
||
ContentRequest( | ||
{required this.title, required this.content, required this.trailID}); | ||
|
||
Map<String, dynamic> toJson() { | ||
return <String, dynamic>{ | ||
'title': title, | ||
'content': content, | ||
'trailID': trailID, | ||
}; | ||
} | ||
|
||
factory ContentRequest.fromJsonString(String jsonString) { | ||
final json = jsonDecode(jsonString); | ||
|
||
return ContentRequest( | ||
title: json['title']! as String, | ||
content: json['content'] as String, | ||
trailID: json['trailID'] as String, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
class ContentResponse { | ||
final String id; | ||
final String title; | ||
final String content; | ||
final String trail; | ||
final int order; | ||
final DateTime createdAt; | ||
final DateTime updatedAt; | ||
|
||
ContentResponse({ | ||
required this.id, | ||
required this.title, | ||
required this.content, | ||
required this.trail, | ||
required this.order, | ||
required this.createdAt, | ||
required this.updatedAt, | ||
}); | ||
|
||
factory ContentResponse.fromJson(Map<String, dynamic> json) { | ||
return ContentResponse( | ||
id: json['_id'], | ||
title: json['title'], | ||
content: json['content'], | ||
trail: json['trail'], | ||
order: json['order'], | ||
createdAt: DateTime.parse(json['createdAt']), | ||
updatedAt: DateTime.parse(json['updatedAt']), | ||
); | ||
} | ||
|
||
static fromJsonString(contentJson) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import 'package:aranduapp/core/log/log.dart'; | ||
import 'package:aranduapp/core/network/studio_maker_api.dart'; | ||
import 'package:dio/dio.dart'; | ||
|
||
class ContentService { | ||
Future<List<Map<String, dynamic>>?> getContentsByTrail(String trailId) async { | ||
try { | ||
String path = '/contents/trail/$trailId'; | ||
Response response = await StudioMakerApi.getInstance().get(path: path); | ||
if (response.statusCode == 200 && response.data != null) { | ||
return List<Map<String, dynamic>>.from(response.data); | ||
} else { | ||
Log.e('Erro ao buscar conteúdos da trilha:${response.statusCode}'); | ||
return null; | ||
} | ||
} catch (e) { | ||
Log.e('Erro na requisição $e'); | ||
return null; | ||
} | ||
} | ||
|
||
Future<Map<String, dynamic>?> getContentsById(String contentId) async { | ||
try { | ||
String path = '/contents/$contentId'; | ||
Response response = await StudioMakerApi.getInstance().get(path: path); | ||
if (response.statusCode == 200 && response.data != null) { | ||
return Map<String, dynamic>.from(response.data); | ||
} else { | ||
Log.e('Erro ao buscar conteúdos da trilha:${response.statusCode}'); | ||
return null; | ||
} | ||
} catch (e) { | ||
Log.e('Erro na requisição $e'); | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_markdown/flutter_markdown.dart'; | ||
import 'package:flutter_markdown_latex/flutter_markdown_latex.dart'; | ||
import 'package:markdown/markdown.dart' as md; | ||
import 'package:provider/provider.dart'; | ||
import 'package:aranduapp/ui/content/service/content_service.dart'; | ||
import 'package:aranduapp/ui/content/viewmodel/content_viewmodel.dart'; | ||
|
||
class ContentView extends StatelessWidget { | ||
final String contentID; | ||
|
||
const ContentView({super.key, required this.contentID}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final ContentService contentService = ContentService(); | ||
|
||
return ChangeNotifierProvider( | ||
create: (context) => ContentViewModel(), | ||
child: Scaffold( | ||
appBar: AppBar( | ||
title: FutureBuilder<Map<String, dynamic>?>( | ||
future: contentService.getContentsById(contentID), | ||
builder: (context, snapshot) { | ||
if (snapshot.connectionState == ConnectionState.waiting) { | ||
return const Text("Carregando..."); | ||
} else if (snapshot.hasError) { | ||
return const Text("Erro"); | ||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) { | ||
return const Text("Conteúdo não encontrado"); | ||
} | ||
final content = snapshot.data!; | ||
return Text(content['title']); | ||
}, | ||
), | ||
backgroundColor: Theme.of(context).colorScheme.onPrimary, | ||
elevation: 0, | ||
foregroundColor: Theme.of(context).colorScheme.onSurface, | ||
leading: IconButton( | ||
icon: const Icon(Icons.arrow_back), | ||
onPressed: () { | ||
Navigator.pop(context); | ||
}, | ||
color: Theme.of(context).colorScheme.onSurface, | ||
), | ||
), | ||
body: FutureBuilder<Map<String, dynamic>?>( | ||
future: contentService.getContentsById(contentID), | ||
builder: (context, snapshot) { | ||
if (snapshot.connectionState == ConnectionState.waiting) { | ||
return const Center(child: CircularProgressIndicator()); | ||
} else if (snapshot.hasError) { | ||
return const Center(child: Text("Erro ao carregar conteúdo")); | ||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) { | ||
return const Center(child: Text("Conteúdo não encontrado")); | ||
} | ||
|
||
final content = snapshot.data!; | ||
|
||
return Consumer<ContentViewModel>( | ||
builder: (context, viewModel, child) { | ||
return Column( | ||
children: [ | ||
// Barra de progresso | ||
SizedBox( | ||
height: 10.0, | ||
child: LinearProgressIndicator( | ||
value: viewModel.progress, | ||
backgroundColor: Colors.grey[300], | ||
valueColor: AlwaysStoppedAnimation<Color>( | ||
Theme.of(context).colorScheme.primary, | ||
), | ||
), | ||
), | ||
Expanded( | ||
child: SingleChildScrollView( | ||
controller: viewModel.scrollController, | ||
padding: const EdgeInsets.all(16.0), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: [ | ||
_renderMarkdown(content['content']), | ||
// Botão "Finalizar" sempre visível no final | ||
Padding( | ||
padding: | ||
const EdgeInsets.symmetric(vertical: 16.0), | ||
child: ElevatedButton( | ||
style: ElevatedButton.styleFrom( | ||
backgroundColor: | ||
Theme.of(context).colorScheme.primary, | ||
), | ||
onPressed: () { | ||
_showCompletionDialog(context); | ||
}, | ||
child: Text( | ||
'Finalizar', | ||
style: Theme.of(context) | ||
.textTheme | ||
.bodyLarge | ||
?.apply( | ||
color: Theme.of(context) | ||
.colorScheme | ||
.onPrimary, | ||
), | ||
), | ||
), | ||
), | ||
], | ||
), | ||
), | ||
), | ||
], | ||
); | ||
}, | ||
); | ||
}, | ||
), | ||
), | ||
); | ||
} | ||
|
||
Widget _renderMarkdown(String markdownContent) { | ||
return MarkdownBody( | ||
data: markdownContent, | ||
builders: { | ||
'latex': LatexElementBuilder(), | ||
}, | ||
extensionSet: md.ExtensionSet( | ||
[LatexBlockSyntax()], | ||
[LatexInlineSyntax()], | ||
), | ||
); | ||
} | ||
|
||
void _showCompletionDialog(BuildContext context) { | ||
showDialog( | ||
context: context, | ||
builder: (BuildContext dialogContext) { | ||
return AlertDialog( | ||
content: SizedBox( | ||
width: 250, | ||
height: 150, | ||
child: Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: [ | ||
Text( | ||
"Conteúdo Finalizado! Voltando para a trilha.", | ||
textAlign: TextAlign.center, | ||
style: Theme.of(context) | ||
.textTheme | ||
.bodyLarge | ||
?.apply(color: Theme.of(context).colorScheme.onSurface), | ||
), | ||
const SizedBox(height: 20), | ||
ElevatedButton( | ||
style: ElevatedButton.styleFrom( | ||
backgroundColor: Theme.of(context).colorScheme.primary, | ||
), | ||
onPressed: () { | ||
Navigator.of(dialogContext).pop(); | ||
Navigator.of(context).pop(); | ||
}, | ||
child: Text( | ||
'Trilha', | ||
style: Theme.of(context).textTheme.bodyLarge?.apply( | ||
color: Theme.of(context).colorScheme.onPrimary, | ||
), | ||
), | ||
) | ||
], | ||
), | ||
), | ||
); | ||
}, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import 'package:flutter/material.dart'; | ||
|
||
class ContentViewModel extends ChangeNotifier { | ||
final ScrollController scrollController = ScrollController(); | ||
double _progress = 0.0; | ||
|
||
double get progress => _progress; | ||
|
||
bool get shouldShowButton => _progress == 1.0; | ||
|
||
ContentViewModel() { | ||
scrollController.addListener(_updateProgress); | ||
} | ||
|
||
void _updateProgress() { | ||
if (scrollController.hasClients) { | ||
final maxScrollExtent = scrollController.position.maxScrollExtent; | ||
final currentScroll = scrollController.position.pixels; | ||
_progress = (currentScroll / maxScrollExtent).clamp(0.0, 1.0); | ||
notifyListeners(); // Notifica as views sobre a atualização | ||
} | ||
} | ||
|
||
@override | ||
void dispose() { | ||
scrollController.removeListener(_updateProgress); | ||
scrollController.dispose(); | ||
super.dispose(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.