I get a QR code for authorization via the REST API and display it on the screen via Image.memory(). After the QR is shown on the screen, I start sending authorization requests to the server.
But since the lifetime of the key k1 is limited, I have to make a request for a new QR with an interval of 10 minutes.
But when I update the screen with a new QR, I see some blinking - first the screen with an empty budget is drawn, then the QR is drawn. What can be done to make a new QR drawn immediately without intermediate rendering of an empty screen?
I tried to use Futurebuilders, StreamBiualder, but they don't give the desired result. futurebuilders does not work with TimeOut, and StreamBuilder gives the same double redrawing of the screen - first with an empty image, then with QR
Here's an accelerated video of what's going on
Model auth from AppManager
Future<ApiResponse<SigninModel>> getQrData() async {
signinData = await signinRepo.fetchSigninData();
appCache.setSigninQR(signinData.data!.qrcode);
appCache.setLnurl(signinData.data!.lnurl);
appCache.setSigninK1(signinData.data!.k1);
if (signinData.status == Status.COMPLETED)
checkAuthorization(signinData.data!.k1);
return signinData;
}
checkAuthorization(String k1) async {
AuthorizationResponse? userData;
if (userData == null) {
ApiResponse response = await signinRepo.checkAuthorization(k1);
switch (response.status) {
case Status.COMPLETED:
userData = response.data;
login(userData!.merchantId, userData.apiKey);
break;
case Status.ERROR:
break;
case Status.LOADING:
break;
}
}
}
StateFul from login page (Consumer without notifyListeners. Just use manager):
class QR extends StatefulWidget {
const QR({Key? key}) : super(key: key);
@override
State<QR> createState() => _QRState();
}
class _QRState extends State<QR> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<AppStateManager>(builder: (context, manager, child) {
return StreamBuilder(
stream: Stream.periodic(Duration(seconds: 10)).asyncMap((i) => manager.getQrData()),
builder: (context, AsyncSnapshot<ApiResponse> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
final Status responseStatus = snapshot.data!.status;
switch (responseStatus) {
case Status.LOADING:
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(150.0),
child: const Center(
child: SizedBox(
height: 50,
width: 50,
child: CircularProgressIndicator()),
),
),
);
case Status.COMPLETED:
final SigninModel signInData = snapshot.data!.data;
final Image imageQR = Image.memory(
Base64Decoder().convert(signInData.qrcode));
return InkWell(
onTap: () async {
launchInNonBrowser(
Uri.parse("lightning:${signInData.lnurl}"));
},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: imageQR,
),
),
);
case Status.ERROR:
final String message = snapshot.data?.message ?? '';
return Center(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
textAlign: TextAlign.center,
message,
style:
TextStyle(color: Colors.black, fontSize: 16)),
),
);
}
} else {
return SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Text(
textAlign: TextAlign.center,
snapshot.error.toString(),
style:
TextStyle(color: Colors.black, fontSize: 16)),
const SizedBox(
height: 20,
),
Text(
textAlign: TextAlign.start,
snapshot.stackTrace.toString(),
style:
TextStyle(color: Colors.blue, fontSize: 16)),
],
),
),
),
);
}
} else {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(120.0),
child: Center(
child: SizedBox(
height: 50,
width: 50,
child: CircularProgressIndicator()),
),
),
);
}
}
);
}
);
}
}