The monitoring feature provides repository-based operations for managing inspection questions. While there are no explicit use case classes, the repository pattern encapsulates the business logic. This page documents the available operations through the repository interface.
Overview
The monitoring system uses the repository pattern to manage inspection questions. Operations are accessed through the QuestionRepository interface and executed via the QuestionsController state manager.
Available Operations
Get Questions for Apiary
Retrieves all questions configured for a specific apiary.
The ID of the apiary to fetch questions for
Returns: Either<Failure, List<Pregunta>>
Usage:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:softbee/feature/monitoring/presentation/providers/questions_providers.dart';
// In a widget
class QuestionsList extends ConsumerWidget {
final String apiaryId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final questionsState = ref.watch(questionsProvider);
useEffect(() {
ref.read(questionsProvider.notifier).fetchPreguntas(apiaryId);
return null;
}, [apiaryId]);
if (questionsState.isLoading) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: questionsState.preguntas.length,
itemBuilder: (context, index) {
final question = questionsState.preguntas[index];
return ListTile(
title: Text(question.texto),
subtitle: Text(question.categoria ?? ''),
);
},
);
}
}
Create Question
Creates a new inspection question for an apiary.
The question entity to create. The id field can be empty as it will be assigned by the server.
Returns: Either<Failure, Pregunta>
Usage:
import 'package:softbee/feature/monitoring/domain/entities/question_model.dart';
final newQuestion = Pregunta(
id: '', // Will be assigned by server
apiarioId: 'apiary123',
texto: '¿Presencia de varroa?',
tipoRespuesta: 'seleccion',
categoria: 'Salud',
obligatoria: true,
opciones: ['Sí', 'No'],
orden: 10,
activa: true,
);
// Using the controller
await ref.read(questionsProvider.notifier).createPregunta(newQuestion);
Update Question
Updates an existing inspection question.
The question entity with updated values. The id field must be valid.
Returns: Either<Failure, Pregunta>
Usage:
final updatedQuestion = existingQuestion.copyWith(
texto: 'Pregunta actualizada',
obligatoria: false,
);
await ref.read(questionsProvider.notifier).updatePregunta(updatedQuestion);
Delete Question
Deletes an inspection question permanently.
The ID of the question to delete
The apiary ID (used to refresh the list after deletion)
Returns: Either<Failure, void>
Usage:
// Delete a question
await ref.read(questionsProvider.notifier).deletePregunta(
'question123',
'apiary456',
);
Permanent Deletion: This operation cannot be undone. Consider implementing a confirmation dialog before deleting questions.
Reorder Questions
Updates the display order of multiple questions at once.
The ID of the apiary whose questions are being reordered
Ordered list of question IDs. The position in the list determines the new order.
Returns: Either<Failure, void>
Usage:
// Reorder questions - swap first and second
final currentOrder = questionsState.preguntas.map((q) => q.id).toList();
final newOrder = [
currentOrder[1], // Move second to first
currentOrder[0], // Move first to second
...currentOrder.skip(2), // Keep rest the same
];
await ref.read(questionsProvider.notifier).reorderPreguntas(
'apiary123',
newOrder,
);
Drag and Drop: This operation is commonly used with drag-and-drop UI components like ReorderableListView in Flutter.
Load Default Questions
Loads a predefined set of default questions into an apiary. Useful for new apiaries or resetting to standard questions.
The ID of the apiary to load defaults into
Returns: Either<Failure, void>
Usage:
// Load default questions for a new apiary
await ref.read(questionsProvider.notifier).loadDefaults('apiary123');
// After loading, the questions list will be automatically refreshed
Automatic Refresh: After loading defaults, the questions list is automatically refreshed to show the new questions.
Get Question Templates
Retrieves available question templates from the global question bank. Templates can be copied to create new questions.
Returns: Either<Failure, List<Pregunta>>
Usage:
// Fetch available templates
await ref.read(questionsProvider.notifier).fetchTemplates();
// Access templates from state
final templates = ref.watch(questionsProvider).templates;
// Create a question from a template
final newQuestion = templates[0].copyWith(
id: '', // Clear ID
apiarioId: 'apiary123', // Set target apiary
orden: nextOrder, // Set appropriate order
);
await ref.read(questionsProvider.notifier).createPregunta(newQuestion);
State Management
The monitoring feature uses Riverpod for state management. The QuestionsController manages the state and coordinates with the repository.
QuestionsState
The state object contains:
preguntas
List<Pregunta>
default:"[]"
List of questions for the current apiary
templates
List<Pregunta>
default:"[]"
List of available question templates
Whether a loading operation is in progress
Error message if the last operation failed
Example: Complete CRUD Flow
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:softbee/feature/monitoring/domain/entities/question_model.dart';
import 'package:softbee/feature/monitoring/presentation/providers/questions_providers.dart';
class QuestionManagementExample extends ConsumerStatefulWidget {
final String apiaryId;
const QuestionManagementExample({required this.apiaryId});
@override
ConsumerState<QuestionManagementExample> createState() =>
_QuestionManagementExampleState();
}
class _QuestionManagementExampleState
extends ConsumerState<QuestionManagementExample> {
@override
void initState() {
super.initState();
// Load questions on init
Future.microtask(() {
ref.read(questionsProvider.notifier).fetchPreguntas(widget.apiaryId);
});
}
Future<void> _createQuestion() async {
final newQuestion = Pregunta(
id: '',
apiarioId: widget.apiaryId,
texto: '¿Nueva pregunta?',
tipoRespuesta: 'texto',
obligatoria: false,
orden: 999,
);
await ref.read(questionsProvider.notifier).createPregunta(newQuestion);
}
Future<void> _updateQuestion(Pregunta question) async {
final updated = question.copyWith(
texto: 'Pregunta actualizada',
);
await ref.read(questionsProvider.notifier).updatePregunta(updated);
}
Future<void> _deleteQuestion(String id) async {
await ref.read(questionsProvider.notifier).deletePregunta(
id,
widget.apiaryId,
);
}
@override
Widget build(BuildContext context) {
final state = ref.watch(questionsProvider);
if (state.isLoading) {
return Center(child: CircularProgressIndicator());
}
if (state.error != null) {
return Center(child: Text('Error: ${state.error}'));
}
return Scaffold(
appBar: AppBar(title: Text('Gestión de Preguntas')),
body: ListView.builder(
itemCount: state.preguntas.length,
itemBuilder: (context, index) {
final question = state.preguntas[index];
return ListTile(
title: Text(question.texto),
subtitle: Text('Orden: ${question.orden}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _updateQuestion(question),
),
IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteQuestion(question.id),
),
],
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _createQuestion,
child: Icon(Icons.add),
),
);
}
}
Error Handling
All operations return an Either<Failure, T> type, which represents either a failure or a successful result.
final result = await repository.getPreguntas('apiary123');
result.fold(
(failure) {
// Handle error
print('Error: ${failure.message}');
showSnackbar('No se pudieron cargar las preguntas');
},
(questions) {
// Handle success
print('Loaded ${questions.length} questions');
},
);
Common Failure Types
Authentication failure - usually means no valid token is available
Server error - includes the error message from the API
Best Practices
Loading States: Always check isLoading before performing operations to prevent duplicate requests.
Error Display: Show user-friendly error messages using state.error instead of technical error details.
Automatic Refresh: Most mutating operations (create, update, delete) automatically refresh the questions list. Don’t call fetchPreguntas manually after these operations.