Presentation Layer
The presentation layer contains the user interface components of the feature. The presentation layer is responsible for displaying the feature to the user and handling user interactions.
Getting started
Create a new folder named presentation
inside the feature folder. Then create a new file named scratch_note_page.dart
inside the presentation
folder. This file will contain the UI code for the feature.
Write the UI code
We'll start by creating a simple UI for the scratch notes feature. An example of the UI code is shown below:
dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:techshoi_app/configure_dependencies.dart';
import 'package:techshoi_app/features/core/extensions/string_extensions.dart';
import 'package:techshoi_app/features/scratch_notes/application/scratch_note_cubit.dart';
import 'package:techshoi_app/features/settings/application/settings_cubit.dart';
import 'package:techshoi_app/features/techshoi_ui/dialogs/techshoi_dialog.dart';
import 'package:techshoi_app/features/techshoi_ui/styles/colors.dart';
import 'package:techshoi_app/l10n/l10n.dart';
class ScratchNotesPage extends StatelessWidget {
const ScratchNotesPage({super.key});
static String route = '/scratch-notes';
@override
Widget build(BuildContext context) {
return BlocProvider<ScratchNoteCubit>(
create: (context) =>
serviceLocator<ScratchNoteCubit>()..listenForChanges(),
child: const ScratchNotesBody(),
);
}
}
class ScratchNotesBody extends HookWidget {
const ScratchNotesBody({
super.key,
});
@override
Widget build(BuildContext context) {
final controller = useTextEditingController();
return BlocBuilder<ScratchNoteCubit, ScratchNoteState>(
builder: (context, state) {
return Scaffold(
appBar: AppBar(
title: Text(context.l10n.appName),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: !state.isLoading && state.notes.isEmpty
? const Center(
child: Text(
'If you give people nothingness, they can ponder '
'what can be achieved from that nothingness.',
textAlign: TextAlign.center,
),
)
: ListView.builder(
itemBuilder: (context, index) {
return Card(
color: Color(
context
.read<SettingsCubit>()
.state
.settings
.isDarkTheme
? AppColors.darkerInk
: AppColors.lightestSky,
),
child: ListTile(
title: Text(
state.notes[index].title,
style: Theme.of(context).textTheme.bodyLarge,
),
subtitle: Text(
state.notes[index].createdAt
.toIso8601String()
.substring(0, 10),
style: Theme.of(context).textTheme.bodySmall,
),
onTap: () {
controller.text = state.notes[index].title;
TechshoiDialog(
context: context,
title: 'Edit Note',
content: TextFormField(
controller: controller,
decoration: const InputDecoration(
hintText: 'Enter your note here',
),
),
actions: [
DialogAction(
text: 'Update',
onPressed: () {
if (!controller.text.isNullOrEmpty()) {
context
.read<ScratchNoteCubit>()
.updateScratchNote(
state.notes[index].copyWith(
title: controller.text.trim(),
),
);
context.pop();
}
},
),
DialogAction(
text: 'Delete',
onPressed: () {
context
.read<ScratchNoteCubit>()
.deleteScratchNote(
state.notes[index].id,
);
context.pop();
},
),
],
).show();
},
),
);
},
itemCount: state.notes.length,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.text = '';
TechshoiDialog(
context: context,
title: 'Add Note',
content: TextFormField(
controller: controller,
decoration: const InputDecoration(
hintText: 'Enter your note here',
),
),
actions: [
DialogAction(
text: 'Add',
onPressed: () {
if (!controller.text.isNullOrEmpty()) {
context.read<ScratchNoteCubit>().createScratchNote(
title: controller.text.trim(),
);
context.pop();
}
},
),
],
).show();
},
tooltip: 'Add Note',
child: const Icon(Icons.add),
),
);
},
);
}
}
Note that we have a static route
property in the ScratchNotesPage
class. This property is used to define the route for the page in the app's router.