3

下面的应用程序按预期工作,但不幸的是它不适用于热重载。我想知道如何使用它进行热重载。

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

正如我所说,这按预期工作,但问题是如果我改变任何东西(我的意思只是简单的装饰性东西)并且热重新加载启动,应用程序将永远坐在那里显示进度指示器,因为connectionState保持ConnectionState.waiting模式。

这是代码更详细的功能:

我将一个String对象注册为GetIt. 由于该字符串包含在 a 中Future,因此代码必须等待它准备好才能注册。

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

AFutureBuilder通过等待GetIt一个名为 的属性来等待字符串单例可用readyFuture

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

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
4

1 回答 1

2

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

另一个单独的问题是,如果广播流没有侦听器GetIt.ready,那么就绪信号将丢失。所以那里有一个竞争条件:你必须确保 FutureBuilderGetIt.ready在发送就绪信号之前正在监听,这通常是不可能的。

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

FutureBuilder等待的财产Completer.future

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

这是更新的代码:

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,);
        }
      ),
    );
  }
}
于 2019-10-11T12:45:31.927 回答