Модель с флаттером не обновляет всех потомков " - PullRequest
0 голосов
/ 28 мая 2019

Мое приложение предназначено для потребления данных с датчиков в реальном времени из API с использованием флаттера scoped_model.Данные представляют собой массив JSON, подобный следующему:

[
  {
    "id": 4,
    "device_name": "fermentero2",
    "active_beer": 4,
    "active_beer_name": "Sourgobo",
    "controller_fridge_temp": "Fridge --.-   1.0 ░C",
    "controller_beer_temp": "Beer   28.6  10.0 ░C",
    "active_beer_temp": 28.63,
    "active_fridge_temp": null,
    "active_beer_set": 10,
    "active_fridge_set": 1,
    "controller_mode": "b"
  },
  {
    "id": 6,
    "device_name": "brewpi",
    "active_beer": 1,
    "active_beer_name": "Amber Ale",
    "controller_fridge_temp": null,
    "controller_beer_temp": null,
    "active_beer_temp": null,
    "active_fridge_temp": null,
    "active_beer_set": null,
    "active_fridge_set": null,
    "controller_mode": null
  }
]

Это устройства.Моя модель устройства выглядит следующим образом (аннотация json):

@JsonSerializable(nullable: false)
class Device {
  int id;
  String device_name;
  @JsonKey(nullable: true) int active_beer;
  @JsonKey(nullable: true) String active_beer_name;
  @JsonKey(nullable: true) String controller_mode; // manual beer/fridge ou perfil
  @JsonKey(nullable: true) double active_beer_temp;
  @JsonKey(nullable: true) double active_fridge_temp;
  @JsonKey(nullable: true) double active_beer_set;
  @JsonKey(nullable: true) double active_fridge_set;


  Device({
    this.id,
    this.device_name,
    this.active_beer,
    this.active_beer_name,
    this.controller_mode,
    this.active_beer_temp,
    this.active_beer_set,
    this.active_fridge_set,
  });

  factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json);
  Map<String, dynamic> toJson() => _$DeviceToJson(this);

}

Мой класс модели устройства для устройства выглядит следующим образом:

class DeviceModel extends Model {

  Timer timer;

  List<dynamic> _deviceList = [];
  List<dynamic> get devices => _deviceList;

  set _devices(List<dynamic> value) {
    _deviceList = value;
    notifyListeners();
  }



  List _data;

  Future getDevices() async {
    loading = true;
    _data = await getDeviceInfo()
        .then((response) {
      print('Type of devices is ${response.runtimeType}');
      print("Array: $response");
      _devices = response.map((d) => Device.fromJson(d)).toList();
      loading = false;
      notifyListeners();
    });
  }



  bool _loading = false;

  bool get loading => _loading;

  set loading(bool value) {
    _loading = value;
    notifyListeners();
  }


    notifyListeners();
}

Мой пользовательский интерфейс предназначен для отображения списка устройств.отображение оперативных данных (перестройка пользовательского интерфейса при изменении данных датчика) и страница сведений о каждом устройстве, а также отображение оперативных данных.Для этого я использую таймер.Страница со списком устройств работает должным образом и «обновляется» каждые 30 секунд:

class DevicesPage extends StatefulWidget {
  @override
  State<DevicesPage> createState() => _DevicesPageState();
}

class _DevicesPageState extends State<DevicesPage> {
  DeviceModel model = DeviceModel();

  Timer timer;

  @override
  void initState() {
    model.getDevices();
    super.initState();
    timer = Timer.periodic(Duration(seconds: 30), (Timer t) => model.getDevices());
  }

  @override
  Widget build(BuildContext) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text('Controladores'),
      ),
      drawer: AppDrawer(),
      body: ScopedModel<DeviceModel>(
        model: model,
        child: _buildListView(),
      ),
    );
  }

  _buildListView() {
    return ScopedModelDescendant<DeviceModel>(
      builder: (BuildContext context, Widget child, DeviceModel model) {
        if (model.loading) {
          return UiLoading();
        }
        final devicesList = model.devices;
        return ListView.builder(
          itemBuilder: (context, index) => InkWell(
            splashColor: Colors.blue[300],
            child: _buildListTile(devicesList[index]),
            onTap: () {
              Route route = MaterialPageRoute(
                builder: (context) => DevicePage(devicesList[index]),
              );
              Navigator.push(context, route);
            },
          ),
          itemCount: devicesList.length,
        );
      },
    );
  }

  _buildListTile(Device device) {
    return Card(
      child: ListTile(
        leading: Icon(Icons.devices),
        title: device.device_name == null
        ? null
            : Text(
        device.device_name.toString() ?? "",
        ),
        subtitle: device.active_beer_name == null
            ? null
            : Text(
          device.active_beer_temp.toString() ?? "",
        ),
      ),
    );
  }
}

class UiLoading extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          CircularProgressIndicator(),
          SizedBox(height: 12),
          Text(
            'Loading',
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
        ],
      ),
    );
  }
} 

Проблема возникает с пользовательским интерфейсом страницы сведений, который также должен отображать живые данные, но при этом он ведет себя как statelesswidget и делаетне восстанавливать себя после обновления модели:

class DevicePage extends StatefulWidget {

  Device device;
  DevicePage(this.device);

  @override
  //State<DevicePage> createState() => _DevicePageState(device);
  State<DevicePage> createState() => _DevicePageState();
}

class _DevicePageState extends State<DevicePage> {

  DeviceModel model = DeviceModel();

  Timer timer;

  @override
  void initState() {
    DeviceModel model = DeviceModel();
    super.initState();
    timer = Timer.periodic(Duration(seconds: 30), (Timer t) => model.updateDevice());

  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: new AppBar(
        title: new Text(widget.device.device_name),
      ),

      drawer: AppDrawer(),
      body: ScopedModel<DeviceModel>(
        model: model,
        child: _buildView(widget.device),
      ),
    );
  }

  _buildView(Device device) {
    return ScopedModelDescendant<DeviceModel>(
      builder: (BuildContext context, Widget child, DeviceModel model) {
        if (model.loading) {
          return UiLoading();
        }
        return Card(
          child: ListTile(
            leading: Icon(Icons.devices),
            title: device.device_name == null
                ? null
                : Text(
              device.device_name.toString() ?? "",
            ),
            subtitle: device.active_beer_name == null
                ? null
                : Text(
              device.active_beer_temp.toString() ?? "",
            ),
          ),
        );
      },
    );
  }
}

class UiLoading extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          CircularProgressIndicator(),
          SizedBox(height: 12),
          Text(
            'Loading',
            style: TextStyle(
              fontWeight: FontWeight.bold,
            ),
          ),
        ],
      ),
    );
  }

Чего мне не хватает?большое спасибо заранее

1 Ответ

0 голосов
/ 28 мая 2019

Похоже, вы создаете новую DeviceModel для своей DevicePage, что означает , что модель будет той, на которую будет реагировать ваш пользовательский интерфейс, а не той, которая находится выше в дереве виджетов - вашей DevicesPage.

ScopedModel<DeviceModel>(
        model: model,
        child: _buildView(widget.device),
      )

На вашей странице устройства, где вы добавляете тело к своей эшафот, замените ScopedModel просто:

_buildView(widget.device)

Это должно решить вашу проблему.

...