0

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

enter image description here

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()),
                                    ),
                                ),
                            );
                        }
                    }
                );
            }
        );
    }
}
Vladimir
  • 11
  • 2

1 Answers1

0

Check if your image's display size is matching your image's decode size. You can profile your app for "Oversized images".

I suggest getting the right size or closest size at least for idle performance. If you have no control over resolution of returned image you can resize it via flutter_image_compress. Few milliseconds in compression can be sacrificed if it improves the performance. Hoping you're using proper placeholder, UX should be fine.

Example:

Uint8List result = await FlutterImageCompress.compressWithList(
  yourImage,
  minHeight: yourDisplayHeight,
  minWidth: yourDisplayWidth,
  quality: 96, <--- adjust with trial and error
);

...
child: Image.memory(result),
...
Abhi Tripathi
  • 526
  • 2
  • 10
  • No, the image size is normal. I forgot to mention that more often the QR is updated normally - the flutter smoothly redraws it. But about every 4-5 times there is a failure. – Vladimir Apr 19 '23 at 21:11