FutureBuilder Flutter перестраивается несколько раз - PullRequest
0 голосов
/ 18 апреля 2020

Я относительно новичок во Флаттере и в настоящее время борюсь с FutureBuilders. Я прочитал ответ Реми в этой ветке , в которой в основном изложена философия Флаттера о том, что сборки виджетов должны быть идемпотентными. Я понимаю принцип в теории, но как это работает на практике?

Рассмотрим следующий фрагмент:

return Scaffold(
  body: SafeArea(
    child: Stack(
      children: [
        Consumer<DataPresenter>(
            builder: (context, presenter, _) {
              return DefaultFutureBuilder<List<Data>>(
                    future: presenter.data(),
                    builder: (context, data) {
                      // bla bla 
                    }
                );
            }
        ),
        // More widgets
      ],
    ),
  ),
);

}

Где это мой DefaultFutureBuilder:

class DefaultFutureBuilder<T> extends StatelessWidget {
  final Future<T> future;
  final Widget Function(BuildContext, T data) builder;
  final Widget Function(BuildContext) errorBuilder;

  DefaultFutureBuilder({
    @required this.future,
    @required this.builder,
    this.errorBuilder,
  });

  @override
  Widget build(BuildContext context) {
    var errBuilder = errorBuilder ?? _buildDefaultErrorScreen;

    return FutureBuilder<T>(
      future: future,
      builder: (context, snapshot) {
        var indicator = Center(child: CircularProgressIndicator());

        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.hasData)
            return builder(context, snapshot.data);
          else if (snapshot.hasError)
            return errBuilder(context);
        }

        return indicator;
      },
    );
  }

  Widget _buildDefaultErrorScreen(BuildContext context) {
    // default
  }
}


Теперь я знаю что вызов presenter.data() (извлекает данные asyn c) не кошерный. Но я делаю хочу повторно извлечь данные, когда докладчик уведомляет потребителя, и в то же время я не хочу извлекать их, когда Flutter перестраивает мое дерево виджетов из-за структурных махинаций.

Моя первоначальная идея состояла в том, чтобы построить презентатор так, чтобы он получал данные только тогда, когда у него нет данных в данный момент. Затем я мог бы установить для данных значение null из других методов и уведомить слушателей о необходимости перестроить поддеревья виджетов, на которые влияют.
Примерно так:

class DataPresenter extends ChangeNotifier {
  // fields, services, bla bla
  List<Data> _data; 

  Future<List<Data>> data() async {
    if (_data == null) {
      var response = await _service.fetchMyStuff(params: params);
      _data = response.data;
    }

    return _data;
  }
}

Проблема с этим решением состоит в том, что любые перестройки, которые происходят, пока я получение данных в первый раз вызовет другой запрос.

Буду признателен, если кто-то сможет указать, какую часть фреймворка / архитектуры я не получил.

Ответы [ 2 ]

1 голос
/ 18 апреля 2020

Я думаю, что это может решить вашу проблему. У меня эта проблема слишком сильно раздражала все, что вы делаете, и ваше приложение перестраивается. Таким образом, они решили, что я запомнил свое будущее. Теперь это может или не может работать для вас. Если он рассматривает перестройку Consumer как новое будущее, то у вас все будет хорошо, потому что тогда перестройка FutureBuilder произойдет, когда Consumer сделает то, что вы хотите, если я правильно понимаю. Вот что вы делаете.

//Initialize at top of class
final AsyncMemoizer _memoizer = AsyncMemoizer(); 


 Future<void> _someFuture() {
    return this._memoizer.runOnce(() async {
     //Do your async work
    });
  }

Тогда _someFuture() будет тем, что вы передадите FutureBuilder. Вот хорошая статья по этому вопросу.

Флаттер: мой будущий строитель продолжает стрелять!

0 голосов
/ 18 апреля 2020

Я бы предложил решение, без futureBuilder

Вы получаете данные после первой сборки вашего виджета без сохранения состояния

WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<DataPresenter>(context, listen: false).fetchdata());

у вас есть разница в вашем _data списке со значением null или lenght==0

class DataPresenter extends ChangeNotifier {
  // fields, services, bla bla
  List<Data> _data; 
  // getter for the Consumer
  List<Data> get data => _data;


  Future<List<Data>> fetchdata() async {

     _data = await _service.fetchMyStuff(params: params);
     if (_data == null) 
       _data = new List(); //difference between null and length==0

     //rebuild Consumer
     notifyListeners();

  }
}

Страница без FutureBilder

class ProvPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    // calls after the widget ist build
    WidgetsBinding.instance.addPostFrameCallback((_) => Provider.of<TodoListModel>(context, listen: false).refreshTasks());

    return MultiProvider(
        providers: [
              ChangeNotifierProvider(create: (context) => DataPresenter()),
        ],
        child: Scaffold(
          body: SafeArea(
            child: Stack(
              children: [
                Consumer<DataPresenter>(
                    builder: (context, presenter, _) {

                       //get current Values vom the DataPresenter._data
                       List<Data> currData = presenter.data;

                       // firstime fetch ??
                       if(currData == null)
                         return CircularProgressIndicator();

                       // DataPresenter.getData is finished
                       return new RefreshIndicator(
                           onRefresh: () => presenter.fetchdata(),
                           child: new Text("length " + currData.length.toString())
                       );

                    },
                ),
                // More widgets
              ],
            ),
          ),
        ),
    );
}
}
...