How to use setState when finding intersection of list of lists in Flutter (Dart)

Viewed 48

I have the following code. This is just created for being able to reproduce the problem, and I will explain what it does down below...

import 'package:flutter/material.dart';

class TestFile extends StatefulWidget {
  const TestFile({Key? key}) : super(key: key);

  @override
  _TestFileState createState() => _TestFileState();
}

class _TestFileState extends State<TestFile> {
  List<dynamic> a = [1, 2, 3, 4, 5];
  List<dynamic> b = [4, 5, 6, 7, 8];
  List<dynamic> c = [4, 5, 8, 9, 10];

  List<List<dynamic>> comb = [
    [],
    [],
    [],
  ];

  List<dynamic> result = [];

  Widget _itemWidget(idx, ml) {
    return InkWell(
      onTap: () {
        setState(() {
          comb[idx] = ml;

          result = comb
              .fold<Set>(
                  comb.first.toSet(), (a, b) => a.intersection(b.toSet()))
              .toList();
        });
      },
      child: Padding(
        padding: const EdgeInsets.fromLTRB(20, 5, 20, 5),
        child: Container(
          margin: const EdgeInsets.only(bottom: 3),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(15.0),
            color: Colors.blueAccent,
          ),
          child: Column(
            children: [
              Center(
                child: Text(idx.toString()),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Text(comb.toString()),
            const SizedBox(height: 20),
            Text(result.toString()),
            _itemWidget(0, a),
            _itemWidget(1, b),
            _itemWidget(2, c),
          ],
        ),
      ),
    );
  }
}

The following is the initial state of the screen:

enter image description here

At top, we see the comb list of lists. In the beginning, it is empty.

Just under that, we see the result list, which is also empty in the beginning and it is supposed to show the intersection of lists from the comb list of lists.

And we have 3 buttons, with the names 0, 1, 2 accordingly.

First, I press on the button 0. This adds the list a to comb[ 0 ]. And at this point, here is how the screen looks like:

enter image description here

So far, everything works as expected. The result list is empty because comb 1 and comb 2 are empty.

And now, I press on the button 1. And here is how the screen looks like after pressing this button:

enter image description here

At top, we see the comb list of lists. Since I have pressed the buttons 0 and 1 so far, I see exactly what I expect to see from the comb. But at this point, I expect the result list to show [4, 5], which are the common elements of comb[ 0 ] and comb 1 but what I see is an empty list [], as also seen on the screen.

After spending quite a number of hours, I am not able to find the reason to this problem.

What do I need to change so that the result list is updated with the common values of selected lists based on which buttons the user presses on? What am I doing wrong here?

1 Answers

In your intersection logic, you are intersecting between sets [1, 2, 3, 4, 5], [4, 5, 6, 7, 8], and [] when buttons 0 and 1 are pressed. The common value between the three sets is the empty set. Change your intersection logic to filter out list elements that are empty:

          final nonEmpty = comb.where((x) => x.isNotEmpty);

          result = nonEmpty
              .fold<Set>(
                  nonEmpty.first.toSet(), (a, b) => a.intersection(b.toSet()))
              .toList();

You can also use reduce:

          result = comb
              .where((x) => x.isNotEmpty)
              .reduce((a, b) => a.toSet().intersection(b.toSet()).toList());
Related