0

I am deleting a engine model via:

  delete(int id) {
    engines.removeWhere((eng) => eng.id == id);
    notifyListeners();
  }
}

The interesting part here is that the engine with the passed id gets removed indeed correctly while debugging.

But visually seen the wrong eengine widget gets deleted and I have no idea why???

I put here all code which can be easily copy/paste into the famous main.dart file!

I am new to flutter and even more to provider package.

Maybe I have done something wrong there...

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Provider nested model demo',
        theme: ThemeData(
          // This is the theme of your application.
          primarySwatch: Colors.blue,
        ),
        home: ApplicationPage());
  }
}

class ApplicationPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // setup test data which could come from a http service
    var engines = new List<EngineEntity>();
    var engine1 = new EngineEntity(1, "Engine 1");
    var engine2 = new EngineEntity(2, "Engine 2");
    var engine3 = new EngineEntity(3, "Engine 3");
    var engine4 = new EngineEntity(4, "Engine 4");
    var engine5 = new EngineEntity(5, "Engine 5");

    engines.add(engine1);
    engines.add(engine2);
    engines.add(engine3);
    engines.add(engine4);
    engines.add(engine5);

    var newMachineEntity = new MachineEntity(1, "Power x1000", engines);

    return Scaffold(
        appBar: AppBar(title: Text("Machine demo"), elevation: 6),
        body: ChangeNotifierProvider(create: (context) => new MachineModel(newMachineEntity), child: MachineWidget()));
  }
}

class MachineModel extends ChangeNotifier {
  MachineEntity machineEntity;
  List<EngineModel> engines;
  MachineModel(this.machineEntity) {
    engines = this.machineEntity.engines.map((eng) => new EngineModel(eng)).toList();
  }

  String get name => machineEntity.name;
  set name(String value) {
    machineEntity.name = value;
    notifyListeners();
  }

  delete(int id) {
    engines.removeWhere((eng) => eng.id == id);
    notifyListeners();
  }
}

class EngineModel extends ChangeNotifier {
  EngineEntity engine;
  EngineModel(this.engine);

  String get name => engine.name;
  set name(String value) {
    name = value;
    notifyListeners();
  }

  int get id => engine.id;
}

class MachineEntity {
  int id;
  String name;
  List<EngineEntity> engines;
  MachineEntity(this.id, this.name, this.engines);
}

class EngineEntity {
  int id;
  String name;
  EngineEntity(this.id, this.name);
}

class EngineListWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<MachineModel>(context);

    return Container(
      child: ListView(
        shrinkWrap: true,
        children: appState.engines.map((eng) => EngineWidget(eng)).toList(),
      ),
    );
  }
}

// Rendered with the EngineModel as data basis
class EngineWidget extends StatelessWidget {
  EngineWidget(this.engineModel);

  final EngineModel engineModel;

  @override
  Widget build(BuildContext context) {
    final appState = Provider.of<MachineModel>(context);

    return Container(
      child: Row(
        children: <Widget>[
          Expanded(flex: 2, child: Text(engineModel.id.toString())),
          Expanded(
              flex: 5,
              child: TextFormField(
                initialValue: engineModel.name,
              )),
          Expanded(
            flex: 3,
            child: FlatButton(onPressed: () => appState.delete(engineModel.id), child: Text("Delete")),
          )
        ],
      ),
    );
  }
}

// Rendered with the MachineModel as data basis
class MachineWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<MachineModel>(builder: (context, machine, child) {
      return Container(
        child: Column(
          children: <Widget>[
            Container(
                padding: new EdgeInsets.all(5),
                child: TextFormField(
                  initialValue: machine.name,
                  onChanged: (value) => machine.name = value,
                )),
            EngineListWidget(),
          ],
        ),
      );
    });
  }
}

SOLUTION:

    children: appState.engines.map((eng) => EngineWidget(ValueKey(eng.id), eng)).toList(),


class EngineWidget extends StatelessWidget {
  EngineWidget(this.key, this.engineModel) : super(key: key);
  final Key key;

}

The widget needs a unique key  which is provided by ValueKey and of course my unique database id ;-)
Pascal
  • 12,265
  • 25
  • 103
  • 195
  • Sounds like you need to read https://stackoverflow.com/questions/50080860/what-are-keys-in-the-stateless-widgets-class/50081052#50081052 – Rémi Rousselet Feb 02 '20 at 21:21
  • Ah I forgot the ValueKey... I used it already but due to the missing error I got earlier I did not expect that this was the error...Now the deletion works not only on the Text element but also the TextFormField ;-) Thanks, good night and tomorrow evening I put something in your github cart ;-) – Pascal Feb 02 '20 at 21:30

1 Answers1

0

You need to assign a Key to the Widgets added to a List to have an identifier. What happens here is that List.removeWhere() correctly removes the value from the List, but since it'll be loaded to a new List when notifyListeners(), there's no guarantee which List item they'll be placed.

Omatt
  • 8,564
  • 2
  • 42
  • 144