Soojeong Lee

Jul 25, 2025

Soojeong Lee

Jul 25, 2025

Soojeong Lee

Jul 25, 2025

Work on additions and detail pages

Work on additions and detail pages

Work on additions and detail pages

Modeling Some Adjustments

First of all, since every object has an id property, I created the BaseModel class.

class BaseModel {
  String? id;
}

And I separated it into a form of text and media in the form of videos/photos that basically recall memories, allowing them to be inherited from the Memory class in common.

import 'base.model.dart';

class Memory extends BaseModel {
  final String memo;
  final DateTime createdAt;

  Memory({required this.memo, required this.createdAt});
}

class MediaMemory extends Memory {
  final String url;
  final String location;

  MediaMemory({
    required this.url,
    required this.location,
    required super.memo,
    required super.createdAt,
  });
}

class WrittenMemory extends Memory {
  final String content;

  WrittenMemory({
    required this.content,
    required super.memo,
    required super.createdAt,
  });
}


Detailed Page for Each Memory

I worked on separating the routes in this way, basically branching according to the type of memories to move to different layout screens.

GoRoute(
      path: '/lost_person/:lost_person_id',
      builder: (context, state) {
        final lostPersonId = state.pathParameters['lost_person_id']!;
        return LostPersonDetailScreen(lostPersonId: lostPersonId);
      },
      routes: [
        GoRoute(
          path: '/memories',
          builder: (context, state) {
            final lostPersonId = state.pathParameters['lost_person_id']!;
            return MemoryDetailScreen(lostPersonId: lostPersonId);
          },
        ),
        GoRoute(
          path: '/item/:item_id',
          builder: (context, state) {
            final itemId = state.pathParameters['item_id']!;
            final itemType = state.uri.queryParameters['item_type'] ?? '';
            switch (itemType) {
              case 'written':
                return WrittenMemoryDetailPage(itemId: itemId);
              default:
                return MediaMemoryDetailPage(itemId: itemId);
            }
          },
        ),
      ],
    ),

The core of this is to receive the type of each memory via state.uri.queryParameters and move to the corresponding layout screen. When moving, we apply a URL of this form.

context.push('/lost_person/1/item/1?item_type=media');


The detailed screen displaying photos or videos has this layout.


The detailed screen displaying text has this layout.


Additional Actions

The additional action is simple; just display a modal on the pre-made FAB.

 floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () => showModalBottomSheet(
          context: context,
          shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
          ),
          builder: (context) {
            return Padding(
              padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  ListTile(
                    leading: const Icon(Icons.photo),
                    title: const Text('Upload Photo'),
                    onTap: () {
                      Navigator.pop(context);
                      // TODO: handle image upload
                    },
                  ),
                  ListTile(
                    leading: const Icon(Icons.videocam),
                    title: const Text('Upload Video'),
                    onTap: () {
                      Navigator.pop(context);
                      // TODO: handle video upload
                    },
                  ),
                  ListTile(
                    leading: const Icon(Icons.contact_page_outlined),
                    title: const Text('Upload a Written Memory'),
                    onTap: () {
                      Navigator.pop(context);
                      // TODO:  handle text upload
                    },
                  ),
                ],
              ),
            );
          },
        ),
      ),


Personally, I think a BottomSheet is neat.


It is still a long way to go, but somehow it seems I have managed to establish the layout related to the core functionalities of the screen. I plan to refine the screen a little more before adding server actions to finalize it.

Next Article

Next Article

Next Article

Comments

Comments

Comments