StreamBuilder не выполняет повторную визуализацию виджета внутри?

#flutter

#flutter

Вопрос:

Я создал этот код, и я хочу, чтобы при нажатии на кнопку происходило то, что я хочу, чтобы диаграмма элементов повторно отображалась с новыми значениями (которые должны быть старыми значениями, но ценность еды увеличилась на 1). Я использую диаграмму элементов из пакета pie_chart: 0.8.0. Deposit — это не что иное, как pojo (категория строки и вклад int).bloc.dart содержит глобальный экземпляр блока, средство получения потока и инициализацию потока типа Вот мой код:

 import 'package:flutter/material.dart';
import 'package:pie_chart/pie_chart.dart';
import 'bloc.dart';
import 'Deposit.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'bloc Chart',
      theme: ThemeData(
        primarySwatch: Colors.blueGrey,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  Map<String, double> datamap = new Map();

  @override
  Widget build(BuildContext context) {
    datamap.putIfAbsent("Food", () => 5);
    datamap.putIfAbsent("transportation", () => 3);
    return Scaffold(
      appBar: AppBar(
        title: Text("PieChart using blocs"),
      ),
      body: Column(
        children: <Widget>[
          StreamBuilder<Deposit>(
              stream: bloc.data, //A stream of Deposit data
              builder: (context, snapshot) {
                addDeposit(Deposit("Food", 1), datamap);
                debugPrint("Value of food in map is: ${datamap["Food"]}");
                return PieChart(dataMap: datamap);
              }),
          SizedBox.fromSize(
            size: Size(20, 10),
          ),
          RaisedButton(
            onPressed: () {
              bloc.add(Deposit("Food", 1)); //returns the stream.add
            },
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }

  void addDeposit(Deposit dep, Map<String, double> map) {
    if (map.containsKey(dep.category)) {
      map.update(dep.category, (value) => value   dep.price);
    } else
      map.putIfAbsent(dep.category, () => dep.price);
  }
}
  

Комментарии:

1. попробуйте это, сначала закройте поток, затем добавьте новые данные

2. Где именно я должен закрыть свой поток? А также почему я должен хотеть, чтобы он был закрыт во время его использования?

3. bloc.sink.close(); bloc.sink.add(Депозит(«Еда», 1));

4. @BenjithKizhisseri если вы закроете свой приемник, как вы сможете что-либо добавить в этот приемник? в close() документах метода говорится: «Сообщает приемнику потока, что дополнительные потоки не будут добавлены».

5. @pskink это bloc.close(); это bloc.sink.close(); оба они разные

Ответ №1:

Я думаю, ваша проблема в том, что поток не запускает новые события. Вам не нужно закрывать поток для восстановления. Я не вижу нигде в вашем коде, где вы запускаете новые события для потока. Проверьте приведенный ниже код, чтобы увидеть простой способ, как вы можете обновить a StatelessWidget с помощью StreamBuilder .

 class CustomWidgetWithStream extends StatelessWidget {
  final CustomBlock block = CustomBlock();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        StreamBuilder(
            stream: block.stream,
            builder: (context, stream) {
              return Text("${stream.data.toString()}");
            }),
        RaisedButton(
          onPressed: () {
            block.incrementNumber();
          },
          child: Text("Increment"),
        )
      ],
    );
  }
}

class CustomBlock {
  num counter = 10;
  final StreamController<num> _controller = StreamController();

  Stream<num> get stream => _controller.stream;

  CustomBlock() {
    _controller.onListen = () {
      _controller.add(counter); // triggered when the first subscriber is added
    };
  }

  void incrementNumber() {
    counter  = 1;
    _controller.add(counter); // ADD NEW EVENT TO THE STREAM
  }

  dispose() {
    _controller.close();
  }
}
  

Хотя это рабочий фрагмент кода, я бы настоятельно рекомендовал изменить ваш виджет с StatelessWidget на StatefulWidget по двум причинам:
* если вы идете «по инструкции», если виджет изменяет содержимое сам по себе, то это не StatelessWidget , виджет без состояния отображает только данные, которые ему предоставлены. В вашем случае виджет обрабатывает нажатие, а затем решает, что делать дальше и как обновить себя.
* если вы используете потоки, в виджете с отслеживанием состояния вы можете безопасно закрыть поток, как вы можете видеть в приведенном выше коде, безопасного способа закрыть поток не существует. Если вы не закроете поток, может возникнуть нежелательное поведение или даже сбой.

Комментарии:

1. Хорошо, признайтесь мне, я новичок в flutter. Этот код — всего лишь тест, поскольку моя настоящая цель — иметь кнопку «добавить депозит», которая позволила бы пользователю вводить категорию и цену, что затем привело бы к обновлению диаграммы. Я прошел курс по udemy, в котором упоминалось, что шаблон bloc рекомендуется flutter. Диаграмма элементов будет телом моего эшафота на моей домашней странице, и я пытаюсь избежать того, чтобы домашняя страница была виджетом с отслеживанием состояния. Я в замешательстве, почему диаграмма элементов не выполняется повторный рендеринг, хотя должна (в потоке Streambuilder данные добавляются к нему с помощью bloc.add в кнопке

2. Я не совсем понимаю, как мне заставить мою кнопку взаимодействовать с виджетом, идея в моей голове была простой. Кнопка добавить событие в поток, streambuilder обновляет круговую диаграмму, и я избегаю отображения состояния всей моей домашней страницы. После того, как это сработает, я бы попытался сделать так, чтобы автоматически, когда я «добавляю данные» в поток, карта, используемая диаграммой элементов, также обновлялась, прямо сейчас я вручную обновляю ее с помощью addDeposit (). Также обратите внимание, что я пытался заменить диаграмму элементов просто текстом, отображающим карту данных [Food], и она обновлялась каждый раз, когда я нажимал кнопку.

3. Можете ли вы опубликовать код из «блока` и где вы его инициализируете? Я не могу найти код для этого.

4. Можете ли вы изменить _data.sink.add на _data.add ?

Ответ №2:

Это мой файл блока

 import 'package:rxdart/rxdart.dart';
import 'package:testing/Deposit.dart';

class Bloc{

  final _data = new BehaviorSubject<Deposit>();

  Stream<Deposit> get data => _data.stream;

  Function(Deposit) get add => _data.sink.add;

  void dispose(){
    _data.close();
  }

}

Bloc bloc = new Bloc();