Skip to content

Commit

Permalink
fix(#63): Refatora código adicionando listnablebuilder, errorscrean e…
Browse files Browse the repository at this point in the history
… loading.
  • Loading branch information
dylancavalcante authored and GabrielCostaDeOliveira committed Feb 1, 2025
1 parent 7f93567 commit a817e26
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 127 deletions.
3 changes: 3 additions & 0 deletions lib/ui/content/di/di_content.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'package:aranduapp/core/di/locator.dart';
import 'package:aranduapp/ui/content/service/content_service.dart';
import 'package:aranduapp/ui/content/viewmodel/content_viewmodel.dart';
import 'package:get_it/get_it.dart';

void setupContentDI() {
locator.registerLazySingleton(() => ContentService());
GetIt.I.registerFactory(() => ContentViewModel());
}
231 changes: 133 additions & 98 deletions lib/ui/content/view/content_view.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:aranduapp/ui/shared/erro_screen.dart';
import 'package:aranduapp/ui/shared/loading_widget.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart';
import 'package:aranduapp/ui/content/viewmodel/content_viewmodel.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
Expand All @@ -12,117 +15,149 @@ class ContentView extends StatelessWidget {

@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ContentViewModel()..fetchContent(contentID),
child: Scaffold(
appBar: AppBar(
title: Consumer<ContentViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Text("Carregando...");
} else if (viewModel.error != null) {
return const Text("Erro");
} else if (viewModel.content == null) {
return const Text("Conteúdo não encontrado");
}
return Text(viewModel.content!.title);
},
final viewModel = GetIt.instance<ContentViewModel>();
viewModel.contentCommand.execute(contentID);
return ChangeNotifierProvider<ContentViewModel>.value(
value: viewModel,
child: const ContentScreen(),
);
}
}

class ContentScreen extends StatelessWidget {
const ContentScreen({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppBar(context),
body: _buildBody(context),
);
}

PreferredSizeWidget _buildAppBar(BuildContext context) {
return AppBar(
title: _buildAppBarTitle(context),
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,
),
);
}

Widget _buildAppBarTitle(BuildContext context) {
ContentViewModel viewModel = Provider.of<ContentViewModel>(context);

return ListenableBuilder(
listenable: viewModel.contentCommand,
builder: (context, child) {
if (viewModel.contentCommand.running) {
return const Text("Carregando...");
} else if (viewModel.contentCommand.isError) {
return const Text("Erro");
} else if (viewModel.content == null) {
return const Text("Conteúdo não encontrado");
}
return Text(viewModel.content!.title);
},
);
}

Widget _buildBody(BuildContext context) {
final viewModel = Provider.of<ContentViewModel>(context);

return ListenableBuilder(
listenable: viewModel.contentCommand,
builder: (context, child) {
if (viewModel.contentCommand.running) {
return const LoadingWidget();
} else if (viewModel.contentCommand.isError) {
return Center(
child: Text(
viewModel.contentCommand.result!.asError!.error.toString()));
} else if (viewModel.content == null) {
return const ErrorScreen(message: "Conteúdo não encontrado.");
}

return Column(
children: [
_buildProgressBar(context),
_buildContent(context),
],
);
},
);
}

Widget _buildProgressBar(BuildContext context) {
final viewModel = Provider.of<ContentViewModel>(context, listen: true);
return SizedBox(
height: 10.0,
child: LinearProgressIndicator(
value: viewModel.progress,
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
);
}

Widget _buildContent(BuildContext context) {
final viewModel = Provider.of<ContentViewModel>(context, listen: true);
final content = viewModel.content!;

return Expanded(
child: SingleChildScrollView(
controller: viewModel.scrollController,
padding: const EdgeInsets.all(16.0),
child: Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
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,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_renderMarkdown(content.content),
if (viewModel.shouldShowButton) _buildCompletionButton(context),
],
),
),
body: Consumer<ContentViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return const Center(child: CircularProgressIndicator());
} else if (viewModel.error != null) {
return Center(child: Text(viewModel.error!));
} else if (viewModel.content == null) {
return const Center(child: Text("Conteúdo não encontrado"));
}

final content = viewModel.content!;

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,
),
),
),
// Conteúdo rolável
Expanded(
child: SingleChildScrollView(
controller: viewModel.scrollController,
padding: const EdgeInsets.all(16.0),
child: Container(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_renderMarkdown(
content.content), // Renderiza Markdown
if (viewModel.shouldShowButton) ...[
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,
),
),
),
),
],
],
),
),
),
),
],
);
},
),
),
);
}

// Método para renderizar Markdown
Widget _renderMarkdown(String markdownContent) {
return MarkdownBody(
data: markdownContent,
builders: {
'latex': LatexElementBuilder(), // Suporte a LaTeX
'latex': LatexElementBuilder(),
},
extensionSet: md.ExtensionSet(
[LatexBlockSyntax()], // Suporte a blocos LaTeX
[LatexInlineSyntax()], // Suporte a LaTeX inline
[LatexBlockSyntax()],
[LatexInlineSyntax()],
),
);
}

Widget _buildCompletionButton(BuildContext context) {
return 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,
),
),
),
);
}
Expand Down
42 changes: 13 additions & 29 deletions lib/ui/content/viewmodel/content_viewmodel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,39 @@ import 'package:flutter/material.dart';
import 'package:aranduapp/core/state/command.dart';
import 'package:aranduapp/ui/content/service/content_service.dart';
import 'package:aranduapp/ui/content/model/content_request.dart';
// ignore: unused_import
import 'package:async/async.dart';

class ContentViewModel extends ChangeNotifier {
final ContentService _contentService = ContentService();
final ScrollController scrollController = ScrollController();
bool _isLoading = false;
String? _error;
ContentRequest? _content;
double _progress = 0.0;

bool get isLoading => _isLoading;
String? get error => _error;
ContentRequest? get content => _content;
double get progress => _progress;
bool get shouldShowButton => _progress == 1.0;

late Command1<ContentRequest, String>
contentCommand; // Corrigido: T = ContentRequest, A = String
late Command1<ContentRequest, String> contentCommand;

ContentViewModel() {
contentCommand = Command1<ContentRequest, String>((contentId) async {
// Corrigido
try {
final result = await _contentService.getContentsById(contentId);
return result; // Retorna Result<ContentRequest>
} catch (e) {
return Result.error(e);
final result = await _contentService.getContentsById(contentId);
return result;
});

contentCommand.addListener(() {
if (contentCommand.isError) {
_content = null;
} else if (contentCommand.isOk) {
_content = contentCommand.result?.asValue?.value;
}
notifyListeners();
});

scrollController.addListener(_updateProgress);
}

Future<void> fetchContent(String contentID) async {
_isLoading = true;
notifyListeners();

final result = await contentCommand.execute(contentID);
if (result.isValue) {
_content = result.asValue!.value; // Agora é ContentRequest
_error = null;
} else if (result.isError) {
_error = "Erro ao carregar conteúdo: ${result.asError!.error}";
_content = null;
}

_isLoading = false;
notifyListeners();
}

void _updateProgress() {
if (scrollController.hasClients) {
final maxScrollExtent = scrollController.position.maxScrollExtent;
Expand All @@ -65,6 +48,7 @@ class ContentViewModel extends ChangeNotifier {
void dispose() {
scrollController.removeListener(_updateProgress);
scrollController.dispose();
contentCommand.dispose(); // Dispose do Command
super.dispose();
}
}

0 comments on commit a817e26

Please sign in to comment.