A List of Objects in a Model
Part of the State Management section of Coddy's Flutter journey. Lesson 14 of 27.
A model often holds a list of objects, and the objects themselves change: a task gets ticked. The rule stays the same: the change goes through a method of the model, and the method calls notifyListeners():
class TaskList with ChangeNotifier {
final List<Task> _tasks = [Task('Buy milk'), Task('Call Sam')];
List<Task> get tasks => _tasks;
void toggle(Task task) {
task.done = !task.done;
notifyListeners();
}
}Changing task.done from the widget would change the object too, but no listener would hear of it, so the box would not move. The model is the one place that changes its data, and the one place that tells.
Note the with. extends says what a class is, and a class extends only one other class. with mixes extra abilities into a class, and you can add several. ChangeNotifier works either way, so class TaskList with ChangeNotifier is the same as extends ChangeNotifier, and leaves extends free for something else.
Challenge
MediumTapping a task flips task.done right in the widget, so nothing on the screen moves. Move the change into the model:
- Add a method
toggle(Task task)toTaskListthat flips the task'sdoneand tells the listeners. - Make the tile's
onChangedcall it.
Try it yourself
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Task {
Task(this.title);
final String title;
bool done = false;
}
class TaskList with ChangeNotifier {
final List<Task> _tasks = [Task('Buy milk'), Task('Walk the dog'), Task('Call Sam')];
List<Task> get tasks => _tasks;
int get doneCount => _tasks.where((t) => t.done).length;
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => TaskList(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final list = context.watch<TaskList>();
return Scaffold(
appBar: AppBar(title: Text('${list.doneCount} of ${list.tasks.length} done')),
body: ListView(
children: [
for (final task in list.tasks)
CheckboxListTile(
title: Text(task.title),
value: task.done,
onChanged: (value) {
task.done = !task.done;
},
),
],
),
);
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in State Management
3Provider
Provider: Share a ModelCalling notifyListenersProvider: Watch or ReadProvider: ConsumerTwo Screens, One ModelA List of Objects in a ModelSeveral Models: MultiProviderPractice on your own: Online Flutter compiler