Flutter get_it和FutureBuilder无法进行热重装

时间:2019-10-10 16:01:08

标签: flutter hot-reload flutter-provider flutter-get-it

下面的应用程序可以正常运行,但不幸的是,它无法与热重装一起使用。我想知道如何使用它进行热重装。

所有代码所做的只是简单地等待特定的Future<String>可用,然后在屏幕上显示该字符串。等待期间,将显示进度指示器。

正如我说的那样,它可以按预期工作,但是问题是,如果我进行了任何更改(我的意思是简单的化妆品),并且热加载开始,应用程序将永远坐在那里显示进度指示器,因为{{ 1}}处于connectionState模式。

这是代码更详细的作用:

我在ConnectionState.waiting中将String对象注册为单例。由于该字符串包装在GetIt中,因此代码必须等待其准备好后才能注册。

(在真实代码中,我正在从资产中加载JSON字符串。这就是为什么它位于Future中。)

Future通过等待FutureBuilder发信号通知名为GetIt的属性来等待字符串单例可用。

当字符串可用时,readyFuture将其显示在屏幕上。在此之前,将显示FutureBuilder

CircularProgressIndicator

我的pubspec.yaml中具有以下依赖项:

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;
        }
      ),
    );
  }
}

1 个答案:

答案 0 :(得分:0)

问题在于GetIt使用广播流GetIt.ready来表示已准备就绪。广播流的问题是信号在热重载时丢失。

另一个独立的问题是,如果GetIt.ready广播流没有侦听器,则就绪信号将丢失。因此,这里存在一个竞争条件:您必须确保FutureBuilder在发送就绪信号之前正在监听GetIt.ready,通常这是不可能的。

我想出的解决方法是使用Completer对象作为就绪信号。 (请参见下面的代码中的locatorReady。)我向GetIt.ready添加了一个侦听器,并向Completer对象发出信号。

FutureBuilder等待Completer.future属性。

(我是Flutter的新手,但在我看来,在GetIt中使用广播流不是一个好选择。我认为最好使用Completer。)

这是更新的代码:

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,);
        }
      ),
    );
  }
}