The app below works as expected, but unfortunately it does not work with hot reload. I would like to know how to get hot reload working with it.
All the code does is simply to wait for a particular Future<String> to be available, and then to display that string on screen. While it's waiting, a progress indicator is displayed.
As I said, that works as expected, but the problem is that if I change anything (I mean just simple cosmetic stuff) and hot reload kicks in, the app will just sit there forever showing the progress indicator because the connectionState stays in ConnectionState.waiting mode.
This is what the code does in more detail:
I register a String object as a singleton in GetIt. Since that string is wrapped in a Future, the code has to wait for it to be ready before it can be registered.
(In the real code, I am loading a JSON string from an asset. That's why it's in a Future.)
A FutureBuilder waits for the string singleton to be available by waiting for GetIt to signal a property called readyFuture.
When the string becomes available, the FutureBuilder displays it on screen. Until then, a CircularProgressIndicator is displayed.
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;
}
),
);
}
}
I have these dependencies in my pubspec.yaml:
get_it: ^3.0.1
provider: ^3.1.0
The problem is that GetIt uses a broadcast stream, GetIt.ready, to signal that it is ready. The problem with a broadcast stream is that the signal is lost on hot reload.
Another, separate problem is that if there are no listeners to the GetIt.ready broadcast stream, then the ready signal will be lost. So there is a race condition there: you have to make sure that the FutureBuilder is listening to GetIt.ready before the ready signal is sent, and that may not be possible in general.
The work-around I came up with was to use a Completer object as the ready signal. (See locatorReady in the code below.) I added a listener to the GetIt.ready, and that signals the Completer object.
The FutureBuilder waits on the Completer.future property.
(I am a Flutter novice, but it seems to me that using a broadcast stream in GetIt was not a good choice. A Completer would be better I think.)
This is the updated code:
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,);
}
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With