build method
- BuildContext context
override
Builds the main UI for the classification screen.
This method creates a responsive layout that adapts to different states:
- Shows upload hints when no image is selected
- Displays image preview during processing
- Shows classification results with species information
- Provides action buttons for camera, gallery, and reset
The layout uses a Scaffold with an AppBar and a scrollable body containing the main content area with image preview and action buttons.
context The build context for accessing theme and localization data.
Returns a Widget representing the complete classification screen UI.
Implementation
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
return ChangeNotifierProvider<ClassificationViewModel>.value(
value: locator<ClassificationViewModel>(),
child: Scaffold(
appBar: AppBar(
title: Text(localizations.classificationScreenTitle),
centerTitle: true,
),
body: Consumer<ClassificationViewModel>(
builder: (context, viewModel, child) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Visibility(
visible: !viewModel.hasImage,
maintainState: true,
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.info_outline,
color: Colors.teal,
size: 32,
),
const SizedBox(height: 8),
Text(
localizations.uploadImageHint,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 4),
Text(
localizations.uploadImageSubHint,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
),
),
),
if (!viewModel.hasImage) const SizedBox(height: 16),
_buildImagePreview(context, viewModel, localizations),
if (viewModel.errorMessage != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
viewModel.errorMessage!,
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
if (viewModel.state == ClassificationState.success &&
viewModel.result != null)
_buildResultCard(context, viewModel, localizations)
else if (viewModel.state == ClassificationState.submitted &&
viewModel.submissionResult != null)
_buildSubmissionResult(context, viewModel, localizations),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildActionButton(
icon: Icons.camera_alt,
label: localizations.cameraButtonLabel,
onPressed: () => _getImage(context, viewModel,
ImageSource.camera, localizations),
),
_buildActionButton(
icon: Icons.photo_library,
label: localizations.galleryButtonLabel,
onPressed: () => _getImage(context, viewModel,
ImageSource.gallery, localizations),
),
if (viewModel.hasImage)
_buildActionButton(
icon: Icons.refresh,
label: localizations.resetButtonLabel,
onPressed: () => viewModel.reset(),
),
],
),
),
],
),
);
},
),
),
);
}