Adding to the struct idea above — splitting it into three small pieces made this much easier to learn when I tried something similar:
1. The data
Make a Blueprint Struct called something like S_InventoryItem with Item Name (Text), Icon (Texture 2D), Description, and a name/ID variable you can test against. Put your items in a Data Table based on that struct, so a Key, a Note and a Battery are just rows, not separate Blueprints.
2. The inventory itself
Put an Actor Component on your player character / player controller called BP_InventoryComponent. Give it an array of that struct, and three functions: Add Item, Remove Item, and Has Item? (a boolean check). Keep all the logic in there and let the widget only display what is in the array — that separation saves a lot of rewiring later.
3. Picking things up and using them — left click only
In the Player Controller, enable Show Mouse Cursor and Click Events. On left click, use Get Hit Result Under Cursor by Channel. If the hit actor implements a simple Blueprint Interface, BPI_Interactable, call Interact on it. A pickup version of that adds its Data Table row to the inventory and destroys itself. For using an item, click an inventory slot to select it, then click the object in the world — the object checks Has Item? / Selected Item ID, so a brass key only opens one specific door, and you remove the item only if the puzzle is meant to consume it.
UI tip
Build the bar as a Widget with a Horizontal Box or Uniform Grid Panel and a separate W_Slot widget (icon image, button, selected highlight). On Add Item, loop the array and refresh the slots rather than trying to update only one slot.
Start tiny: 4 slots, one pickup, and one locked door you can test in 2 minutes. Once that loop works, combining items and descriptions are just extra fields and one extra check, and debugging stays manageable because you know which of the three pieces broke.