On this page16 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Why Bytes
- 2. Showing the Image
- 3. Hobbies With FilterChip
- 4. Copy the List When Editing
- 5. Save Everything
- 6. Show the Photo on the Dashboard
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Key Takeaways
Lecture 007 — Image Storage & Hobby Chips
| Category | 4 — Images & Hobbies |
| Lecture | 007 of 007 |
| Estimated video length | 25 min |
| Target | Flutter 3.44 · Dart 3.12 · hive 2.2 |
| Level | Beginner |
| Video | Part 3 — Image Storage, Hobbies with FilterChip & ValueListenableBuilder |
| Source code | GitHub — urraan_hive_final_code |
The last two fields of our model: a profile photo stored as bytes, and hobbies chosen with
FilterChips and stored as aList<String>. Then the dashboard shows the photo in a round avatar.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Prerequisites
Lectures 001–006, and FilterChip, Wrap and DecorationImage from Essential Widgets.
Connection With Previous Lectures
The model already has profileImage (Uint8List?) and hobbies (List<String>). The form
does not fill them yet. We add an image picker and chips, and the dashboard shows the result.
Learning Objectives
- Pick an image with
image_pickerand read it as bytes - Store and show a
Uint8ListwithMemoryImage - Build multi-select chips with
FilterChipover aList<String> - Copy the list so editing does not change the saved record early
- Show the avatar on the dashboard
1. Why Bytes
Hive cannot store a File path safely (the file may move or be cleared), but it can store
Uint8List — the raw bytes. Picking returns an XFile; readAsBytes() gives the bytes.
final imagePicker = ImagePicker();
Uint8List? _imageBytes;
Future<void> _pickImage(ImageSource source) async {
final image = await imagePicker.pickImage(source: source);
if (image == null) return; // user cancelled
final bytes = await image.readAsBytes();
setState(() => _imageBytes = bytes);
}ImageSource.gallery or ImageSource.camera. The null check handles a user who backs out.
2. Showing the Image
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(15),
image: _imageBytes != null
? DecorationImage(fit: BoxFit.cover, image: MemoryImage(_imageBytes!))
: null,
),
child: _imageBytes != null ? null : const Icon(Icons.image, size: 50),
)MemoryImage turns bytes into an image provider. BoxFit.cover fills the box without
stretching. When there is no image, an icon is the placeholder.
3. Hobbies With FilterChip
final List<String> _hobbies = ['Cricket', 'Football', 'Hockey', 'Badminton'];
List<String> _selectedHobbies = [];Wrap(
spacing: 8,
children: [
for (final hobby in _hobbies)
FilterChip(
label: Text(hobby),
selected: _selectedHobbies.contains(hobby),
onSelected: (selected) {
setState(() {
if (selected) {
_selectedHobbies.add(hobby);
} else {
_selectedHobbies.remove(hobby);
}
});
},
),
],
)Wrap moves chips to the next line when the row is full. selected is true when the hobby
is in the list; onSelected adds or removes it.
4. Copy the List When Editing
_selectedHobbies = List.of(item.hobbies);If you assign item.hobbies directly, tapping chips edits the saved record's list before
you press Update. List.of makes a copy so changes only count when you save.
5. Save Everything
final item = DataObjects()
..key = widget.item?.key ?? GeneralBox.getUniqueKey()
..name = controllerName.text.trim()
..address = controllerAddress.text.trim()
..profileImage = _imageBytes
..hobbies = _selectedHobbies;6. Show the Photo on the Dashboard
In ListTile add a leading:
leading: CircleAvatar(
backgroundImage:
item.profileImage != null ? MemoryImage(item.profileImage!) : null,
child: item.profileImage == null ? const Icon(Icons.person) : null,
),Code Examples
Example 1 — The Finished Form
The finished lib/screen_add_update_record.dart is in the course repository at
app/lib/screen_add_update_record.dart. It is Lecture 006's screen plus the fields, picker,
preview, chips, and the copy in initState.
Example 2 — Platform Setup for image_picker
- iOS: add
NSPhotoLibraryUsageDescription(andNSCameraUsageDescriptionfor the camera) toios/Runner/Info.plist. - Android: the gallery works without extra permissions on current Flutter; the camera uses the system camera app.
- Web: works as a file chooser.
Common Beginner Mistakes
1. Not handling null from the picker — cancelling returns null; using it crashes.
2. Saving big photos — a full-resolution photo can be several MB per record, slowing the
list. Reduce it: pickImage(source: ..., maxWidth: 600, imageQuality: 80).
3. Editing item.hobbies directly — copy with List.of (section 4).
4. Missing iOS usage description — the app closes the moment the picker opens.
5. context after await — if you ever call Navigator/ScaffoldMessenger after an
await, check if (!mounted) return; first.
Best Practices
- Resize images when picking (
maxWidth,imageQuality). - Keep the list of hobbies in one constant list; later it can come from the box too.
- Show a placeholder whenever the image is
null.
Mini Challenge
Add a Remove photo button that sets _imageBytes = null, and a Camera button that
uses ImageSource.camera.
Practice Questions
- Why bytes and not a file path? 2. What does
readAsBytesreturn? 3. WhyList.ofininitState? 4. What stops the chips from overflowing the screen?
Practice Answers
- A path can break; bytes are stored inside the database itself.
- A
Future<Uint8List>. - So edits happen on a copy until the user saves.
Wrap, which flows onto new lines.
Key Takeaways
- Store images as
Uint8List; show them withMemoryImage. FilterChip+List<String>= multi-select; copy the list before editing.- You now have a complete Hive app: model, adapter, manager, live list, add/update, photo and hobbies.