Flutter get_it и FutureBuilder не работают с горячей перезагрузкой - PullRequest
0 голосов
/ 10 октября 2019

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

Все, что делает код, - это просто ждет, пока конкретный Future<String> станет доступным, и затем отобразит эту строку на экране. Пока он ждет, отображается индикатор прогресса.

Как я уже сказал, это работает, как и ожидалось, но проблема в том, что, если я что-то изменяю (я имею в виду просто косметические вещи) и запускается горячая перезагрузка, приложениебудет просто сидеть там вечно, показывая индикатор прогресса, потому что connectionState остается в режиме ConnectionState.waiting.

Вот что код делает более подробно:

Я регистрирую объект Stringкак синглтон в GetIt. Поскольку эта строка заключена в Future, код должен дождаться ее готовности, прежде чем ее можно будет зарегистрировать.

(В реальном коде я загружаю строку JSON из ресурса. почему он находится в Future.)

A FutureBuilder ждет, пока строка синглтона будет доступна, ожидая, пока GetIt покажет свойство с именем readyFuture.

КогдаСтрока становится доступной, FutureBuilder отображает ее на экране. До этого времени отображается CircularProgressIndicator.

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

GetIt locator = GetIt.instance;

void setupLocator() async {
  final str = await Future<String>.delayed(Duration(seconds: 3), () => "hello");
  locator.registerSingleton(str, signalsReady: true);
  locator.signalReady(str);
}

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<void>(
        future: locator.readyFuture,
        builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
          Widget result;

          switch (snapshot.connectionState) {
            case ConnectionState.none:
            case ConnectionState.active:
            case ConnectionState.waiting:
              result = Center(child: CircularProgressIndicator());
              break;
            case ConnectionState.done:
              if (snapshot.hasError)
                result = Center(child: Text('Error: ${snapshot.error}'));
              else
                result = Center(child: Text('${locator.get<String>()}'));
              break;
          }

          return result;
        }
      ),
    );
  }
}

У меня есть эти зависимости в моем pubspec.yaml:

  get_it: ^3.0.1
  provider: ^3.1.0

1 Ответ

0 голосов
/ 11 октября 2019

Проблема в том, что GetIt использует широковещательный поток GetIt.ready, чтобы сигнализировать, что он готов. Проблема с широковещательным потоком состоит в том, что сигнал теряется при горячей перезагрузке.

Другая отдельная проблема заключается в том, что если прослушивающие потоки GetIt.ready не прослушиваются, сигнал готовности будет потерян. Таким образом, есть условие гонки: вы должны убедиться, что FutureBuilder прослушивает GetIt.ready перед отправкой сигнала готовности, и это может быть вообще невозможно.

Обход, который я пришелв итоге было необходимо использовать объект Completer в качестве сигнала готовности. (См. locatorReady в коде ниже.) Я добавил слушателя к GetIt.ready, и это сигнализирует об объекте Completer.

FutureBuilder ожидает свойства Completer.future.

(Я новичок в Flutter, но мне кажется, что использование вещательного потока в GetIt не было хорошим выбором. ACompleter было бы лучше, я думаю.)

Это обновленный код:

import 'dart:async';

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

GetIt locator = GetIt.instance;
Completer<void> locatorReady = Completer<void>();

void setupLocator() async {
  final str = await Future<String>.delayed(Duration(seconds: 8), () => "hello");
  locator.registerSingleton(str, signalsReady: true);
  locator.ready.listen((_){locatorReady.complete();});
  locator.signalReady(str);
}

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<void>(
        future: locatorReady.future,
        builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
          Widget result;

          switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.active:
          case ConnectionState.waiting:
            result = Text('Awaiting result ...');
            break;
          case ConnectionState.done:
            if (snapshot.hasError)
              result = Text('Error: ${snapshot.error}');
            else
              result = Text('Result: ${locator.get<String>()}');
            break;
          }

          return Center(child: result,);
        }
      ),
    );
  }
}
...