Menu
CoddyTech

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 icon

Challenge

Medium

Tapping 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) to TaskList that flips the task's done and tells the listeners.
  • Make the tile's onChanged call 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;
              },
            ),
        ],
      ),
    );
  }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in State Management

Practice on your own: Online Flutter compiler